文章目录
一、简述
- 描述:当Activity / Fragment数据处理量大时,一个XXActivity.java / XXFragment.java里就会有庞大的数据量。而这两者的主要作用应该是起到连接UI控件、渲染数据的作用,所以这就违背“单一负责”原则,那么数据就应该单独进行存放以及读取。为此Android官方就为开发者提供了ViewModel类。
- 难度:初级
- 知识点:
- 1、了解Java中数据存储地址的概念(逻辑 and 物理)
- 2、了解软件体系结构里的观察者模式
- 3、适配器(Adapter)的使用
- 4、认识ViewModel
二、创建ViewModel
为了使文章简洁,这里就直接使用Android studio配置好了的Bottom Navigation Activity
创建好工程后,就可以看到已经配置好了的ViewModel
三、了解LiveData
LiveData对于初学者而言,还是比较难以理解,这里会用就好了。
主要作用:通知Observe类将最新数据传递给渲染方法(和Vue里的双向绑定v-model有异曲同工之妙),实体类里通过postValue(this)监听数据变化,这在软件体系结构里被称为观察者模式。
ViewModel观察者模式
四、使用ViewModel
1、单体数据
如,创建初始的样子
这个很简单,直接跳过,平常就可以按照这个模板来。
2、实体类数据
很显然,任何在应用商店的Android app,不可能只是对单一数据进行渲染。必然牵扯到实体类数据(服务器发送json -> Android接收json -> json转实体)
(1)创建LiveData实体
这里以商品实体类为例(这是后面将写的博客购物车功能模块的实体类)
import androidx.lifecycle.LiveData;
public class Shop extends LiveData<Shop> {
int id;
String image;
String name;
int price;
String bus;
float dis;
String time;
String des;
public int getId() {
return id;
}
public String getImage() {
return image;
}
public String getName() {
return name;
}
public int getPrice() {
return price;
}
public String getBus() {
return bus;
}
public float getDis() {
return dis;
}
public String getTime() {
return time;
}
public String getDes() {
return des;
}
public void setId(int id) {
this.id = id;
postValue(this);
}
public void setImage(String image) {
this.image = image;
postValue(this);
}
public void setName(String name) {
this.name = name;
postValue(this);
}
public void setPrice(int price) {
this.price = price;
postValue(this);
}
public void setBus(String bus) {
this.bus = bus;
postValue(this);
}
public void setDis(float dis) {
this.dis = dis;
postValue(this);
}
public void setTime(String time) {
this.time = time;
postValue(this);
}
public void setDes(String des) {
this.des = des;
postValue(this);
}
}
(2)编写ViewModel
这里以模板里的HomeFragment和HomeViewModel为例
- HomeViewModel.java
import androidx.lifecycle.ViewModel;
import com.hngy.xpq.shoppingcartmodule.bean.Shop;
import java.util.ArrayList;
import java.util.List;
public class HomeViewModel extends ViewModel {
private Shop shop = new Shop();
private List<Shop> ls;
public List<Shop> getListShop() {
ls = new ArrayList<>();
// 这里是获取数据,一般有三种方式
// 1、手动填充数据(测试)
// 2、HTTP获取服务器数据
// 3、本地数据库存储的数据
for (int i = 0; i < 5;i++) {
Shop s = new Shop();
s.setName("name" + i);
s.setPrice(10 + i);
s.setBus("商家" + i);
s.setDis(Float.valueOf("2.55") + i);
s.setTime("周" + i);
s.setDes("无");
ls.add(s);
}
// ------------end---------- //
return ls;
}
// 测试数据的改变,会不会影响视图显示的数据
public void xg() {
// 修改数据(不通过setText方法)
// 需要明白 Java数据地址原理
ls.get(1).setName("ViewModel绑定数据修改!");
}
}
- HomeFragment.java
学习此节前,需要先学习栏目【Android开发基础】三种常见的适配器(Adapter)中List适配器。也可以复制附件中关于适配器的代码
private RecyclerView listShop;
private List<Shop> shopList;
private void init(View root) {
shopList = homeViewModel.getListShop();
listShop = root.findViewById(R.id.listShop);
getListAdapter(); // 数据渲染
homeViewModel.xg(); // 修改数据
}
// List适配器
private void getListAdapter() {
StaggeredGridLayoutManager layoutManager = new StaggeredGridLayoutManager(1,StaggeredGridLayoutManager.VERTICAL);
listShop.setLayoutManager(layoutManager);
ListAdapter adapter = new ListAdapter(shopList);
listShop.setAdapter(adapter);
}
- 效果
五、附件
1、适配器UI界面设计代码
item_list.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="15dp"
android:orientation="horizontal">
<ImageView
android:id="@+id/homeImage"
android:layout_width="120dp"
android:layout_height="120dp"
android:background="@drawable/ic_launcher_background"/>
<TextView
android:id="@+id/homeName"
android:layout_width="match_parent"
android:layout_height="25dp"
android:layout_marginLeft="18dp"
android:layout_toEndOf="@+id/homeImage"
android:textSize="50px"/>
<TextView
android:id="@+id/homeTime"
android:layout_width="150dp"
android:layout_height="25dp"
android:layout_below="@+id/homeName"
android:layout_marginStart="16dp"
android:layout_marginLeft="18dp"
android:layout_marginTop="5dp"
android:layout_toEndOf="@+id/homeImage"
android:textSize="50px" />
<TextView
android:id="@+id/homeDes"
android:layout_width="150dp"
android:layout_height="25dp"
android:layout_below="@+id/homeTime"
android:layout_marginStart="16dp"
android:layout_marginLeft="18dp"
android:layout_marginTop="5dp"
android:layout_toEndOf="@+id/homeImage"
android:textSize="50px" />
<TextView
android:id="@+id/homePrice"
android:layout_width="150dp"
android:layout_height="25dp"
android:layout_below="@+id/homeDes"
android:layout_marginStart="16dp"
android:layout_marginLeft="18dp"
android:layout_marginTop="5dp"
android:layout_toEndOf="@+id/homeImage"
android:textSize="50px" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="120dp"
android:gravity="bottom|right"
android:paddingBottom="20dp">
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="加入"/>
</LinearLayout>
</RelativeLayout>
2、适配器数据绑定代码
ListAdapter.java
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.ListView;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
import com.hngy.xpq.shoppingcartmodule.R;
import com.hngy.xpq.shoppingcartmodule.bean.Shop;
import java.util.List;
public class ListAdapter extends RecyclerView.Adapter<ListAdapter.ViewHolder> {
private List<Shop> listData;
static class ViewHolder extends RecyclerView.ViewHolder {
View fileView;
ImageView image;
TextView name, price, dis, time, des;
public ViewHolder(View itemView) {
super(itemView);
fileView = itemView;
name = itemView.findViewById(R.id.homeName);
price = itemView.findViewById(R.id.homePrice);
time = itemView.findViewById(R.id.homeTime);
des = itemView.findViewById(R.id.homeDes);
}
}
// 获取到数据
public ListAdapter(List<Shop> list) {
this.listData = list;
}
@NonNull
@Override
public ViewHolder onCreateViewHolder(@NonNull final ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_list,parent,false);
final ViewHolder holder = new ViewHolder(view);
return holder;
}
@Override
public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
// 数据绑定
final Shop s = listData.get(position);
holder.name.setText(s.getName());
holder.price.setText("¥" + s.getPrice());
holder.time.setText(s.getTime());
holder.des.setText(s.getDes());
}
@Override
public int getItemCount() {
return listData.size();
}
}