项目地址
本项目实现了对Lifecycle,LiveData,ViewModel,Room,Paging,Navigation这六个官方构架组件的全面使用,组件的单独使用或者合作使用都有(PS:WorkManager bug 太多,不建议使用,我这里也不会提到他,因为我反正是有坑,迈不过去)
贴一张项目主界面图,大家可以先下载项目自己运行一下
Lifecycle主要作用是方便监听activity和Fragment生命周期
在监听activity和Fragment时,在代码里没有什么区别,和下面一样
getLifecycle().addObserver(new IPresenter());
class IPresenter implements LifecycleObserver {
@OnLifecycleEvent(Lifecycle.Event.ON_CREATE)
void onCreate( LifecycleOwner owner){
tv.setText(tv.getText()+"onCreate\n");
}
。。。。。。。
}
但是他有一个枚举类,有点意思
Lifecycle.State 是一个枚举类,用于描述当前 生命周期拥有者 的状态,与onStop之类的有点不一样,更加宽泛点 ,
通过此函数获取getLifecycle().getCurrentState(),
DESTROYED:onDestroy执行中和之后
INITIALIZED : onCreate执行前包括执行时
CREATED:onCreate与onStop之间
STARTED:onStart与onPause之间
RESUMED:onResume执行中和 到 onPause开始执行前
还有一个特殊的比较方法,getLifecycle().getCurrentState().isAtLeast(Lifecycle.State.STARTED),这个比较的当前值的等级是否等于或高于给的值,
也就是说State枚举类里的值越往后面,值越大,如果当前是 RESUMED ,那上面这个判断是 true
所以在我们刷新界面时,可以添加一个判断 if(getLifecycle().getCurrentState().isAtLeast(Lifecycle.State.STARTED)),保证当前activity 可见时才刷新
LIveData是一个抽象类,实现类有MutableLiveData、MediatorLiveData
MutableLiveData:这个数据类有着监听自身变化的能力,并且通过监听者模式告诉 其他组件数据更新。这个能够与ViewModel、Room配合,这个后面说
MutableLiveData num=new MutableLiveData<>();
MyObserver observer;
observer=new MyObserver();
num.observe(this,observer);
class MyObserver implements Observer {
@Override
public void onChanged(@Nullable Integer integer) {
tv.setText(""+integer);
}
}
MediatorLiveData 与 MutableLiveData的不同之处在于,他能统合 MutableLiveData,就像是一个ArrayList添加一个list一样,不仅数据添加进去了,而且如果添加进去的 MutableLiveData 有Observer ,而MediatorLiveData 也有Observer,在这个MutableLiveData 数据发生改变时, MediatorLiveData 的Observer也会触发。
以下代码就是将MutableLiveData添加到 MediatorLiveData,
mediatorLiveData.addSource(num,mediatorLiveData::setValue);
我们先看看我们如何获取ViewModel这个类的对象
ViewModelProviders.of(getActivity()).get(MyViewModel.class);
而这个getActivity()也可以换成Fragment的实例
现在我说一下这个为何能够做到跨组件通讯,因为它能跨组件获取同一个实例
比如:在ActivityA里有FragmentA、FragmentB,你在FragmentA通过给getActivity()得到ViewModel的实例,和你在FragmentB也这样做得到的实例是一样的。说白了,在FragmentA你如果改变这个ViewModel的LIveData这样的属性,在FragmentB能够监听的到
viewModel= ViewModelProviders.of(getActivity()).get(MyViewModel.class);
btn_add.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
viewModel.setNum(viewModel.getNum().getValue()+1);
}
});
viewModel= ViewModelProviders.of(getActivity()).get(MyViewModel.class);
viewModel.getNum().observe(this,new MyObserver());
Room有三个部分:实体类、数据库操控类、数据库创建类
实体类,和GreenDao、Realm一样通过注解来创建表,和修改属性
这个是实体类的属性能够是LIveData
@Entity(tableName = "test")
public class TestBean {
@PrimaryKey
private long id;
private String name="";
......
}
数据库操控类,你别看他是接口类,但是他通过Room框架的注解却有着真正的操控数据的能力,
@Dao
public interface TestDao {
@Query("SELECT * FROM test")
List getAll();
@Query("SELECT * FROM test WHERE id = (:id)")
TestBean getById(int id);
@Insert
void insert(TestBean testBean);
@Delete
void delete(TestBean testBean);
@Update
void update(TestBean testBean);
}
数据库创建类,能够创建数据库,并且能够修改数据库版本
@Database(entities = {TestBean.class},version = 1)
public abstract class TestDataBase extends RoomDatabase{
public abstract TestDao testDao();
}
Room数据库的数据可以通过 给MutableLiveData,来完成对界面刷新的绑定
data.observe(this, new Observer>() {
@Override
public void onChanged(@Nullable List testBeans) {
}
});
data.setValue(testDao.getAll());
Paging实现的分页加载指的是:你如果有100条数据,但是你的屏幕能够显示出来的只有10条,那你把100条item都加载到RecyclerView里,就会浪费内存,那你先给RecyclerView加载20条数据,当你把RecyclerView拉到底部,Paging再给RecyclerView 20条数据,让他再加载出20条item给用户看。
还是三个步骤实现Paging的基础使用
第一个LiveData数据类,这个PageSize就是你每一次给RecyclerView的数据数量
val allCheeses = LivePagedListBuilder(dao.allCheesesByName(), PagedList.Config.Builder()
.setPageSize(PAGE_SIZE)
.setEnablePlaceholders(ENABLE_PLACEHOLDERS)
.build()).build()
第二个实现PagedListAdapter,其中还用了DiffUtil这个帮助RecyclerView优化更新的工具类
class CheeseAdapter : PagedListAdapter(diffCallback) {
override fun onBindViewHolder(holder: CheeseViewHolder, position: Int) {
holder.bindTo(getItem(position))
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): CheeseViewHolder =
CheeseViewHolder(parent)
companion object {
private val diffCallback = object : DiffUtil.ItemCallback() {
override fun areItemsTheSame(oldItem: Cheese, newItem: Cheese): Boolean =
oldItem.id == newItem.id
override fun areContentsTheSame(oldItem: Cheese, newItem: Cheese): Boolean =
oldItem == newItem
}
}
}
第三个将Adapter与数据库连接起来
viewModel.allCheeses.observe(this, Observer(adapter::submitList))
说实话代码太多了,大家还是看看项目代码比较好,这个例子是Kotlin的语言,我是从官方例子里弄出来的
首先用NavHostFragment占个位,并且设置xml来控制Fragment的显示和跳转
然后看看这个 nav_graph_main 写了啥,开头 这个app:startDestination 设置了默认显示Fragment
然后接下来 在fragment标签里 指定了该fragment 的具体实现类和 action(活动)
通过id 来唯一标识, name来指定 实现类
action标签描述的是Fragment跳转的 目的地和跳转动画(目的地 进入动画、出发地退出、出发地返回时 进入、目的地返回时退出), 而action标签里,id是唯一标识,app:destination指定了跳转目的地
,在Java代码通过以下触发(从出发地到目的地)
Navigation.findNavController(it).navigate(R.id.action_page2)
想要返回时(目的地到出发地)
Navigation.findNavController(it).navigateUp()
在activity点击返回键可以触发 返回动画
override fun onSupportNavigateUp() =
findNavController(this, R.id.my_nav_host_fragment).navigateUp()
Android架构组件Room的使用
Android官方架构组件Navigation:大巧不工的Fragment管理框架
Android.Arch.Paging: 分页加载的新选项
Android 架构组件(一)——Lifecycle-Aware Components
Android架构组件(三)——ViewModel
官网
官方github 源码