如何在不同的 Fragment 上使用 ModelView 来获取 EditText 的输入并将 TextView 设置为其值?

Posted

技术标签:

【中文标题】如何在不同的 Fragment 上使用 ModelView 来获取 EditText 的输入并将 TextView 设置为其值?【英文标题】:How to use ModelView on different Fragments to get the input of an EditText and set a TextView to its value? 【发布时间】:2020-01-22 16:52:57 【问题描述】:

所以基本上我想要实现的是将 EditText 的输入显示到另一个 Fragment 的 TextView 中。因此,用户回答一个问题并单击一个按钮,该按钮将他带到下一个片段,在 TextView 中应该显示他的答案。由于我使用相同的方法有 3 个问题,因此我使用 ModelView 来实现这一点。由于我对编程比较陌生,我不知道这是否是我实现它的正确方法。非常感谢您的帮助!

这是我的 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);
    


这是我第一个问题的第一个片段:

   ...

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

这是我的答案概述片段,它显示在第一个问题片段之后,输入的答案应该替换 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>() 

【问题讨论】:

请提供LogCat 尝试将onActivityCreated的代码移到fragment的onCreate中 将代码移动到onCreate后仍然显示相同的错误 我需要在 ViewModel 中初始化地图但我不知道该怎么做 【参考方案1】:

在代码的这个特定部分:您没有为 get answer on key 1 设置任何值,您应该首先将 MutableLiveData 设置为 key " 1"

viewModel.getAnswer(1)

首先,您必须在private HashMap&lt;Integer, MutableLiveData&lt;String&gt;&gt; answers = new HashMap&lt;&gt;(); 中输入一些内容作为key 1:

answers.put(1, new MutableLiveData<String>());

之后您可以通过代码访问可变的实时数据:viewModel.getAnswer(1)

【讨论】:

我添加了类似这样的代码 private HashMap> answers = new HashMap>() answers.put(1, new MutableLiveData ()); ;但是answers.put中有错误 @Kaiser 您应该将此代码添加到构造函数中,或者其他地方,不要作为变量的主体。请查看有关使用 HashMap 的详细信息,例如,您可以在此处查看如何使用它,这只是众多链接之一。w3schools.com/java/java_hashmap.asp【参考方案2】:
public void setAnswer(int questionId, String answer) 
    if (answers.get(questionId) != null) 
        answers.get(questionId).setValue(answer);
    

LiveData 初始化错误。 您无需在 OnActivityCreated() 内部进行初始化即可访问它。 首先初始化你的 mutablelivedata。

【讨论】:

【参考方案3】:

我将举一个例子,说明如何解决 ViewModel 的问题。

视图模型

public class ViewModel_RoutineStartConnection extends androidViewModel

public ViewModel_RoutineStartConnection(
      @NonNull Application application) 
    super(application);
  

private MutableLiveData<String> sAnswer = new MutableLiveData<>();

public void setAnswer(String sAnswer)
 this.sAnswer.post(sAnswer);


public LiveData<String> getsAnswer()
return this.sAnswer;



使用 EditText 设置值的片段

public class Fragment_getAnswer extends Fragment 

private ViewModel_RoutineStartConnection myViewModle;
private EditText edittext;

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

edittext = root.findViewById(R.id.YOURID);
edittext.setOnEditorActionListener(listener);

return root;


@Override
  public void onActivityCreated(@Nullable Bundle savedInstanceState) 
    super.onActivityCreated(savedInstanceState);
    myViewModle = ViewModelProviders.of(getActivity()).get(ViewModel_RoutineStartConnection.class);

  

public TextView.OnEditorActionListener listener = new OnEditorActionListener() 
    @Override
    public boolean onEditorAction(TextView textView, int i, KeyEvent keyEvent) 
      if (i == EditorInfo.IME_ACTION_DONE) 
         myViewModle.setAnswer(edittext.getText.toString);
      
      return false;
    
  ;

读取值的片段

public class Fragment_setAnswer extends Fragment 

private ViewModel_RoutineStartConnection myViewModle;
private TextView textview;

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

textview= root.findViewById(R.id.YOURID);

return root;


@Override
  public void onActivityCreated(@Nullable Bundle savedInstanceState) 
    super.onActivityCreated(savedInstanceState);
    myViewModle = ViewModelProviders.of(getActivity()).get(ViewModel_RoutineStartConnection.class);
 myViewModle.getsAnswer.observe(getViewLifecycleOwner(), obs_getsAnswer):
  

private Observer<String> obs_getsAnswer= new Observer<String>() 
    @Override
    public void onChanged(@Nullable String sAnswer) 
      textview.setText(sAnswer);
    
  ;


要对所有答案使用此方法,我建议为每个答案添加一个“id”。 所以当你设置setAnswer("01" + answerText)

当你观察你可以使用的数据时:

switch(sAnswer.subString(0,3))
case "01":
//set answer text for answer 1
break;

【讨论】:

【参考方案4】:

经过长时间的尝试,我找到了解决问题的正确方法:

我正在使用 ViewModel,因此 6 个不同的片段可以在 ViewModel 的函数中存储和检索数据。所以基本上,如果你有一个片段,你有一个 EditText 并且想在另一个片段中显示它的输入,这对我来说是有效的方式:

    使用 set 和 get 方法创建 ViewModel:
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;
    


    在您有一个 EditText 并希望使用其输入的片段中,您必须调用 ViewModel 的 set 方法。:
// 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) 

            
        );
    

在 onCreate 方法之前你必须调用这个:

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);
    
    在要使用输入的片段中,只需调用 ViewModel 的 get 方法即可:
// 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);
            
        );
    

和其他片段一样,你必须在 onCreate 方法之前调用以下代码:

/**
     * 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);
    

我希望这能以某种方式帮助某人,因为我为找到问题的正确答案而苦苦挣扎了很长时间。

【讨论】:

以上是关于如何在不同的 Fragment 上使用 ModelView 来获取 EditText 的输入并将 TextView 设置为其值?的主要内容,如果未能解决你的问题,请参考以下文章

如何将 List<Model> 从 Activity 传递到 Fragment

如何在同一页面上的 Fragment 内创建具有 3 个 Fragment 的 ViewPager?

如何在 Fragment 实现中显示不同的活动?

如何从使用 Fragment 的 ViewPager2 获得用户点击?

如何使用导航组件切换到不同后堆栈中的其他片段?

Android 将 ArrayList<Model> 从 Activity 传递给 Fragment