David
David

Reputation: 163

Android Studio - Update RecyclerView from Dialog Fragment in Menu Item

Hey guys I really need your help. I've spent like 5 days trying to get my recyclerview to update only when the user presses OK on a dialogbox, that appears from the menu actionbar. I've tried every possible method I could think of, every method I've seen on stackoverflow, YouTube, etc. and none of them worked for me. How do I get the recyclerview in a fragment to update after dialogbox closes? I know there are similar questions regarding updating the menu, and (recyclerviews with dialogfragments), but none of them have a combination.

Out of the countless attempts, the current code configuration posted below isn't causing any errors, however, the recyclerview remains blank. The closest attempt I had to finding a solution, was creating an adapter and setting up the recycler in onOptionsItemSelected. But obviously, it updates only when the user clicks the button, and the initial click would create a blank recyclerview.

Fragment:

(There's a lot of repeated commented code from different attempts)

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());
}
}

My Dialog Fragment:

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;
}
}

Upvotes: 1

Views: 1172

Answers (1)

B&#246; macht Blau
B&#246; macht Blau

Reputation: 13009

Your current approach seems to be to pass the RecyclerView.Adapter to the DialogFragment and try to insert the new data on-the-spot. I think this is a problematic setup. The dialog's purpose is to offer some means for the users to enter the required data, period. It should not be tasked with the job of managing the RecyclerView or its Adapter because that way your components will be very tightly coupled:

Imagine that first you use a ListView in your implementation, and suddenly someone decides to ban every ListView from your app (maybe for performance reasons) and has you exchange them all for RecyclerViews. Then your approach would force you to change the code for the DialogFragment (it would have to cater to a different type of Adapter). A more loosely coupled implementation would enable you to make changes to one component without having to rewrite too many others.

That's why I won't try to make your code work as-is, instead I'd like to show you another way:

Because the RecyclerView is part of the Fragment's UI, the Fragment is the place where code related to managing the RecyclerView belongs. It is basically possible to have the Adapter as an inner class of the Fragment but I prefer having it as a standalone class if the code gets a little longer, and also because it enforces "decoupling".

Interfaces play a very important part in good architecture, so the DialogFragment will still make use of an interface to send its data. But it's up to the class which actually implements the interface (here: the Fragment) to pass the data to any interested third parties, e.g. the RecyclerView.Adapter (The Adapter in turn could have its own interface to publish important events like clicks on list items).

Having said that, here are some code snippets:

The 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;
    }
}

The 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);
    }
}

Upvotes: 1

Related Questions