使用 ViewModel 和 LiveData 递增变量的简单片段示例 - 变量始终为空

Posted

技术标签:

【中文标题】使用 ViewModel 和 LiveData 递增变量的简单片段示例 - 变量始终为空【英文标题】:Simple fragment example of incrementing a variable using ViewModel and LiveData - variable always null 【发布时间】:2019-02-19 19:36:42 【问题描述】:

注意:我是编码新手,我正在尝试按照标题的行来遵循一个简单的示例。

一切似乎都很顺利,除了在Tab1FragmentonClick() 方法中增加变量时遇到问题。 value 变量的值总是出现null

我认为,这与将 IntergerLiveData<Integer> 混合使用有关,但我不确定如何处理它。我已经尝试了各种方法来将一种转换为另一种并返回 get/store 结果。我关注的指南 (link) 使用 .setValue() 方法,但 android studio 不允许我使用该方法。

我很想为解决这个问题提供意见,甚至只是一个超级简单的选项卡片段教程的链接,我可以将其复制粘贴到项目中并检查这一切是如何工作的。我确信有一种更简单的方法可以完成这项任务,但我想了解如何在未来的项目中使用这个概念。

谢谢。

Tab1Fragment.java

package com.example.android.tabsharer;

import android.arch.lifecycle.LiveData;
import android.arch.lifecycle.Observer;
import android.arch.lifecycle.ViewModelProviders;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v4.app.Fragment;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.TextView;

public class Tab1Fragment extends Fragment 
    private static final String TAG = "Tab1Fragment";

    public SharedViewModel model;

    public LiveData<Integer> score;

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

//      Get the viewmodel
        model = ViewModelProviders.of(getActivity()).get(SharedViewModel.class);

//      Create observer to update the fragment
        TextView score1 = getActivity().findViewById(R.id.score1);

        final Observer<Integer> scoreObserver = new Observer<Integer>() 
            @Override
            public void onChanged(@Nullable Integer newScore) 
                //Update the textview that holds the score
                score1.setText(newScore);
            
        ;

//      Observe the live data
        model.getScore().observe(getActivity(), scoreObserver);

// Create the button in java, set listener on it, increment score, and store to viewmodel
        final Button button = view.findViewById(R.id.editScore1);

        button.setOnClickListener(new View.OnClickListener() 

            @Override
            public void onClick(View view) 
//              Basically every version of this comes up as null
                Integer value = model.getScore().getValue();
                if (value != null)
                    model.storeScore(++value);
                
                else 
                    model.storeScore(0);
                

            
        );

        return view;
    

SharedModelView.java

package com.example.android.tabsharer;

import android.app.Application;
import android.arch.lifecycle.AndroidViewModel;
import android.arch.lifecycle.LiveData;
import android.arch.lifecycle.MutableLiveData;
import android.support.annotation.NonNull;
import android.util.Log;

public class SharedViewModel extends AndroidViewModel 

    public static final String TAG = "SharedViewModel";

    private MutableLiveData<Integer> mScore = new MutableLiveData<>();

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

    public Integer storeScore(Integer score) 
        Log.d(TAG, "Score is being stored is " + mScore);
        mScore.setValue(score);
        return score;
    

    public LiveData<Integer> getScore() 
        Log.d(TAG, "Score is being retrieved");
        if (mScore == null)
            mScore = new MutableLiveData<>();
            mScore.setValue(0);
        
        return mScore;
    

MainActivity.java

package com.example.android.tabsharer;

import android.arch.lifecycle.ViewModel;
import android.arch.lifecycle.ViewModelProvider;
import android.arch.lifecycle.ViewModelProviders;
import android.support.design.widget.TabLayout;
import android.support.v4.app.FragmentActivity;
import android.support.v4.view.ViewPager;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.view.View;


public class MainActivity extends AppCompatActivity 

    private static final String TAG = "MainActivity";

    public SharedViewModel model;

    private SectionsPageAdapter mSectionsPageAdapter;

    private ViewPager mViewPager;

    @Override
    protected void onCreate(Bundle savedInstanceState) 
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Log.d(TAG, "onCreate: Starting.");

        model = ViewModelProviders.of(this).get(SharedViewModel.class);

        mSectionsPageAdapter = new SectionsPageAdapter(getSupportFragmentManager());

        mViewPager = (ViewPager) findViewById(R.id.container);
        setupViewPager(mViewPager);

        TabLayout tabLayout = (TabLayout) findViewById(R.id.tabs);
        tabLayout.setupWithViewPager(mViewPager);

    

    private void setupViewPager(ViewPager viewPager) 
        SectionsPageAdapter adapter = new SectionsPageAdapter(getSupportFragmentManager());
        adapter.addFragment(new Tab1Fragment(), "TAB1");
        adapter.addFragment(new Tab2Fragment(), "TAB2");
        viewPager.setAdapter(adapter);
    



【问题讨论】:

【参考方案1】:

你的问题在于两件事,


问题 1:(可选)

// Observe the live data
model.getScore().observe(getActivity(), scoreObserver);

您在 Fragment & 内部使用观察者,因此您应该使用它的 LifeCycle 进行这样的观察:

// Observe the live data
model.getScore().observe(this, scoreObserver);

问题 2: 是您使用 Integer 作为 LiveData 内部的包装器,在此方法期间未初始化:

public LiveData<Integer> getScore() 
    Log.d(TAG, "Score is being retrieved");
    if (mScore == null)//This code won't execute because you've initialized it globally.
        mScore = new MutableLiveData<>();
        mScore.setValue(0);
    
    return mScore;

所以,尝试将Interger 更改为int & 它将被初始化为默认值0

因为Integer 在此处将是null

// Basically every version of this comes up as null
Integer value = model.getScore().getValue();

解决方案:将 private MutableLiveData&lt;Integer&gt; mScore = new MutableLiveData&lt;&gt;(); 更改为 private MutableLiveData&lt;int&gt; mScore = new MutableLiveData&lt;&gt;(); (它会在您使用 LiveData 的任何地方显示错误,因此将其更改为 int)

【讨论】:

感谢您的帮助。我可以看到您对问题 1 的看法。但是,对于问题 2,我最初尝试使用 MutableLiveData 来声明 mScore,但它告诉我它不会接受原始 int 类型并提供将其转换为 Integer,并且这就是我在那里结束的方式。 我正在尝试不同的方法来潜在地将 Integer 输入片段中的 onClick 方法,将其解包为 int 并进行增量,然后将其重新包装为 Integer 以使 LIveData 快乐,但我很高兴足够新我不确定什么方法可以做到这一点。还有,对于modelview场景,是最好在model view中做increment函数,还是在fragment中做increment函数并将结果存到modelview中? 我真的想通了。问题 #1 与 getActivity 配合得很好。在我对文档的理解中,视图模型属于主要活动,您在声明模型时引用视图模型的所有者,否则它会随着 UI 更改的片段而被破坏。 这就是我的 onClick 最终的样子。我需要使用 model.mScore.etc 来让我的功能正常工作。我只使用了 model.etc,这就是为什么它之前从来没有给我使用 setValue 的选项。 ` @Override public void onClick(View view) Integer score = model.mScore.getValue(); if (score != null) model.mScore.setValue(score + 1); 其他 model.storeScore(1); ` 好吧,很明显你的逻辑有问题,而不是ViewModelLiveData

以上是关于使用 ViewModel 和 LiveData 递增变量的简单片段示例 - 变量始终为空的主要内容,如果未能解决你的问题,请参考以下文章

JetpackLiveData 架构组件 ( LiveData 简介 | LiveData 使用方法 | ViewModel + LiveData 示例 )

使用 ViewModel 和 LiveData 多次改造执行 API

MVVM 架构,ViewModel和LiveData

MVVM 架构,ViewModel和LiveData

Android Jetpack 学习之旅--> ViewModel & LiveData 的使用

MVVM 架构,ViewModel和LiveData