如何正確地使用RecyclerView.ListAdapter


默認是在一個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的方法
        }

    }
  1. 構造函數里也不需要傳一個list了,在onBindViewHolder里原來是list[position],現在可以用currentList。這個字段是ListAdapter里面的。之前我的一個bug就是這個B弄的。
  2. 繼承自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,那我更改了其他信息,回到主界面也是不會立即更新的,會有一個延遲。

  3. 重頭戲!!!那如何提交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
                    }
                }
            )
        }

     

  4. 注意!!傳給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&lt;List&lt;User>> usersByLastName();
 * }
 *
 * class MyViewModel extends ViewModel {
 *     public final LiveData&lt;List&lt;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&lt;User> adapter = new UserAdapter();
 *         viewModel.usersList.observe(this, list -> adapter.submitList(list));/////////直接用行了
 *         recyclerView.setAdapter(adapter);
 *     }
 * }
 *
 * class UserAdapter extends ListAdapter&lt;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&lt;User> DIFF_CALLBACK =
 *             new DiffUtil.ItemCallback&lt;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>

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM