一.項目簡介
本項目實現了對Lifecycle,LiveData,ViewModel,Room,Paging,Navigation這六個官方構架組件的全面使用,組件的單獨使用或者合作使用都有(PS:WorkManager bug 太多,不建議使用,我這裏也不會提到他,因爲我反正是有坑,邁不過去)
貼一張項目主界面圖,大家可以先下載項目自己運行一下
二.對於六大組件的簡介
1.Lifecycle(監聽activity生命週期)
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 可見時才刷新
2.LiveData(監聽自身變化的數據類)
LIveData是一個抽象類,實現類有MutableLiveData、MediatorLiveData
MutableLiveData:這個數據類有着監聽自身變化的能力,並且通過監聽者模式告訴 其他組件數據更新。這個能夠與ViewModel、Room配合,這個後面說
MutableLiveData<Integer> num=new MutableLiveData<>();
MyObserver observer;
observer=new MyObserver();
num.observe(this,observer);
class MyObserver implements Observer<Integer> {
@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);
3.ViewModel(跨組件通訊)
我們先看看我們如何獲取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());
4.Room(數據庫框架)
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<TestBean> 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<List<TestBean>>() {
@Override
public void onChanged(@Nullable List<TestBean> testBeans) {
}
});
data.setValue(testDao.getAll());
5.Paging(分頁加載)
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<Cheese, CheeseViewHolder>(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<Cheese>() {
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的語言,我是從官方例子裏弄出來的
6.Navigation(Fragment控制框架)
首先用NavHostFragment佔個位,並且設置xml來控制Fragment的顯示和跳轉
<fragment
android:id="@+id/my_nav_host_fragment"
android:name="androidx.navigation.fragment.NavHostFragment"
app:defaultNavHost="true"
app:navGraph="@navigation/nav_graph_main" />
然後看看這個 nav_graph_main 寫了啥,開頭 這個app:startDestination 設置了默認顯示Fragment
<navigation
app:startDestination="@id/page1Fragment">
然後接下來 在fragment標籤裏 指定了該fragment 的具體實現類和 action(活動)
通過id 來唯一標識, name來指定 實現類
<fragment
android:id="@+id/page1Fragment"
android:name="com.example.lilingzhi.llvr.fragment.MainPage1Fragment"
android:label="fragment_page1"
tools:layout="@layout/fragment_main_page1">
<action
android:id="@+id/action_page2"
app:destination="@id/page2Fragment"
app:enterAnim="@anim/slide_right_in"
app:exitAnim="@anim/slide_left_out"
app:popEnterAnim="@anim/slide_left_in"
app:popExitAnim="@anim/slide_right_out" />
</fragment>
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架構組件(二)——LiveData
Android官方架構組件Navigation:大巧不工的Fragment管理框架
Android 架構組件(一)——Lifecycle-Aware Components