So basically what I want to achieve is to display the input of an EditText into a TextView of another Fragment. So a user answers a question and clicks on a button which gets him to the next fragment where in a TextView his answer should be displayed. Since I have 3 questions with the same method I am using ModelView to achieve this. Since I am relatively new to programming I don't know if this is the right way I implemented this. Your help is much appreciated!
Here is my ViewModel:
public class SharedViewModel extends ViewModel {
private HashMap<Integer, MutableLiveData<String>> answers = new HashMap<>();
public MutableLiveData<String> getAnswer(int questionId) {
return answers.get(questionId);
}
public void setAnswer(int questionId, String answer) {
if (answers.get(questionId) != null) {
answers.get(questionId).setValue(answer);
}
}
}
Here is my first Fragment with the first Question:
...
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_question_1, container, false);
btnNavFrag1 = view.findViewById(R.id.btn_question1);
mEditTextQuestion1 = view.findViewById(R.id.edit_text_question_1);
btnNavFrag1.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
viewModel.getAnswer(1);
((GameActivity)getActivity()).setViewPager(2);
}
});
return view;
}
@Override
public void onActivityCreated(@Nullable Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
viewModel = ViewModelProviders.of(getActivity()).get(SharedViewModel.class);
viewModel.getAnswer(1).observe(getViewLifecycleOwner(), new Observer<CharSequence>() {
@Override
public void onChanged(@Nullable CharSequence charSequence) {
mEditTextQuestion1.setText(charSequence);
}
});
}
And here is my Answer overview Fragment which is displayed after the first question Fragment and the typed in answer should replace the default value of a TextView:
...
public View onCreateView( LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_question_answer_1, container, false);
btnNavFragAns1 = view.findViewById(R.id.next_question_1);
tv_answer1 = view.findViewById(R.id.answer_player_1_text_view);
btnNavFragAns1.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
((GameActivity)getActivity()).setViewPager(3);
}
});
return view;
}
@Override
public void onActivityCreated(Bundle bundle) {
super.onActivityCreated(bundle);
viewModel = ViewModelProviders.of(getActivity()).get(SharedViewModel.class);
viewModel.getAnswer(1).observe(getViewLifecycleOwner(), new Observer<CharSequence>() {
@Override
public void onChanged(@Nullable CharSequence charSequence) {
tv_answer1.setText(charSequence);
}
});
}
}
Currently I get a NullPointerException at this line:
viewModel.getAnswer(1).observe(getViewLifecycleOwner(), new Observer<CharSequence>() {
After long trying I found the right solution to my problem:
I am using a ViewModel so the 6 different fragments can store and retrieve data in the functions of the ViewModel. So basically if you have a fragment where you have for example an EditText and want to display its input in another fragment, this is the way that worked for me:
public class SharedViewModel extends ViewModel {
private MutableLiveData<String> mAnswer1 = new MutableLiveData<>();
private MutableLiveData<String> mAnswer2 = new MutableLiveData<>();
private MutableLiveData<String> mAnswer3 = new MutableLiveData<>();
public void setAnswer1 (String answer1){
mAnswer1.setValue(answer1);
}
public void setAnswer2 (String answer2){
mAnswer2.setValue(answer2);
}
public void setAnswer3 (String answer3){
mAnswer3.setValue(answer3);
}
public LiveData<String> getAnswer1(){
return mAnswer1;
}
public LiveData<String> getAnswer2(){
return mAnswer2;
}
public LiveData<String> getAnswer3(){
return mAnswer3;
}
}
// This method gets the input of the EditText and "sends" it to the SharedViewModel where the next fragment can access it
@Override public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
EditText nameEditText = view.findViewById(R.id.edit_text_question_1);
// Add Text Watcher on name input text
nameEditText.addTextChangedListener(new TextWatcher() {
@Override public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) {
}
@Override public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) {
viewModel.setAnswer1(charSequence.toString());
}
@Override public void afterTextChanged(Editable editable) {
}
});
}
And before the onCreate method you have to call this:
public FragmentQuestion1() {
// Required empty public constructor
}
/**
* Create a new instance of this fragment
* @return A new instance of fragment FirstFragment.
*/
public static FragmentQuestion1 newInstance() {
return new FragmentQuestion1();
}
@Override public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// init ViewModel
viewModel = ViewModelProviders.of(requireActivity()).get(SharedViewModel.class);
}
// Gets the answer from player 1 from the previous fragment and set the TextView to the answer
@Override public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
tv_answer1 = view.findViewById(R.id.answer_player_1_text_view);
viewModel.getAnswer1().observe(requireActivity(), new Observer<String>() {
@Override
public void onChanged(@Nullable String s) {
tv_answer1.setText(s);
}
});
}
And like in the other fragment before you have to call the following before the onCreate method:
/**
* Use this factory method to create a new instance of this fragment.
*
* @return A new instance of fragment SecondFragment.
*/
public static FragmentAnswer1 newInstance() {
return new FragmentAnswer1();
}
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// initialise ViewModel here
viewModel = ViewModelProviders.of(requireActivity()).get(SharedViewModel.class);
}
I hope this will somehow help somebody because I struggled for a long time to find the right answer to my problem.