Android Studio - обновить RecyclerView из фрагмента диалогового окна в пункте меню

Привет, ребята, мне действительно нужна ваша помощь. Я потратил около 5 дней, пытаясь обновить мой recyclerview только тогда, когда пользователь нажимает OK в диалоговом окне, которое появляется из панели действий меню. Я пробовал все возможные методы, которые только мог придумать, все методы, которые я видел в stackoverflow, YouTube и т. д., И ни один из них у меня не работал. Как мне получить обновление recyclerview во фрагменте после закрытия диалогового окна? Я знаю, что есть похожие вопросы по обновлению меню и (повторное использование с диалоговыми фрагментами), но ни у одного из них нет комбинации.

Из бесчисленных попыток текущая конфигурация кода, представленная ниже, не вызывает никаких ошибок, однако recyclerview остается пустым. Ближайшая попытка найти решение заключалась в создании адаптера и настройке ресайклера в onOptionsItemSelected. Но, очевидно, он обновляется только тогда, когда пользователь нажимает кнопку, и при первом щелчке создается пустой recyclerview.

Фрагмент:

(Есть много повторяющегося кода с комментариями из разных попыток)

public class ExerciseRoutine extends Fragment implements ExerciseRoutine_Dialog.RoutineDialogListener{

private String Routine_name, Routine_split;
private ArrayList<ExerciseRoutine_Information> Routine_information = new ArrayList<>();

private RecyclerView recyclerView;
private RecyclerView.Adapter adapter;
private RecyclerView.LayoutManager layoutManager;

@Override
public void sendInput(String name, String split, RecyclerView.Adapter DialogAdapter) {
    Routine_name = name;
    Routine_split = split;
    adapter = DialogAdapter;
}

@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
    View view = inflater.inflate(R.layout.exercise_routine_fragment, container, false);

    //Report that this fragment would like to participate in populating the options menu by
    //receiving a call to onCreateOptionsMenu(Menu, MenuInflater) and related methods.
    //If true, the fragment has menu items to contribute.
    setHasOptionsMenu(true);

    recyclerView = view.findViewById(R.id.ExerciseRoutine_Recycler);
    //BuildRecyclerView();

    //recyclerView.setHasFixedSize(true); //If the Recyclerview is static

    /*Routine_information.add(new ExerciseRoutine_Information(Routine_name, Routine_split));

    recyclerView = view.findViewById(R.id.ExerciseRoutine_Recycler);
    //recyclerView.setHasFixedSize(true); //If the Recyclerview is static
    layoutManager = new LinearLayoutManager(getActivity());
    adapter = new ExerciseRoutineAdapter(Routine_information);

    recyclerView.setLayoutManager(layoutManager);
    recyclerView.setAdapter(adapter);*/

    return view;
}

@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
    super.onCreateOptionsMenu(menu, inflater);

    inflater.inflate(R.menu.exercise_routine_menu, menu);
}

@Override
public boolean onOptionsItemSelected(MenuItem item) {

    switch(item.getItemId()){

        case R.id.action_addRoutine:
            ExerciseRoutine_Dialog routineDialog = new ExerciseRoutine_Dialog();
            routineDialog.setTargetFragment(ExerciseRoutine.this, 1);
            routineDialog.show(getFragmentManager(), "Routine Dialog");

            //Routine_information.add(new ExerciseRoutine_Information(Routine_name, Routine_split));

            BuildRecyclerView();
            //adapter.notifyItemInserted(Routine_information.size());

            //if (!Routine_name.equals("") && !Routine_split.equals("")) {
            //}
    }

    return super.onOptionsItemSelected(item);
}

public void BuildRecyclerView(){

    layoutManager = new LinearLayoutManager(getActivity());

    recyclerView.setLayoutManager(layoutManager);
    recyclerView.setAdapter(adapter);
}

public void BuildAdapter(){
    //adapter = new ExerciseRoutineAdapter(getContext(),Routine_information);
    adapter.notifyItemInserted(Routine_information.size());
}
}

Мой фрагмент диалога:

public class ExerciseRoutine_Dialog extends DialogFragment{

private TextView ActionOK, ActionCANCEL;
private EditText Routine_name, Routine_split;

private RoutineDialogListener activityCommander;

private ArrayList<ExerciseRoutine_Information> Routine_information = new ArrayList<>();

private RecyclerView.Adapter adapter;

public interface RoutineDialogListener{
    void sendInput(String name, String split, RecyclerView.Adapter DialogAdapter);
}

@Override
public void onAttach(Context context) {
    super.onAttach(context);
    try{
        activityCommander = (RoutineDialogListener) getTargetFragment();
    }catch(ClassCastException e){
        throw new ClassCastException(context.toString() + "Must Implement RoutineDialogListener");
    }
}

@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
    View view = inflater.inflate(R.layout.exercise_routine_dialog, container, false);

    Routine_name = view.findViewById(R.id.ExerciseRoutine_DialogNameInput);
    Routine_split = view.findViewById(R.id.ExerciseRoutine_DialogSplitInput);

    ActionOK = view.findViewById(R.id.ExerciseRoutine_DialogAction_OK);
    ActionCANCEL = view.findViewById(R.id.ExerciseRoutine_DialogAction_CANCEL);

    //recyclerView = view.findViewById(R.id.ExerciseRoutine_Recycler);

    ActionCANCEL.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View view) {
            getDialog().dismiss();
        }
    });

    ActionOK.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View view) {

            String name = Routine_name.getText().toString();
            String split = Routine_split.getText().toString();

            if (!name.equals("") && !split.equals("")) {
                Routine_information.add(new ExerciseRoutine_Information(name, split));
                adapter = new ExerciseRoutineAdapter(getContext(), Routine_information);
                activityCommander.sendInput(name, split, adapter);
                adapter.notifyItemInserted(Routine_information.size());
            }

            getDialog().dismiss();
        }
    });


    return view;
}
}

Основная проблема в том, что «посылать» адаптер - не лучшая идея. Вместо этого вы должны отправлять данные из диалога в хост-фрагмент / действие. У вас может быть метод интерфейса, принимающий List <ExerciseRoutine_Information>, и как только вы получите данные, вы передадите их адаптеру (который всегда должен быть создан в том же фрагменте, где существует RecyclerView) и вызвать notifyDatasetChanged (). "передать их ..." <=> написать какой-нибудь метод для добавления новых данных в существующий список данных адаптера. Очистите список, прежде чем добавлять что-либо, если это необходимо.

Bö macht Blau 24.06.2018 22:15

Я пробовал это, но заметил, что моя основная проблема в том, что я не знаю, куда поместить код, обновляющий адаптер. Как я уже сказал выше, я пробовал как onCreate, так и onOptionsItemSelected, но оба не работали. Какие-либо предложения?

David 25.06.2018 00:00

Если вы посмотрите на закомментированный код, вы увидите много способов, которыми я пытался

David 25.06.2018 00:05

К сожалению, сейчас у меня очень мало времени, чтобы что-нибудь записать. Я постараюсь сделать это через несколько дней, если до тех пор никто не сможет / не поможет вам.

Bö macht Blau 25.06.2018 21:33

Хорошо, спасибо, но сахар

David 26.06.2018 01:50
1
5
788
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Ваш текущий подход, похоже, состоит в том, чтобы передать RecyclerView.Adapter в DialogFragment и попытаться вставить новые данные на месте. Я считаю, что это проблемная установка. Цель диалога - предложить пользователям некоторые средства для ввода требуемых данных, точки. Ему не следует поручить работу по управлению RecyclerView или его Adapter, потому что в этом случае ваши компоненты будут очень тесно связаны:

Представьте, что сначала вы используете ListView в своей реализации, и вдруг кто-то решает запретить каждый ListView в вашем приложении (возможно, из соображений производительности) и просит вас обменять их все на RecyclerView. Тогда ваш подход вынудит вас изменить код для DialogFragment (он должен обслуживать другой тип Adapter). Более слабосвязанная реализация позволит вам вносить изменения в один компонент без необходимости переписывать слишком много других.

Вот почему я не буду пытаться заставить ваш код работать как есть, вместо этого я хочу показать вам другой способ:

Поскольку RecyclerView является частью пользовательского интерфейса Fragment, Fragment - это то место, где находится код, связанный с управлением RecyclerView. В принципе возможно иметь Adapter как внутренний класс Fragment, но я предпочитаю иметь его как отдельный класс, если код становится немного длиннее, а также потому, что он обеспечивает «развязку».

Интерфейсы играют очень важную роль в хорошей архитектуре, поэтому DialogFragment по-прежнему будет использовать интерфейс для отправки своих данных. Но это зависит от класса, который фактически реализует интерфейс (здесь: Fragment), чтобы передавать данные любым заинтересованным третьим сторонам, например. RecyclerView.Adapter (Adapter, в свою очередь, может иметь собственный интерфейс для публикации важных событий, таких как щелчки по элементам списка).

Сказав это, вот несколько фрагментов кода:

DialogFragment

public class ExerciseRoutine_Dialog extends DialogFragment {

    private EditText Routine_name, Routine_split;

    public interface RoutineDialogListener{
        /**
         * There is some new ExerciseRoutine_Information
         */
        void sendInput(String name, String split);
    }

    @Nullable
    @Override
    public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        View view = inflater.inflate(R.layout.exercise_routine_dialog, container, false);

        Routine_name = view.findViewById(R.id.ExerciseRoutine_DialogNameInput);
        Routine_split = view.findViewById(R.id.ExerciseRoutine_DialogSplitInput);

        TextView actionOK = view.findViewById(R.id.ExerciseRoutine_DialogAction_OK);
        TextView actionCANCEL = view.findViewById(R.id.ExerciseRoutine_DialogAction_CANCEL);

        actionCANCEL.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                getDialog().dismiss();
            }
        });

        actionOK.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {

                String name = Routine_name.getText().toString();
                String split = Routine_split.getText().toString();

                if (!name.equals("") && !split.equals("")) {
                    // just send the input to the main Fragment
                    RoutineDialogListener listener = getListener();
                    if (listener != null) {
                       listener.sendInput(name, split);
                    }
                }
                getDialog().dismiss();
            }
        });


        return view;
    }

    /**
     * Tries to find a suitable listener, examining first the hosting Fragment (if any) and then the Activity.
     * Will return null if this fails
     * @return x
     */
    private RoutineDialogListener getListener(){
        RoutineDialogListener listener;
        try{
            Fragment onInputSelected_Fragment = getTargetFragment();
            if (onInputSelected_Fragment != null){
                listener = (RoutineDialogListener) onInputSelected_Fragment;
            }
            else {
                Activity onInputSelected_Activity = getActivity();
                listener = (RoutineDialogListener) onInputSelected_Activity;
            }
            return listener;
        }catch(ClassCastException e){
            Log.e("Custom Dialog", "onAttach: ClassCastException: " + e.getMessage());
        }
        return null;
    }
}

Fragment:

public class ExerciseRoutine extends Fragment implements ExerciseRoutine_Dialog.RoutineDialogListener{

    public static final String ROUTINE_DIALOG = "Routine Dialog";
    private ArrayList<ExerciseRoutine_Information> routineInformations = new ArrayList<>();

    private RecyclerView.Adapter adapter;

    public static ExerciseRoutine instance(){
        return new ExerciseRoutine();
    }

    @Override
    public void sendInput(String name, String split) {
        routineInformations.add(new ExerciseRoutine_Information(name, split));
        adapter.notifyItemInserted(routineInformations.size());
    }

    @Nullable
    @Override
    public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        View view = inflater.inflate(R.layout.exercise_routine_fragment, container, false);

        setHasOptionsMenu(true);

        RecyclerView recyclerView = view.findViewById(R.id.ExerciseRoutine_Recycler);
        RecyclerView.LayoutManager layoutManager = new LinearLayoutManager(getActivity());
        recyclerView.setLayoutManager(layoutManager);
        adapter = new ExerciseRoutineAdapter(getContext(), routineInformations);
        // So far you have a RecyclerView with an empty List.
        recyclerView.setAdapter(adapter);

        return view;
    }

    @Override
    public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
        super.onCreateOptionsMenu(menu, inflater);

        inflater.inflate(R.menu.exercise_routine_menu, menu);
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {

        switch(item.getItemId()){

            case R.id.action_addRoutine:
                showDialog();
                return true;
        }

        return super.onOptionsItemSelected(item);
    }

    private void showDialog(){
        ExerciseRoutine_Dialog routineDialog = new ExerciseRoutine_Dialog();
        routineDialog.setTargetFragment(ExerciseRoutine.this, 1);
        routineDialog.show(getFragmentManager(), ROUTINE_DIALOG);
    }
}

Эй, нет, ваш код имеет больше смысла, чем то, что я делал, но мне было интересно, почему вы сделали функцию getListener вместо использования onAttach? Также listener.sendInput (name, split) никогда не вызывается, так как split имеет значение null, есть идеи, почему?

David 28.06.2018 20:54

@David - я использую getListener () вместо установки слушателя в onAttach (), потому что, например, в вашем случае, если пользователи нажимают «Отмена», определение слушателя не требуется. Это своего рода ленивая инициализация. Строка: расколоть не должна быть нулевой, потому что ей присвоены некоторые EditText.getText (). ToString (), и они могут быть пустыми, но не могут быть нулевыми. (Я запустил код, который опубликовал в образце приложения, и он отлично сработал, но, конечно, мне пришлось делать свои собственные макеты, поэтому я не могу быть уверен, где есть какие-то различия)

Bö macht Blau 28.06.2018 21:20

Другие вопросы по теме