IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 移动开发 -> Jetpack学习之ViewModel -> 正文阅读

[移动开发]Jetpack学习之ViewModel

ViewModel

如果系统销毁或重新创建Activity或者fragment,则存储在其中的任何瞬态界面相关数据都会丢失。例如,应用的某个 Activity 中可能包含用户列表。因配置更改(如旋转屏幕,分辨率改变等)而重新创建 Activity 后,新 Activity 必须重新提取用户列表。对于简单的数据,activity 可以使用 onSaveInstanceState() 方法从 onCreate() 中的捆绑包恢复其数据,但此方法仅适合可以序列化再反序列化的少量数据,而不适合数量可能较大的数据,如用户列表或位图。诸如activityfragment之类的界面控制器主要用于显示界面数据、对用户操作做出响应或处理操作系统通信(如权限请求)。如果要求界面控制器也负责从数据库或网络加载数据,那么会使类越发膨胀。

使用ViewModel可以做到以生命周期形式管理界面数据。将视图和数据分离。架构组件为界面控制器提供了 ViewModel 辅助程序类,该类负责为界面准备数据。在配置更改期间会自动保留 ViewModel 对象,以便它们存储的数据立即可供下一个 activity 或 fragment 实例使用。实现数据共享。

不使用ViewModel

实现一个小功能,点击按钮,textview的数字加1

public class ViewModelStudy extends AppCompatActivity {
    private TextView tv;
    private int i = 0;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_view_model_study);
        tv = findViewById(R.id.tv_test);
        findViewById(R.id.btn).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                tv.setText(Integer.toString(i));
                i++;
            }
        });
    }
}

但是如果在这过程中屏幕发生旋转,那么显示的值会变为最开始的,而不是和旋转之前的一样。因为屏幕发生了旋转,所以activity重新经历了onCreat的生命周期。不使用ViewModel的话,界面数据不会保存。ViewModel的生命周期如下。可以看到ViewModel的生命周期比activity的时间更长。ViewModel 对象存在的时间范围是获取 ViewModel 时传递给 ViewModelProvider LifecycleViewModel 将一直留在内存中,直到限定其存在时间范围的 Lifecycle 永久消失:对于 Activity,是在 Activity 完成时;而对于 Fragment,是在 Fragment 分离时。取决于Lifecycle

使用ViewModel

首先继承ViewModel,数据成员是需要保存的界面数据。

// TestViewModel .java
public class TestViewModel extends ViewModel {
    public int getNum() {
        return num;
    }
    public void setNum(int num) {
        this.num = num;
    }
    private int num = 0;
}

//ViewModelStudy.java
public class ViewModelStudy extends AppCompatActivity {
    private TextView tv;
    private TestViewModel testViewModel;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_view_model_study);
        tv = findViewById(R.id.tv_test);
        testViewModel = new ViewModelProvider(this).get(TestViewModel.class);
        findViewById(R.id.btn).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                int num = testViewModel.getNum();
                tv.setText(Integer.toString(num));
                testViewModel.setNum(++num);
            }
        });
    }
}

上面这种方式可以实现界面数据的保存,但是旋转之后还是会变为最开始的样子。按下按钮之后数字是旋转前的数字加1,实现了数据的保存。为了严格的实现旋转后数字不变,那么需要使用Livedata,这也是ViewModel一般结合Livedata使用的原因。LiveData的优势,数据始终保持最新状态如果生命周期变为非活跃状态,它会在再次变为活跃状态时接收最新的数据。例如,曾经在后台的 Activity 会在返回前台后立即接收最新的数据。适当的配置更改,如果由于配置更改(如设备旋转)而重新创建了 activityfragment,它会立即接收最新的可用数据。

public class TestViewModel extends ViewModel {
    private MutableLiveData<Integer> num;

    public MutableLiveData<Integer> getNum() {
        if (num == null)
            num = new MutableLiveData<Integer>(0);
        return num;
    }
    public void setNum(Integer num) {
        this.num.setValue(num);
    }
}

public class ViewModelStudy extends AppCompatActivity {
    private TextView tv;
    private TestViewModel testViewModel;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_view_model_study);
        tv = findViewById(R.id.tv_test);
        testViewModel = new ViewModelProvider(this).get(TestViewModel.class);
        testViewModel.getNum().observe(this,
                new Observer<Integer>() {
                    @Override
                    public void onChanged(Integer integer) {
                        tv.setText(integer.toString());
                    }
                }
        );
        findViewById(R.id.btn).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                Integer num = testViewModel.getNum().getValue();
                testViewModel.setNum(++num);
            }
        });
    }
}

使用ViewModel进行Fragment之间的通信

Activity 中的两个或更多 Fragment 需要相互通信是一种很常见的现象。一般情况这两个 Fragment 都需要定义某种接口描述,并且所有者 Activity 必须将两者绑定在一起。此外,这两个 Fragment 都必须处理另一个 Fragment 尚未创建或不可见的情况。可以使用 ViewModel 对象解决这一常见的难点。这两个Fragment可以使用其 Activity 范围共享 ViewModel 来处理此类通信。

public class SharedViewModel extends ViewModel {
    private final MutableLiveData<Integer> selected = new MutableLiveData<Integer>();

    public void select(Integer item) {
        selected.setValue(item);
    }

    public LiveData<Integer> getSelected() {
        return selected;
    }
}

public class ListFragment extends Fragment {
    private SharedViewModel model;

    public void onViewCreated(@NonNull View view, Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);
        // 它们会收到相同的 SharedViewModel 实例(其范围限定为该 Activity)
        //因为这里指定为requireActivity()
        model = new ViewModelProvider(requireActivity()).get(SharedViewModel.class);
        itemSelector.setOnClickListener(item -> {
            model.select(item);
        });
    }
}

public class DetailFragment extends Fragment {

    public void onViewCreated(@NonNull View view, Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);
        //它们会收到相同的 SharedViewModel 实例(其范围限定为该 Activity),
        //因为这里指定为requireActivity()
        SharedViewModel model = new ViewModelProvider(requireActivity()).get(SharedViewModel.class);
        model.getSelected().observe(getViewLifecycleOwner(), item -> {
           // Update the UI.
        });
    }
}

Activity 不需要执行任何操作,也不需要对此通信有任何了解。除了 SharedViewModel 约定之外,Fragment 不需要相互了解。如果其中一个 Fragment 消失,另一个 Fragment 将继续照常工作。每个 fragment 都有自己的生命周期,而不受另一个 fragment 的生命周期的影响。如果一个fragment替换另一个 fragment,界面将继续工作而没有任何问题。

通信实例

// LeftFragment.java
public class LeftFragment extends Fragment {

    private TestViewModel model;
    private TextView tv;

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        // Inflate the layout for this fragment
        return inflater.inflate(R.layout.fragment_left, container, false);
    }

    @Override
    public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);
        model = new ViewModelProvider(requireActivity()).get(TestViewModel.class);
        tv = view.findViewById(R.id.tv_test);
        view.findViewById(R.id.addone).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                Integer num = model.getNum().getValue();
                model.setNum(++num);
            }
        });

        model.getNum().observe(getViewLifecycleOwner(), new Observer<Integer>() {
            @Override
            public void onChanged(Integer integer) {
                tv.setText(integer.toString());
            }
        });
    }
}
//RightFragment.java
public class RightFragment extends Fragment {
    private TextView tv;
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        // Inflate the layout for this fragment
        return inflater.inflate(R.layout.fragment_right, container, false);
    }

    public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);

        TestViewModel model = new ViewModelProvider(requireActivity()).get(TestViewModel.class);
        tv = view.findViewById(R.id.tv_test);
        view.findViewById(R.id.addone).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                Integer num = model.getNum().getValue();
                model.setNum(++num);
            }
        });
        model.getNum().observe(getViewLifecycleOwner(), new Observer<Integer>() {
            @Override
            public void onChanged(Integer integer) {
                tv.setText(integer.toString());
            }
        });
    }
}

  移动开发 最新文章
Vue3装载axios和element-ui
android adb cmd
【xcode】Xcode常用快捷键与技巧
Android开发中的线程池使用
Java 和 Android 的 Base64
Android 测试文字编码格式
微信小程序支付
安卓权限记录
知乎之自动养号
【Android Jetpack】DataStore
上一篇文章      下一篇文章      查看所有文章
加:2022-09-04 01:23:00  更:2022-09-04 01:26:55 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/25 4:40:46-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码