ViewModel 核心作用:管理 Activity 中的数据
使用 ViewModel 管理 Navigation 导航当中的数据,可以实现切换页面数据不丢失,架构更加清晰
关于安卓搭建界面:是用图形化方式还是用代码:ConstraintLayout 布局的出现,使得图形化方式搭建界面变得十分便利。而在以前,ConstraintLayout出现之前,往往使用的是 RelativeLayout,使用图形化界面不方便,所以更倾向于用代码写界面。现在,利用图形化方式搭建界面是十分直观与方便的,建议使用图形化方式,没有必要再用代码写界面了。
首先创建两个 Fragment ,我们将实现 Home 切换到 Detail
搭建 Home 页面:
在 activity_main.xml 中,放入 NavHostFragment
界面搭建完成,接下来是完善代码部分。
在 ViewModel 中管理一个变量 num:
public class MyViewModel extends ViewModel {
private MutableLiveData<Integer> num;
public MutableLiveData<Integer> getNum(){
if(num == null){ // 不存则创建一个,初值为 0
num = new MutableLiveData<>();
num.setValue(0);
}
return num;
}
public void add(int x){
num.setValue(num.getValue() + x);
if(num.getValue() < 0){ // 数字不能 < 0
num.setValue(0);
}
}
}
要使用 DataBinding ,需要在 build.gradle(Module:app) 下,android 的 defaultConfig 下添加一句话:
dataBinding.enabled true
然后去 home 界面的 xml 中,将布局转化成 data binding 布局
然后在 xml 中设置变量(variable) data,获取 MyViewModel 中的变量
<data>
<variable
name="data"
type="com.example.navviewmodel.MyViewModel" />
data>
将 MyViewModel 中的变量绑定到 Home 界面的 TextView上:
android:text="@{String.valueOf(data.getNum())}"
关于按键的动作绑定(例如Button的OnClick()):一般与数据相关则通过DataBinding 绑定到界面上,与数据无关而仅与逻辑相关(例如界面跳转)一般在 Activity 或 Fragment 中写代码。
来到 HomeFragment.java :
编写 Button 动作,在导航控制器跳转页面时,无需像之前一样利用 bundle 存储数据,由于利用了 ViewModel 管理变量,只需跳转页面即可。
public View onCreateView(LayoutInflater inflater, final ViewGroup container,
Bundle savedInstanceState) {
final MyViewModel myViewModel;
myViewModel = ViewModelProviders.of(getActivity()).get(MyViewModel.class);
final FragmentHomeBinding binding;
binding = DataBindingUtil.inflate(inflater, R.layout.fragment_home,container, false);
binding.setData(myViewModel);
binding.setLifecycleOwner(getActivity());
binding.button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
NavController controller = Navigation.findNavController(view); // 获取导航控制器
controller.navigate(R.id.action_homeFragment_to_detailFragment); // Home -> Detail
}
});
binding.seekBar.setProgress(myViewModel.getNum().getValue()); // 初始化进度条,使得数据不丢失
binding.seekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
@Override
public void onProgressChanged(SeekBar seekBar, int i, boolean b) { // 滑动进度条时
myViewModel.getNum().setValue(i); // 将进度条的数字赋给 num
}
@Override
public void onStartTrackingTouch(SeekBar seekBar) {
}
@Override
public void onStopTrackingTouch(SeekBar seekBar) {
}
});
return binding.getRoot();
}
同理,来到 DetailFragment.java:
设置变量(variable) data,获取 MyViewModel 中的变量的
<data>
<variable
name="data"
type="com.example.navviewmodel.MyViewModel" />
data>
将 MyViewModel 中的变量绑定到 Detail 界面的 TextView上:
由于 Detail 界面的 “+” 与 “-” 按键动作与数据相关,因此通过DataBinding 绑定到界面上:
Button " - " 实现了数据 -1,添加如下代码:
android:onClick="@{()->data.add(-1)}"
Button " + " 实现了数据 +1,添加如下代码:
android:onClick="@{()->data.add(+1)}"
而“返回按钮”与数据无关,因此在 DetailFragment.java 中写代码:
public View onCreateView(LayoutInflater inflater, final ViewGroup container,
Bundle savedInstanceState) {
MyViewModel myViewModel;
myViewModel = ViewModelProviders.of(getActivity()).get(MyViewModel.class);
FragmentDetailBinding binding;
binding = DataBindingUtil.inflate(inflater, R.layout.fragment_detail, container, false);
binding.setData(myViewModel);
binding.setLifecycleOwner(getActivity());
binding.button4.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
NavController controller = Navigation.findNavController(view);
controller.navigate(R.id.action_detailFragment_to_homeFragment2);
}
});
return binding.getRoot();
}
至此,功能全部写完,
运行效果如下:
滑动进度条,显示数字;
点击进入,切换页面;
点击+,数字变大;
点击返回,切换回初始界面。