Search code examples
javaandroidandroid-fragmentsandroid-alertdialog

Change a textview in a fragment based on the DialogAlert positive/negative buttons in Android Java


I am building a quiz app that has two types of questionnaires. I have a DialogAlert that allows the user to choose which questionnaire (two options: either "history" or "chemistry") they want to complete and have the results display in a fragment.

My goal is to be able to display the textview ("history" or "chemistry") that the questionnaire they selected in the result fragment.

I have a Dialog class:

public class DialogAlert extends DialogFragment {
private Context context;
    String selection;
   
    @Override
    public Dialog onCreateDialog(Bundle savedInstanceState) {
        final String[] items = {"History", "Chemistry"};
        AlertDialog.Builder builder = new AlertDialog.Builder(this.getActivity());
        builder.setTitle("Select a topic to complete")
                .setSingleChoiceItems(items, -1, new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialog, int which) {
                        selection = items[which];
                    }
                })

                // Set the action buttons
                .setPositiveButton("Ok", new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialog, int id) {
                        switch (selection)
                        {
                            case("History"):
                                Intent intent_hist = new Intent(getActivity(), historyquestions.class);
                                startActivity(intent_hist);
                                break;
                            case("Chemistry"):
                                Intent intent_chem = new Intent(getActivity(), chemistryquestions.class);
                                startActivity(intent_chem);
                                break;
                        }
                    }
                })
                .setNegativeButton("Cancel", new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialog, int id) {
                        Toast.makeText(getActivity(), "No topic was selected", Toast.LENGTH_SHORT)
                                .show();
                        dialog.cancel();
                    }
                });
        return builder.create();
    }

My Result fragment in which I have a placeholder textview that I want to display what the user had selected (either history or chemistry) based on the positive/ negative button above:

public class homeFragment extends Fragment{
TextView topic  
public View onCreateView(@NonNull LayoutInflater inflater,
                                 ViewGroup container, Bundle savedInstanceState) {
    
View layoutView = inflater.inflate(R.layout.fragment_home, container, false);       
topic = (TextView) layoutView.findViewById(R.id.topic);
return layoutView;
}
}

How should I approach this? The challenge is that the result is shown in one of the fragments and I am unsure how would I "pass" what the user had selected in the Dialog to the Result fragment. Can someone please help?

Thank you in advance!

*Edit with the home fragment Id

<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".ui.home.homeFragment">

    <FrameLayout
        android:id="@+id/home_layout"
        android:layout_width="match_parent"
        android:layout_height="match_parent">
    </FrameLayout>

 <LinearLayout
            android:id="@+id/layout1"
            android:layout_width="match_parent"
            android:layout_height="80dp"
            android:layout_marginStart="10dp"
            android:layout_marginTop="270dp"
            android:layout_marginEnd="10dp"
            android:layout_marginBottom="10dp"
            android:background="@drawable/layout_bg"
            android:orientation="vertical"
            android:weightSum="300">

            <TextView
                android:id="@+id/text1"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:layout_gravity="left"
                android:layout_weight="10"
                android:fontFamily="@font/inter_regular"
                android:text="Selection of topic"
                android:textColor="#000000"
                android:textSize="22sp" />

            <TableRow
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:orientation="vertical">


                <TextView
                    android:id="@+id/topic"
                    android:layout_width="340dp"
                    android:layout_height="wrap_content"
                    android:layout_gravity="left"
                    android:fontFamily="@font/inter_regular"
                    android:text="Topic"
                    android:textColor="#000000"
                    android:textSize="20sp" />

            </TableRow>

        </LinearLayout>
</FrameLayout> 

Edit 2023-02-13

HomeViewModel class:

import androidx.lifecycle.LiveData;
import androidx.lifecycle.MutableLiveData;
import androidx.lifecycle.ViewModel;

public class HomeViewModel extends ViewModel {
    private final MutableLiveData<String> mText;

    public HomeViewModel() {
        mText = new MutableLiveData<>();
        mText.setValue("This is a home fragment");
    }

    // getText method
    public LiveData<String> getText() {
        return mText;
    }

    // function to update the mText value
    public void setText(String updateText) {
        mText.setValue(updateText);
    }
}

homeFragment class:

       import androidx.lifecycle.Observer;
       import androidx.lifecycle.ViewModelProviders;
       import androidx.annotation.Nullable;
         
       public class homeFragment extends Fragment{
                        
            private HomeViewModel homeViewModel;
            public View onCreateView(@NonNull LayoutInflater inflater,ViewGroup container, Bundle savedInstanceState) {
                    
            View layoutView = inflater.inflate(R.layout.fragment_home, container, false);
        
    homeViewModel =                    
  ViewModelProviders.of(requireActivity()).get(HomeViewModel.class);                        
    
    final TextView topic = (TextView)layoutView.findViewById(R.id.topic);
                          homeViewModel.getText().observe(getViewLifecycleOwner(), new Observer<String>() {
                       @Override
                       public void onChanged(@Nullable String s) {
                            topic.setText(s);
                       }
                        });
                return layoutView;
               }

Dialog class:

import androidx.annotation.Nullable;
import androidx.fragment.app.DialogFragment;
import androidx.lifecycle.ViewModelProviders;
import com.example.quizzApp.MainActivity;
import com.example.quizzApp.ui.home.HomeViewModel;

public class DialogAlert extends DialogFragment {
    private Context context;
    String selection;
    private HomeViewModel model;

    @Override
    public void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        model = ViewModelProviders.of(requireActivity()).get(HomeViewModel.class);
    }

    @Override
    public Dialog onCreateDialog(Bundle savedInstanceState) {
        final String[] items = {"History", "Chemistry"};
        AlertDialog.Builder builder = new AlertDialog.Builder(this.getActivity());
        // Set the dialog title
        builder.setTitle("Select a topic to complete")
                // Specify the list array, the items to be selected by default (null for none),
                // and the listener through which to receive callbacks when items are selected
                .setSingleChoiceItems(items, -1, new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialog, int which) {
                        selection = items[which];
                    }
                })

                // Set the action buttons
                // User clicked OK, so save the selectedItems results somewhere
                .setPositiveButton("Ok", new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialog, int id) {
                        switch (selection)
                        {
                            case("History"):
                                model.setText("History");
                                Intent intent_hist = new Intent(getActivity(), MainActivity.class);
                                startActivity(intent_hist);
                                break;
                            case("Chemistry"):
                                model.setText("Chemistry");
                                Intent intent_chem = new Intent(getActivity(), MainActivity.class);
                                startActivity(intent_chem);
                                break;
                        }
                    }
                })
                // or return them to the component that opened the dialog
                .setNegativeButton("Cancel", new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialog, int id) {
                        Toast.makeText(getActivity(), "No topic was selected", Toast.LENGTH_SHORT)
                                .show();
                        dialog.cancel();
                    }
                });
        return builder.create();
    }

Solution

  • Scenario

    You have created an App with Bottom Navigation Activity template. There should be 3 tabs Home, Dashboard, Notifications. And somehow you have opened a DialogFragment and that allows you to choose between History or Chemistry as the topic. And upon selecting either one and press OK, your Home fragment should display the topic selected.


    Possible solution

    If you are using Bottom Navigation Activity template, there should be a HomeViewModel class. You can make use of this HomeViewModel class, share it among different Fragments, and update the view accordingly.

    HomeFragment class:

    public class HomeFragment extends Fragment {
    
        private HomeViewModel homeViewModel;
        private Button btnDialog;
    
        public View onCreateView(@NonNull LayoutInflater inflater,
                                 ViewGroup container, Bundle savedInstanceState) {
            // Please notice the below line, getting ViewModel using the below method can ensure
            //  the model can be shared across different Fragments
            homeViewModel =
                    ViewModelProviders.of(requireActivity()).get(HomeViewModel.class);
                    // new ViewModelProvider(this).get(HomeViewModel.class);
            View root = inflater.inflate(R.layout.fragment_home, container, false);
            final TextView textView = root.findViewById(R.id.text_home);
            // You have registered the ViewModel to change your HomeFragment TextView. So if the value
            //  of mText has been updated, the TextView in HomeFragment will also be updated 
            //  accordingly.
            homeViewModel.getText().observe(getViewLifecycleOwner(), new Observer<String>() {
                @Override
                public void onChanged(@Nullable String s) {
                    textView.setText(s);
                }
            });
            // This btnDialog is just my testing button, it is a button to open your mentioned
            //  DialogFragment. So you can ignore this button and the openDialog(View) function.
            btnDialog = root.findViewById(R.id.btnDialog);
            btnDialog.setOnClickListener(this::openDialog);
            return root;
        }
    
        // As mentioned above, this function can be ignored
        public void openDialog(View v) {
            DialogAlert frag = new DialogAlert();
            frag.show(getChildFragmentManager(), "DialogAlert");
        }
    }
    

    HomeViewModel class:

    public class HomeViewModel extends ViewModel {
    
        private MutableLiveData<String> mText;
    
        public HomeViewModel() {
            mText = new MutableLiveData<>();
            mText.setValue("This is home fragment");
        }
    
        public LiveData<String> getText() {
            return mText;
        }
    
        // You need to add a function so that you can update mText value
        public void setText(String updateText) {
            mText.setValue(updateText);
        }
    }
    

    DialogAlert class:

    public class DialogAlert extends DialogFragment {
        private Context context;
        String selection;
        // Define a variable to hold the ViewModel class
        private HomeViewModel model;
    
        @Override
        public void onCreate(@Nullable Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            // Obtain the HomeViewModel so that we can update the value of mText and sync to view in
            //  HomeFragment
            model = ViewModelProviders.of(requireActivity()).get(HomeViewModel.class);
        }
    
        @Override
        public Dialog onCreateDialog(Bundle savedInstanceState) {
            final String[] items = {"History", "Chemistry"};
            AlertDialog.Builder builder = new AlertDialog.Builder(this.getActivity());
            builder.setTitle("Select a topic to complete")
                    .setSingleChoiceItems(items, -1, new DialogInterface.OnClickListener() {
                        @Override
                        public void onClick(DialogInterface dialog, int which) {
                            selection = items[which];
                        }
                    })
    
                    // Set the action buttons
                    .setPositiveButton("Ok", new DialogInterface.OnClickListener() {
                        @Override
                        public void onClick(DialogInterface dialog, int id) {
                            switch (selection) {
                                case ("History"):
                                    // Magic line, it will trigger the onChanged() in HomeFragment
                                    model.setText("History");
                                    dialog.dismiss();
                                    break;
                                case ("Chemistry"):
                                    // Magic line, it will trigger the onChanged() in HomeFragment
                                    model.setText("Chemistry");
                                    dialog.dismiss();
                                    break;
                            }
                        }
                    })
                    .setNegativeButton("Cancel", new DialogInterface.OnClickListener() {
                        @Override
                        public void onClick(DialogInterface dialog, int id) {
                            Toast.makeText(getActivity(), "No topic was selected", Toast.LENGTH_SHORT)
                                    .show();
                            dialog.cancel();
                        }
                    });
            return builder.create();
        }
    }
    

    With the above code, after you have selected the option in DialogFragment, your HomeFragment TextView should be updated at once.


    Misconception

    Regarding on the following code:

    switch (selection)
    {
        case("History"):
            Intent intent_hist = new Intent(getActivity(), historyquestions.class);
            startActivity(intent_hist);
            break;
        case("Chemistry"):
            Intent intent_chem = new Intent(getActivity(), chemistryquestions.class);
            startActivity(intent_chem);
            break;
    }
    

    The above code will start an Activity to either historyquestions Activity or chemistryquestions Activity (if you have properly created these Activity), instead of proceeding to your result homeFragment Fragment.


    Additional Note

    It is a good practice to follow Java naming conventions when you name classes. For example:

    • homeFragment should be named as HomeFragment
    • historyquestions should be named as HistoryQuestions