当前位置:   article > 正文

Fragment与ViewModel(MVVM架构)_homeviewmodel.gettext().observe(getviewlifecycleow

homeviewmodel.gettext().observe(getviewlifecycleowner(), recyclerview::sette

简介

        在Android应用开发中,FragmentViewModel是两个非常重要的概念,它们分别属于架构组件库的一部分,旨在帮助开发者构建更加模块化、健壮且易维护的应用。

Fragment

  Fragment是Android系统提供的一种可重用的UI组件,它能够作为活动(Activity)的一部分,具有自己的生命周期,并且可以在多个Activity中使用。Fragment的设计初衷是为了支持更灵活的屏幕布局,特别是在需要适配不同屏幕尺寸和方向时。通过组合多个Fragment,开发者可以创建丰富的用户界面,并且每个Fragment都可以独立地处理用户输入、保存状态等,从而提高代码的复用性和模块化。

ViewModel

  ViewModel是Android架构组件库中的一个核心类,用于存储和管理UI相关的数据。它的主要目的分离视图(View)和数据,使得数据能够在配置变更(如屏幕旋转)时保持避免了因Activity或Fragment重建导致的数据丢失问题。ViewModel的生命周期独立于UI控制器(Activity或Fragment),确保了数据的持久性。此外,ViewModel还可以与LiveData等组件结合使用,实现数据变化的自动通知,简化了UI更新的逻辑

Fragment与ViewModel的协同工作

在实际开发中,为了实现Fragment的数据持久化和解耦,通常会为Fragment关联一个ViewModel。这样做有以下几个好处:

  1. 数据共享:如果多个Fragment需要共享数据,可以将这些数据放在一个共享的ViewModel中。这样,即使Fragment被重建,数据仍然保持不变,而且Fragment之间可以直接访问这些共享数据,无需通过Activity传递。

  2. 生命周期解耦:ViewModel不依赖于UI组件的生命周期,因此即使Fragment销毁并重新创建(比如由于配置变更),ViewModel仍然存在,保证了数据的连续性。

  3. 简化数据管理:ViewModel负责数据的获取、存储和处理,而Fragment专注于展示数据和处理用户交互,这使得代码结构更加清晰,易于维护。

一、开启绑定Binding

Step 1: 打开build.gradle(Module级别)文件。

Step 2:android闭包内,确保buildFeatures块存在,然后添加viewBinding属性并设为true

buildFeatures:

  1. android {
  2. ...
  3. buildFeatures {
  4. viewBinding = true // 注意,新版一定要有=
  5. }
  6. }
  • 这是启用ViewBinding的推荐方式,特别是在较新的Android Gradle插件版本中。buildFeatures是一个集合了各种构建特性的开关,通过在这里设置viewBindingtrue,你告诉Gradle在构建时生成ViewBinding类。这些类让你能够以类型安全的方式访问XML布局中的视图,无需手动调用findViewById

dataBinding:

  1. android {
  2. ...
  3. dataBinding {
  4. enabled = true // 注意,新版一定要有=
  5. }
  6. }
  • 类似地,这是启用DataBinding的方式。通过在dataBinding块内设置enabledtrue,你激活了DataBinding特性。DataBinding比ViewBinding更进一步,提供了数据和视图之间的双向绑定能力,允许在布局文件中直接使用数据对象,并支持表达式来处理数据变化,实现更复杂的UI逻辑。

viewBinding:

  1. android {
  2. ...
  3. viewBinding {
  4. enabled = true // 注意,新版一定要有=
  5. }
  6. }

正确的配置应该遵循上述第一条提到的buildFeatures { viewBinding = true }。实际上推荐使用buildFeatures块来配置ViewBinding。选择哪种绑定技术取决于你的项目需求:简单视图绑定用ViewBinding,需要更复杂数据逻辑处理则使用DataBinding。

二、加载布局

ActivityMain:

        

  1. // 定义MainActivity类,继承自AppCompatActivity,这是Android提供的一个Activity基类,用于兼容旧版设备
  2. public class MainActivity extends AppCompatActivity {
  3. // 声明一个私有成员变量binding,类型为ActivityMainBinding,用于存储由Data Binding生成的绑定对象
  4. private ActivityMainBinding binding;
  5. // 重写onCreate()方法,这是Activity生命周期的第一个回调方法,用于初始化Activity
  6. @Override
  7. protected void onCreate(@Nullable Bundle savedInstanceState) {
  8. super.onCreate(savedInstanceState); // 调用父类的onCreate()方法,执行基本的初始化工作
  9. // 使用Data Binding的inflate方法从XML布局文件创建一个绑定对象
  10. // getLayoutInflater()返回LayoutInflater实例,用于将XML布局转换为View对象
  11. // ActivityMainBinding.inflate()方法将布局文件转换为ActivityMainBinding对象
  12. binding = ActivityMainBinding.inflate(getLayoutInflater());
  13. // 设置Activity的内容视图,即将绑定对象的根视图设置为Activity的布局
  14. // binding.getRoot()返回inflate生成的View对象,即整个布局的根View
  15. setContentView(binding.getRoot());
  16. }
  17. }

在这个过程中,以下步骤发生:

  1. 声明Binding对象:在MainActivity类中声明了一个ActivityMainBinding类型的私有变量bindingActivityMainBinding是Data Binding自动生成的类,用于封装和管理XML布局文件中的所有视图。

  2. 加载布局:在onCreate()方法中,首先调用super.onCreate()来执行父类的初始化过程。然后使用ActivityMainBinding.inflate()方法加载布局文件,getLayoutInflater()提供了创建布局的能力。

  3. 设置内容视图:调用setContentView()方法,并传入binding.getRoot()返回的根视图,将该视图设置为MainActivity的布局视图。这意味着MainActivity的界面将按照ActivityMainBinding所绑定的XML布局文件来渲染。

通过使用Data Binding,开发者可以直接通过binding对象访问布局文件中的所有视图,而无需调用findViewById()方法,这使得代码更加简洁、可读性更强,同时也避免了一些常见的错误,如空指针异常

ViewModel:

  1. // 定义一个继承自ViewModel的类,用于存储界面相关的数据,保证数据在配置变化时不会丢失
  2. public class SyFragmentViewModel extends ViewModel {
  3. // 声明一个私有成员变量mText,类型为MediatorLiveData<String>,用于存储和分发字符串数据
  4. private MediatorLiveData<String> mText;
  5. // 构造函数,初始化mText并设置其初始值
  6. public SyFragmentViewModel() {
  7. // 创建并初始化MediatorLiveData实例
  8. mText = new MediatorLiveData<>();
  9. // 设置mText的初始值
  10. mText.setValue("第一个页面");
  11. }
  12. // 公共方法,返回mText,允许外部组件观察mText的数据变化
  13. public LiveData<String> getText() {
  14. return mText;
  15. }
  16. }

通过上述代码,SyFragmentViewModel可以被FragmentActivity使用,以观察和响应数据变化,从而实现实时更新UI的效果。

Fragment:

  1. // 定义SyFragment类,继承自Fragment,这是Android中用于构建可重用UI块的类。
  2. public class SyFragment extends Fragment {
  3. // 声明一个私有成员变量binding,类型为SyActivityBinding。这是Data Binding自动生成的类,
  4. // 它包含了对SyFragment所使用的XML布局文件中所有View的引用。
  5. private SyActivityBinding binding;
  6. // 重写onCreateView()方法,这是Fragment生命周期的一部分,用于创建并返回Fragment的用户界面视图。
  7. @Nullable
  8. @Override
  9. public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
  10. // 使用Data Binding的inflate方法从XML布局文件创建一个绑定对象。
  11. // 第一个参数是LayoutInflater,用于将XML布局转换为View对象;
  12. // 第二个参数是ViewGroup,表示inflate出的View是否应立即附加到该ViewGroup;
  13. // 第三个参数是一个布尔值,如果为true,inflate出的View将附加到container,否则不会。
  14. // 这里inflate方法会根据XML布局文件生成相应的View对象,并将这些View对象封装进binding对象中。
  15. binding = SyActivityBinding.inflate(inflater, container, false);
  16. // 获取inflate生成的View对象,即整个布局的根View,以便返回给onCreateView()方法。
  17. View root = binding.getRoot();
  18. // 从binding中获取TextView的引用,这一步利用了Data Binding的便利性,可以直接通过属性名访问View。
  19. final TextView textView = binding.textView;
  20. // 创建并获取SyFragmentViewModel的实例。ViewModelProvider是一个工具类,用于创建和管理ViewModel实例。
  21. // 这里的this参数告诉ViewModelProvider当前Fragment需要哪个ViewModel。
  22. SyFragmentViewModel syFragmentViewModel = new ViewModelProvider(this).get(SyFragmentViewModel.class);
  23. // 观察SyFragmentViewModel中getText()返回的LiveData对象。
  24. // observe()方法用于注册观察者,getViewLifecycleOwner()确保观察者只在Fragment可见时生效。
  25. // textView::setText是一种方法引用,表示当LiveData数据改变时,自动调用TextView的setText()方法更新UI。
  26. syFragmentViewModel.getText().observe(getViewLifecycleOwner(), textView::setText);
  27. // 返回inflate生成的根View,这将是Fragment的用户界面。
  28. return root;
  29. }
  30. // 重写onDestroy()方法,这是Fragment生命周期的一部分,当Fragment不可见时调用。
  31. // 这里设置binding为null,有助于回收资源,防止内存泄漏。
  32. @Override
  33. public void onDestroy() {
  34. super.onDestroy();
  35. binding = null;
  36. }
  37. }

        这段代码展示了如何在一个Fragment中使用Data Binding和ViewModel来构建UI,并响应数据变化。通过使用Data Binding,我们可以更简洁地访问布局中的View;通过ViewModel和LiveData,我们可以在数据变化时自动更新UI,同时保证数据在配置变更时的持久性。

ViewGroup:

        ViewGroup是一个非常重要的概念,它是View体系结构中的基础组件之一,负责组织和管理子View(包括其他ViewGroup)。简单来说,ViewGroup就是一种特殊的View,它不仅自己可以显示内容,还可以包含多个子View,并且能够控制这些子View的布局方式。

SyFragmentonCreateView()方法中,ViewGroup主要体现在inflater.inflate()方法的第二个参数——container。这里的container实际上就是一个ViewGroup,它是指定用于容纳由LayoutInflater从XML布局文件中解析出来的View组件的父容器。

当你调用SyActivityBinding.inflate(inflater, container, false)时:

  • inflaterLayoutInflater的实例,它负责读取XML布局文件,并将其转换为实际的View对象。
  • containerViewGroup的实例,代表了onCreateView()方法中返回的View将要被添加到的父容器。通常情况下,containerFragment将要附加到的Activity的主布局。
  • false作为第三个参数,意味着从XML布局文件中inflate出来的View不会立即被添加到container中。这是因为FragmentView应该由FragmentManager来管理,而不是直接由ViewGroup来管理。FragmentManager会在适当的时机将View添加到container中。

所以,在这个特定的上下文中,ViewGroup的作用主要是作为Fragment视图层次结构的一部分,为Fragment的布局提供一个容器。当Fragment变得可见时,其视图将被FragmentManager添加到指定的ViewGroup(即container)中。

这里,SyActivityBinding.inflate()方法通过LayoutInflaterViewGroupcontainer),实现了从XML布局文件到View对象的转换,并通过Data Binding的方式将这些View对象封装进SyActivityBinding对象中,便于后续的代码访问和操作。

三、布局加载比较

        binding = ActivityMainBinding.inflate(getLayoutInflater());binding = YourActivityBinding.inflate(inflater, container, false); 都使用了 Android 的 Data Binding 库来从 XML 布局文件生成对应的 Java 对象,但它们之间存在一些关键区别,主要在于 inflate 方法的调用方式和参数上。

第一种情况:

binding = ActivityMainBinding.inflate(getLayoutInflater());

这里使用的是 ActivityMainBinding 类的 inflate 方法,这个方法不需要额外的容器参数。它直接使用 getLayoutInflater() 来获取 LayoutInflater 实例,然后调用 inflate 方法生成布局。这种情况下,inflate 方法会自动找到一个合适的根视图,并且返回一个 ActivityMainBinding 类型的对象,该对象包含了布局中的所有 View。

第二种情况:

binding = YourActivityBinding.inflate(inflater, container, false);

这个版本的 inflate 方法接收三个参数:

  • inflater: 这是一个 LayoutInflater 实例,通常从父视图或者 Activity 中获取。
  • container: 这是一个可选的父视图容器,如果你想要将这个布局添加到某个已存在的 View 组中时,你需要提供这个容器。如果布局是要作为独立的视图,则可以忽略此参数。
  • attachToRoot: 这个布尔值参数决定了是否将生成的布局视图自动添加到 container 参数指定的容器中。如果设置为 false,则不会自动添加;如果设置为 true,则会自动添加到 container 中。

总结:

  • 如果你的布局是要直接设置为 Activity 的根布局,通常使用第一种方法,因为不需要考虑容器问题。
  • 如果你的布局是要作为子布局添加到某个容器中(比如在 Fragment 或者自定义 View 中),那么你应该使用第二种方法,并且要确保 attachToRoot 参数设置正确,以便于控制布局是否应该被自动添加到容器中。

在大多数情况下,Activity 的布局会直接设置为 Activity 的根视图,因此第一种情况更为常见。然而,在更复杂的场景下,例如在 Fragment 中使用 Data Binding,第二种情况则更为适用。

四、navigation

navigation创建

布局加载

代码完善:

五、menu菜单

menu创建:

Vector图标:

        new创建:

        颜色、名称等设定:

        

        finsh完成:

添加item项:

        代码完善:

切记一定要与navigation的xml代码中fragment的id一致

六、导航实现

BottomNavigationView:

fragment:

        一定要将NavHostFragment改fragment

activity中代码实现:

  1. public class MainActivity extends AppCompatActivity {
  2. // 定义一个 ActivityMainBinding 类型的成员变量 binding,
  3. // ActivityMainBinding 是由 Data Binding 自动生成的类,用于绑定 XML 布局文件中的元素到 Java 代码。
  4. private ActivityMainBinding binding;
  5. @Override
  6. protected void onCreate(@Nullable Bundle savedInstanceState) {
  7. // 调用父类的 onCreate 方法,这是每个 Activity 的 onCreate 方法都应该做的。
  8. super.onCreate(savedInstanceState);
  9. // 使用 Data Binding 的 inflate 方法从 XML 文件加载布局。
  10. // getLayoutInflater() 返回 LayoutInflater 实例,用于加载布局。
  11. // ActivityMainBinding.inflate 方法会解析 res/layout/activity_main.xml 文件,
  12. // 并返回一个 ActivityMainBinding 实例,其中包含布局文件中的所有视图组件。
  13. binding = ActivityMainBinding.inflate(getLayoutInflater());
  14. // 设置 Activity 的内容视图。binding.getRoot() 方法返回布局文件中的根视图。
  15. setContentView(binding.getRoot());
  16. // 通过 Data Binding 访问 BottomNavigationView 视图,其 ID 在 activity_main.xml 文件中定义。
  17. BottomNavigationView bottomNavigationView = binding.BottomNavi;
  18. // 创建 NavController 实例,用于管理应用中的导航。
  19. // Navigation.findNavController 方法需要传入一个 Context 和一个 View 的 ID,
  20. // 这里使用 R.id.fragmentContainerView 表示要查找的 NavController 管理的 Fragment 容器。
  21. NavController navController = Navigation.findNavController(this, R.id.fragmentContainerView);
  22. // 创建 AppBarConfiguration 实例,用于配置 App Bar 的行为,这里使用默认配置。
  23. // AppBarConfiguration 的构造函数可以接收多个参数来配置不同的行为,
  24. // 但在这个例子中,使用了默认的构造函数,没有进行任何配置。
  25. AppBarConfiguration appBarConfiguration = new AppBarConfiguration.Builder().build();
  26. // 使用 NavigationUI.setupActionBarWithNavController 方法设置 ActionBar 与 NavController 的关联,
  27. // 这样 ActionBar 就可以根据 NavController 的状态显示相应的标题和导航项。
  28. // 第一个参数是当前 Activity,第二个参数是 NavController,第三个参数是 AppBarConfiguration。
  29. NavigationUI.setupActionBarWithNavController(this, navController, appBarConfiguration);
  30. // 使用 NavigationUI.setupWithNavController 方法设置 BottomNavigationView 与 NavController 的关联,
  31. // 这样 BottomNavigationView 就可以响应 NavController 的变化,显示正确的菜单项。
  32. // 第一个参数是 BottomNavigationView,第二个参数是 NavController。
  33. NavigationUI.setupWithNavController(bottomNavigationView, navController);
  34. }
  35. }

最终效果:

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/小舞很执着/article/detail/772464
推荐阅读
相关标签
  

闽ICP备14008679号