如何通过ViewModel类在Android中在Activity和Fragment之间共享数据?

16
我想知道是否可以将在Activity类中声明的String数据传递给ViewModel类,然后将数据传递给Fragment类。
ViewModel类
class TimeTableViewModel extends ViewModel {

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

void send_StartTime(String start_Time){
    start_time_str.setValue(start_Time);
}

LiveData<String> get_StartTime(){
    return start_time_str;
}}

在ViewModel类中,我有一个MutableLiveData<String> start_time_str,它已经初始化为new MutableLiveData<>();

我想在Activity类中使用void send_StartTime(String start_Time)函数来设置参数String start_Time的值,并在Fragment类中调用start_time_str

Activity类

@Override
public boolean onOptionsItemSelected(MenuItem item){
    switch (item.getItemId()){
        case android.R.id.home:
            finish();
            break;
        case R.id.add_schedule_save:
            String start_time_str = startTime.getText().toString();
            Intent intent_restart0 = new Intent(TimeTable_Add_New_Schedule.this, MainActivity.class);
            startActivity(intent_restart0);
            TimeTableViewModel timeTableViewModel = new TimeTableViewModel();
            timeTableViewModel.send_StartTime(start_time_str);
            Toast.makeText(this,""+start_time_str,Toast.LENGTH_LONG).show();
            break;
    }
    return super.onOptionsItemSelected(item);
}

片段类

@Override
public void onActivityCreated(@Nullable Bundle savedInstanceState) {
    super.onActivityCreated(savedInstanceState);
    TimeTableViewModel timeTableViewModel = new TimeTableViewModel();
    timeTableViewModel.get_StartTime().observe(getViewLifecycleOwner(), new Observer<String>() {
        @Override
        public void onChanged(String s) {
            mon_textView_11.setText(s);
        }
    });
}

在 Fragment 类中,我调用 get_StartTime() 函数来获取 start_time_str 并将 String 值设置到我的 TextView 中。我认为由 Activity 类中的 timeTableViewModel.send_StartTime(start_time_str); 函数成功设置了 start_time_str,因为 Toast.maketext 能够正常工作。然而,TextView 没有显示任何内容。我已经测试过 Text Color 不是白色,所以如果正确调用了字符串值,它应该出现在屏幕上。如果您有任何建议,我很乐意听取您的建议。非常感谢。

enter image description here

4个回答

35

这真的取决于您如何创建 ViewModel 实例。现在,您正在通过其构造函数创建 ViewModel,但这不是一种适当的方式。您应该使用由Google团队创建的 ViewModelProvider 或扩展方法。

如果您选择使用 ViewModelProvider,则应按照以下方式执行:

TimeTableViewModel viewModel = new ViewModelProvider(this).get(TimeTableViewModel.class);

在调用ViewModelProvider构造函数时传递正确的上下文很重要。如果您在Fragment中仅使用getContext()而不是getActivity(),则您将无法获得与在Activity中创建的相同实例。您将创建一个新的ViewModel实例,其作用域仅限于Fragment生命周期内。因此,在两个部分中使用Activity上下文以获取相同的实例非常重要。

Activity部分:

TimeTableViewModel viewModel = new ViewModelProvider(this).get(TimeTableViewModel.class);

片段部分:

TimeTableViewModel viewModel = new ViewModelProvider(getActivity()).get(TimeTableViewModel.class);

重要的是,你的片段(fragment)必须位于使用此ViewModel的同一活动(activity)中。

但是Google的开发人员为我们提供了一些扩展方法,使得这变得更加容易。但据我所知,它们仅适用于Kotlin类。因此,如果你有Kotlin代码,可以像这样简单地声明你的ViewModel

private val quizViewModel: TimeTableViewModel by activityViewModels()

对于片段范围内的ViewModel,您需要编写类似以下内容的代码:

private val quizViewModel: TimeTableViewModel by viewModels()

但是你需要在你的项目build.gradle文件中添加Kotlin ktx依赖项。 例如像这样:

但是您必须将Kotlin ktx依赖项添加到项目的build.gradle文件中。例如:

implementation 'androidx.fragment:fragment-ktx:1.1.0'

非常感谢您的回答。由于片段不在我的活动类中,我无法使用ViewModel类共享数据,因此我创建了一个新的片段,然后将其替换为活动类。它完美地运作了。非常感谢。 - user13024999
3
如果您正在使用导航组件,您也可以通过以下方式将ViewModel绑定到导航图中:val myViewModel: TimeTableViewModel by navGraphViewModels(R.id.navigation_graph)。很高兴我能帮上忙。 :) - BVantur
这样做的好处是什么? - nyx69
你指的是哪个部分?导航范围内的ViewModels吗? - BVantur
1
根据Android团队的最新指导,我们通常使用单个Activity与导航组件结合来构建我们的应用程序。如果是这种情况,那么如果我们使用Activity ViewModel,则实际上正在创建一个在整个应用程序中存在的单例。为了避免这种情况,我们可以将应用程序分解为不同的导航,并通过导航用例适当地限定我们的逻辑组件。例如,在已登录用户的主屏幕中,我们不需要OnboardingManager。 - BVantur
我已经知道了。我的问题是这两者之间是否有任何区别:val myViewModel: TimeTableViewModel by navGraphViewModels(R.id.navigation_graph)和myViewModel = ViewModelProvider.AndroidViewModelFactory(Application()).create(MyViewModel::class.java) - nyx69

8
如果你正在使用 Android Architecture,并想在片段中分享 activityViewModel。获取片段中的 viewModels,请使用以下代码:
private val fragmentViewModel: Fragment1ViewModel by viewModels()
private val activityViewModel: MainActivityViewModel by activityViewModels()

MainActivity 中使用以下代码:

private val activityViewModel: MainActivityViewModel by viewModels()

2
第一行代码是否具有误导性?您将无法使用“private val fragmentViewModel: Fragment1ViewModel by viewModels()”访问在Activity中设置的ViewModel。 - Chucky
这真的很有帮助... 我以为viewModels()默认是活动范围的!我在这上面浪费了一些时间。 - undefined

3

我正在使用 Kotlin 语言。

我按照教程的步骤操作,但是不起作用。

以下是如何声明我的 ViewModel 的示例代码:

在 build.gradle 中添加依赖项:

implementation 'androidx.fragment:fragment-ktx:1.4.1'

Activity 类示例:

class HomepageActivity : AppCompatActivity() {
    private lateinit var sharedViewModel: SharedViewModel

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding = ActivityHomepageBinding.inflate(layoutInflater)
        setContentView(binding.root)

        sharedViewModel = ViewModelProvider(this).get(SharedViewModel::class.java)
        sharedViewModel.isMenuOpen.observe(this, {
            onMenuOpen(it)
        })
    }

Fragment类中的示例

class HomeFragment : Fragment() { private lateinit var sharedViewModel: SharedViewModel

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
    super.onViewCreated(view, savedInstanceState)

    sharedViewModel = activity!!.run{
        ViewModelProvider(this).get(SharedViewModel::class.java)
    }
}

0
我找到了解决这个问题的最简单方法,不要将所有者设置为"this",而是改为getActivity()。

    @Override
        public void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            if (mvm == null) {
                mvm = new ViewModelProvider(getActivity()).get(CounterFgViewModel.class);
            }
        }


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接