RecyclerView 和 ListView 使用對比分析(佈局、API、巢狀滾動機制)
空資料處理
ListView 提供了 setEmptyView 這個 API 來讓我們處理 Adapter 中資料為空的情況,只需輕輕一 set 就能搞定一切。程式碼設定和效果如下
mListView = (ListView) findViewById(R.id.listview);
mListView.setEmptyView(findViewById(R.id.empty_layout));//設定內容為空時顯示的檢視
而 RecyclerView 並沒有提供此類 API,所以,這些工作需要自己來幹。雖說這類邏輯並不複雜,但是作為一個有追求的程式猿,能偷懶還是要想著偷懶的嘛...
HeaderView 和 FooterView
在 ListView 的設計中,存在著 HeaderView 和 FooterView 兩種型別的檢視,並且系統也提供了相應的 API 來讓我們設定
使用 HeaderView 和 FooterView 的好處在於,當我們指向在 ListView 的頭部或者底部新增一個 View 的時候(例如:新增一個下拉重新整理檢視,底部載入更多檢視),我們可以不用影響到 Adapter 的編寫,使用起來相當方便。而到了 RecyclerView 中,翻來翻去你都不會看到類似 addFooterView 、 addFooterView 這種 API,是的,沒錯,壓根就沒有...這也是 RecyclerView 讓我覺得很雞肋的地方,按道理說應該是使用頻率很高的 API,居然都不給我(一臉懵逼)。那有木有解決方法呢,肯定有,系統不給就自己動手豐衣足食唄。我想到的方法比較笨,就是在
Adapter 中提供三種類型(Header,Footer以及普通Item)的 Type 和 View,但是這種方法寫起來很麻煩,對 Adapter 的影響很大,改動的程式碼量多並且也容易產生BUG。這裡需要吹一下鴻洋老師的解決方案了,大家可以看他的文章:
區域性重新整理
在 ListView 中,說到重新整理很多童鞋會記得 notifyDataSetChanged() ,但是說到區域性重新整理估計有很多童鞋就知道得比較少了。我們知道在更新了 ListView 的資料來源後,需要通過 Adapter 的 notifyDataSetChanged 來通知檢視更新變化,這樣做比較的好處就是呼叫簡單,壞處就是它會重繪每個 Item,但實際上並不是每個 Item 都需要重繪。最常見的,例如:朋友圈點贊,點贊只是更新當前點讚的Item,並不需要每個 Item 都更新。然而 ListView 並沒有提供區域性重新整理重新整理某個 Item 的 API 給我們,同樣自己自足,套路大致如下方的 updateItemView:
public class AuthorListAdapter extends BaseAdapter {
...
@Override
public View getView(int position, View convertView, ViewGroup parent) {
...
return convertView;
}
/**
* 更新Item檢視,減少不必要的重繪
*
* @param listView
* @param position
*/
public void updateItemView(ListView listView, int position) {
//換算成 Item View 在 ViewGroup 中的 index
int index = position - listView.getFirstVisiblePosition();
if (index >= 0 && index < listView.getChildCount()) {
//更新資料
AuthorInfo authorInfo = mAuthorInfoList.get(position);
authorInfo.setNickName("Google Android");
authorInfo.setMotto("My name is Android .");
authorInfo.setPortrait(R.mipmap.ic_launcher);
//更新單個Item
View itemView = listView.getChildAt(index);
getView(position, itemView, listView);
}
}
}
即可實現重新整理單個 Item 的效果
RecyclerView.Adapter 則我們提供了 notifyItemChanged 用於更新單個 Item View 的重新整理,我們可以省去自己寫區域性更新的工作。
實現效果如下
動畫效果
如果你細心觀察上面 ListView 和 RecyclerView 區域性更新 Item 的效果,你會發現相比 ListView 而言, RecyclerView 在做區域性重新整理的時候有一個漸變的動畫效果。這也是 RecyclerView 相對非常值得一提的地方,作為 ListView 自身並沒有為我們提供封裝好的 API 來實現動畫效果切換。所以,如果要給 ListView 的 Item 加動畫,我們只能自己通過屬性動畫來操作 Item 的檢視。 Github 也有很多封裝得好好的開源庫給我們用,如:ListViewAnimations 就封裝了大量的效果供我們玩耍,童鞋們可以自行學習一下
ListViewAnimations 主要大致實現方式是通過裝飾者模式來擴充 Adapter ,並結合屬性動畫 Animator 來新增動畫效果。相比之下,RecyclerView 則為我們提供了很多基本的動畫 API ,如下方的增刪移改
簡單的呼叫即可實現相應的效果,用起來方便很多,視覺互動上也會更好些
如果你對動畫效果有追求,覺得系統提供的並不能滿足你的需求,也可以通過相應介面實現自己的動畫效果,方式也非常簡單,繼承 RecyclerView.ItemAnimator 類,並實現相應的方法,再呼叫 RecyclerView 的 setItemAnimator(RecyclerView.ItemAnimator animator) 方法設定完即可實現自定義的動畫效果。
系統也為我們提供了兩個預設的動畫實現:SimpleItemAnimator 和 DefaultItemAnimator。而 RecyclerView 在不手動呼叫 setItemAnimator 的情況下,則預設用了內建的 DefaultItemAnimator 。
當然編寫自定義的 ItemAnimator 也是需要一定工作量的,這裡同樣為大家介紹一個針對 RecyclerView 開源的動畫庫:recyclerview-animators。其內部封裝了大量的動畫效果給供我們呼叫。