默認是在一個fragment中實現RecyclerView.
private inner class CrimeAdapter() : ListAdapter<Crime, CrimeHolder>(mDiffCallback) { override fun onCreateViewHolder( parent: ViewGroup, viewType: Int ): CrimeHolder {//這個parent就是RecyclerView;在這個方法中綁定子項視圖 val view = layoutInflater.inflate( R.layout.fragment_crime_list_item, parent, false )//負責創建要顯示的視圖,並封裝到ViewHolder里 return CrimeHolder(view) } override fun onBindViewHolder( holder: CrimeHolder, position: Int ) {//負責將數據集里指定位置的crime數據發送給指定的ViewHolder val crime = currentList[position] holder.bind(crime)//把viewholder和adapter的處理邏輯分開,只需調用viewholder的方法 } }
- 構造函數里也不需要傳一個list了,在onBindViewHolder里原來是list[position],現在可以用currentList。這個字段是ListAdapter里面的。之前我的一個bug就是這個B弄的。
- 繼承自ListAdapter, <模型類, 自定義holder>, 參數需要一個DiffUtil.ItemCallback<Crime>()實例,可以在這個fragment里以伴隨類的方式定義。
companion object { val mDiffCallback = object : DiffUtil.ItemCallback<Crime>() {//作為ListAdapter的參數 override fun areItemsTheSame(oldItem: Crime, newItem: Crime): Boolean { return oldItem.id == newItem.id } override fun areContentsTheSame(oldItem: Crime, newItem: Crime): Boolean { return oldItem.title == newItem.title } }
必須實現這兩個方法,第一個方法檢測item是否相同,第二個檢測item內容是否相同。這兩個方法都很重要。第二個方法如果比較的item的屬性不對,更新會有延遲!!比如說比較的是id,那我更改了title,我回到列表界面時,他判斷我的id沒變,所以內容沒變,結果他不給我更新我操他媽的。上面那個寫法也是不完整的,只比較了title,那我更改了其他信息,回到主界面也是不會立即更新的,會有一個延遲。
- 重頭戲!!!那如何提交recycleview列表的更新呢?首先在fragment字段中定義一個adapter, 然后讓recyclerview的adapter字段指向這個adapter, 然后因為傳進去的列表是livedata,所以我們用observe檢測數據變化,一有變化就用ListAdapter里的方法——submitList來提交,到這里就明白了,我們取消了原來以參數的方式傳列表進去,改為submitList.(更刪改查都用它,反正有變化就用它)
private var mAdapter: CrimeAdapter = CrimeAdapter()
override fun onCreateView(//onCreateview方法里不用做什么和本文相關的事,看下面 inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? ): View { _binding = FragmentCrimeListBinding.inflate(inflater, container, false) binding.crimeRecyclerview.layoutManager = LinearLayoutManager(context)//把加載的recyclerview視圖的布局設為線性布局 return binding.root }
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) binding.crimeRecyclerview.adapter = mAdapter// crimeListViewModel.crimeListLiveData.observe( viewLifecycleOwner, { crimes -> crimes?.let { mAdapter.submitList(crimes)//有更新就提交給adapter } } ) }
- 注意!!傳給submitList的列表不能是同一個列表引用,如果是同一個列表引用的話要拷貝一份,
新拿到的數據根本沒有更新到列表中來。問題可能出在 submitList() 方法上: public void submitList(@Nullable List<T> list) { mDiffer.submitList(list); } 最終調用的是 AsyncListDiffer 類中的 submitList() 方法: public void submitList(@Nullable final List<T> newList, @Nullable final Runnable commitCallback) { // incrementing generation means any currently-running diffs are discarded when they finish final int runGeneration = ++mMaxScheduledGeneration; if (newList == mList) { // nothing to do (Note - still had to inc generation, since may have ongoing work) if (commitCallback != null) { commitCallback.run(); } return; } final List<T> previousList = mReadOnlyList; // fast simple remove all if (newList == null) { //noinspection ConstantConditions int countRemoved = mList.size(); mList = null; mReadOnlyList = Collections.emptyList(); // notify last, after list is updated mUpdateCallback.onRemoved(0, countRemoved); onCurrentListChanged(previousList, commitCallback); return; } // fast simple first insert if (mList == null) { mList = newList; mReadOnlyList = Collections.unmodifiableList(newList); // notify last, after list is updated mUpdateCallback.onInserted(0, newList.size()); onCurrentListChanged(previousList, commitCallback); return; } final List<T> oldList = mList; // 省略一些代碼。 } 第一次我們調用 submitList() 方法時,把成員變量集合 list 傳遞過來,那么 newList 參數就不為 null,在第 25 行,會進入 if 語句,把 newList 賦值給 mList,也就是把 list 賦值給 mList。 第二次我們調用submitList() 方法時,仍是把成員變量集合 list 傳遞過來,這時 mList 就是指向的 list,所以第 5 行的 if (newList == mList) 成立,進而直接 return 掉 submitList() 方法。新的數據並未更新到列表中。
因為livedata的observe傳給我們的不是同一個列表引用,所以可以直接放進去submitList.
最后加上一份官方的示例代碼
A complete usage pattern with Room would look like this: * <pre> * {@literal @}Dao * interface UserDao { * {@literal @}Query("SELECT * FROM user ORDER BY lastName ASC") * public abstract LiveData<List<User>> usersByLastName(); * } * * class MyViewModel extends ViewModel { * public final LiveData<List<User>> usersList; * public MyViewModel(UserDao userDao) { * usersList = userDao.usersByLastName(); * } * } * * class MyActivity extends AppCompatActivity { * {@literal @}Override * public void onCreate(Bundle savedState) { * super.onCreate(savedState); * MyViewModel viewModel = new ViewModelProvider(this).get(MyViewModel.class); * RecyclerView recyclerView = findViewById(R.id.user_list); * UserAdapter<User> adapter = new UserAdapter(); * viewModel.usersList.observe(this, list -> adapter.submitList(list));/////////直接用行了 * recyclerView.setAdapter(adapter); * } * } * * class UserAdapter extends ListAdapter<User, UserViewHolder> { * public UserAdapter() { * super(User.DIFF_CALLBACK); * } * {@literal @}Override * public void onBindViewHolder(UserViewHolder holder, int position) { * holder.bindTo(getItem(position)); * } * public static final DiffUtil.ItemCallback<User> DIFF_CALLBACK = * new DiffUtil.ItemCallback<User>() { * {@literal @}Override * public boolean areItemsTheSame( * {@literal @}NonNull User oldUser, {@literal @}NonNull User newUser) { * // User properties may have changed if reloaded from the DB, but ID is fixed * return oldUser.getId() == newUser.getId(); * } * {@literal @}Override * public boolean areContentsTheSame( * {@literal @}NonNull User oldUser, {@literal @}NonNull User newUser) { * // NOTE: if you use equals, your object must properly override Object#equals() * // Incorrectly returning false here will result in too many animations. * return oldUser.equals(newUser); * } * } * }</pre>