Fastadapter使RecyclerView更加簡便高效
翻譯自文章 http://blog.grafixartist.com/recyclerview-adapter-android-made-fast-easy/
使用Android RecyclerView最麻煩的莫過于使用其[adapter][]了,如果界面再復雜一些眷唉,adapter里面需要包含多個[RecyclerView.ViewHolder][viewholder]就更復雜了予颤,這里面還不包括處理各種點擊事件,點擊拖動等等其他一些很酷的功能冬阳。如果你正為此發(fā)愁蛤虐,那么這篇文章就是為你準備的。當然如果你熟悉[RecyclerView.Adapter][adapter]的標準寫法了摩泪,但是簡單的重復寫相同的代碼是很浪費時間的,那有沒有更好的辦法呢劫谅?
[adapter]: <https://developer.android.com/reference/android/support/v7/widget/RecyclerView.Adapter.html "RecyclerView.Adapter"
[viewholder]: https://developer.android.com/reference/android/support/v7/widget/RecyclerView.ViewHolder.html
Say hello to FastAdapter !
The bullet proof, fast and easy to use adapter library, which minimizes developing time to a fraction… – Mike Penz
穩(wěn)定见坑、高效嚷掠、簡單的adapter庫,能夠有效節(jié)省開發(fā)時間
Mike Penz 不僅編寫了FastAdapter荞驴,而且還編寫了MaterialDrawer和AboutLibraries兩個比較火的開源庫不皆,FastAdapter中的Demo也有集成這些功能。
FastAdapter不僅僅只是減少了你的開發(fā)時間熊楼,而且它還提供了許多非常棒的功能霹娄,是時候用FastAdapter替換原來簡單形式的RecyclerView Adapters了。點擊事件(Click listeners)鲫骗,多項選擇(Multi-selection)犬耻,過濾器(filtering),拖拽功能(drag and drop)执泰,加入表頭(headers)等等枕磁,只要你能說出來的,FastAdapter都能提供术吝。
那么计济,還等什么?上車吧排苍,騷年沦寂!
(滴~~~,學生卡L匝谩传藏!)
FastAdapter庫分為核心和擴展兩個部分,所以幔翰,在Android Studio中的build.gradle
中添加如下依賴:
compile('com.mikepenz:fastadapter:1.8.2@aar') {
transitive = true
}
其余擴展包在下面的兩個庫中:(我的建議是第一個一定要加漩氨,第二個隨便)
compile 'com.mikepenz:fastadapter-extensions:1.8.0@aar'
//The tiny Materialize library used for its useful helper classes
compile 'com.mikepenz:materialize:1.0.0@aar'
最新的配置還請看FastAdapter首頁說明。
建立模型類(Creating the Model class)
用作者最喜歡的芒果為例遗增,寫個相關(guān)app
public class Mango {
private String name, description, imageUrl;
public Mango() { }
public Mango(String name, String description, String imageUrl) {
this.name = name;
this.description = description;
this.imageUrl = imageUrl;
}
// Your variable Getter Setters here
}
實現(xiàn)Adapter(Implementing the Adapter)
FastAdapter使用上面你的定義的模型類(model class)來創(chuàng)建RecyclerView.Adapter
和RecyclerView.ViewHolder
叫惊。所以讓你的類實現(xiàn)AbstractItem<Item, Item.ViewHolder>
這個接口:
-
getType()
– 返回一個唯一的ID。這里這個ID一定要是唯一的不能重復做修,推薦的使用方法是在app/res/values/
目錄下新建一個文件霍狰,在其中指定。參考Android文檔說明饰及。 -
getLayoutRes()
– 返回你布局文件的id蔗坯,(R.layout.yours)
-
bindView()
– 和RecyclerView的onBindViewHolder()
方法一樣
public class Mango extends AbstractItem<Mango, Mango.ViewHolder> {
private String name, imageUrl, description;
public Mango() { }
public Mango(String name, String imageUrl) {
this.name = name;
this.imageUrl = imageUrl;
}
// Your Getter Setters here
// Fast Adapter methods
@Override
public int getType() { return R.id.item_card; }
@Override
public int getLayoutRes() { return R.layout.list_item; }
@Override
public void bindView(ViewHolder holder) {
super.bindView(holder);
}
// Manually create the ViewHolder class
protected static class ViewHolder extends RecyclerView.ViewHolder {
public ViewHolder(View itemView) {
super(itemView);
}
}
}
綁定FastAdapter到RecyclerView(Marrying FastAdapter to RecyclerView)
FastItemAdapter<Mango> fastAdapter = new FastItemAdapter<>();
recyclerView.setAdapter(fastAdapter);
// Fetch your data here
List<Mango> mangoes = loadMangoes();
fastAdapter.add(mangoes);
就是這么簡單了,這里所做的和普通的RecyclerView.Adapter
沒有什么不一樣燎含。
來回顧一下我們以前所做的事情宾濒,我們沒有做一下這些
- 建立一個
RecyclerView.Adapter
的子類 - 加載每一項的布局文件
- 還有煩人的
getItemCount()
由此可見FastAdapter確實能夠很方便的建立一個列表。但這還不是全部屏箍,F(xiàn)astAdapter還能提供更多的功能绘梦。
功能列表(Feature List)
Let’s see how some popular RecyclerView features are done with FastAdapter. Use the below index to navigate.
- 點擊事件 Click listener
- 數(shù)據(jù)過濾 Filtering data with Search
- 拖拽功能 Drag and drop
- 多項選擇 Multi-select with CAB (Contextual Action Bar) and Undo action
- 添加列表頭 Adding Header view (multiple ViewHolders)
- 無限滾動 Infinite (endless) scrolling
1. 點擊事件 Click listener <a name="Click"/>
FastAdapter設置為可選擇模式后設置點擊監(jiān)聽
fastAdapter.withSelectable(true);
fastAdapter.withOnClickListener(new FastAdapter.OnClickListener<Mango>() {
@Override
public boolean onClick(View v, IAdapter<Mango> adapter, Mango item, int position) {
// Handle click here
return true;
}
});
2. 數(shù)據(jù)過濾 Filtering data with Search <a name="Filtering"/>
如果你使用了SearchView
橘忱,F(xiàn)astAdapter提供了接口可以為你過濾查詢結(jié)果
// Call this in your Search listener
fastAdapter.filter("yourSearchTerm");
fastAdapter.withFilterPredicate(new IItemAdapter.Predicate<Mango>() {
@Override
public boolean filter(Mango item, CharSequence constraint) {
return item.getName().startsWith(String.valueOf(constraint));
}
});
留意filter(Mango item, CharSequence constraint)
方法。返回true
意味著你要把這些項目從adapter中移除卸奉;如果要保留這些項目在adapter中钝诚,移除其他東西,你需要返回false
榄棵。
3. 拖拽功能 Drag and drop <a name="Dragdrop"/>
首先建立一個SimpleDragCallback
的實例凝颇,其次用這個實例初始化ItemTouchHelper
,最后把ItemTouchHelper
綁定到RecyclerView
。
SimpleDragCallback dragCallback = new SimpleDragCallback(this);
ItemTouchHelper touchHelper = new ItemTouchHelper(dragCallback);
touchHelper.attachToRecyclerView(recyclerView);
在Activity中實現(xiàn)ItemTouchCallback
接口疹鳄,然后覆蓋itemTouchOnMove()
方法
@Override
public boolean itemTouchOnMove(int oldPosition, int newPosition) {
Collections.swap(fastAdapter.getAdapterItems(), oldPosition, newPosition); // change position
fastAdapter.notifyAdapterItemMoved(oldPosition, newPosition);
return true;
}
4. 多項選擇 Multi-select with CAB (Contextual Action Bar) and Undo action <a name="Multiselect"/>
在Activity
中建立一個內(nèi)部類ActionBarCallBack
拧略,然后讓其實現(xiàn)ActionMode.Callback
接口。
private class ActionBarCallBack implements ActionMode.Callback {
@Override
public boolean onCreateActionMode(ActionMode mode, Menu menu) { return true; }
@Override
public boolean onPrepareActionMode(ActionMode mode, Menu menu) { return false; }
@Override
public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
mode.finish();
return true;
}
@Override
public void onDestroyActionMode(ActionMode mode) { }
}
通過FastAdapter初始化ActionModeHelper
尚辑。
fastAdapter.setHasStableIds(true);
fastAdapter.withSelectable(true);
fastAdapter.withMultiSelect(true);
fastAdapter.withSelectOnLongClick(true);
actionModeHelper = new ActionModeHelper(fastAdapter, R.menu.cab, new ActionBarCallBack());
常用的onClick
方法用來處理其他事件了辑鲤,比如說點擊進入細節(jié)展示頁面(detail navigation)。所以FastAdapter提供了兩個新的接口preClick
和preLongClick
來處理當前的選擇點擊事件(CAB)杠茬。
fastAdapter.withOnPreClickListener(new FastAdapter.OnClickListener<Mango>() {
@Override
public boolean onClick(View v, IAdapter<Mango> adapter, Mango item, int position) {
Boolean res = actionModeHelper.onClick(item);
return res != null ? res : false;
}
});
fastAdapter.withOnPreLongClickListener(new FastAdapter.OnLongClickListener<Mango>() {
@Override
public boolean onLongClick(View v, IAdapter<Mango> adapter, Mango item, int position) {
ActionMode actionMode = actionModeHelper.onLongClick(MainActivity.this, position);
if (actionMode != null) {
// Set CAB background color
findViewById(R.id.action_mode_bar).setBackgroundColor(Color.GRAY);
}
return actionMode != null;
}
});
注意月褥,如果Action Mode沒有開啟,不要處理preClick
事件瓢喉,直接返回false
宁赤。同時它允許我們處理常規(guī)的點擊事件。只是注意當你同時使用withOnClickListener()
的時候栓票,也要返回false
决左。
最后,在values/styles.xml文件中的AppTheme里開啟windowActionModeOverlay
選項:
<item name="windowActionModeOverlay">true</item>
撤銷功能(Undo Action)
在CAB模式中刪除項目走贪,請結(jié)合UndoHelper
一起使用佛猛。這個類可以幫助我們實現(xiàn)刪除撤銷功能。
UndoHelper undoHelper = new UndoHelper(fastAdapter, new UndoHelper.UndoListener<Mango>() {
@Override
public void commitRemove(Set<Integer> positions, ArrayList<FastAdapter.RelativeInfo<Mango>> removed) {
Log.d("remove", "removed: " + removed.size());
}
});
在這個接口的實現(xiàn)方法中我們記錄了刪除對象的個數(shù)坠狡。UndoHelper.UndoListener
同時也能幫助我們得到被刪除對象在列表中的位置继找。
還記得我們創(chuàng)建的那個內(nèi)部類ActionBarCallBack
嗎?我們現(xiàn)在來修改下其中的onActionItemClicked()
方法:
@Override
public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
undoHelper.remove(findViewById(android.R.id.content), "Item removed", "Undo", Snackbar.LENGTH_LONG, fastAdapter.getSelections());
mode.finish();
return true;
}
UndoHelper的remove()
方法逃沿,才是最終執(zhí)行刪除對象的地方婴渡。刪除執(zhí)行以后會出現(xiàn)一個SnackBar提醒我們刪除成功,同時也給我了我們一個便捷的撤銷最后一次刪除操作的接口凯亮。
5. 添加列表頭 Adding Header view (multiple ViewHolders) <a name="Header"/>
你可以選擇修改之前創(chuàng)建的Mango類边臼,但是基于代碼整潔的考慮,我們這里新建立一個類去加載Header view假消。
public class Header extends AbstractItem<Header, Header.ViewHolder> {
String title;
public Header(String title) {
this.title = title;
}
// Your getter setters here
// AbstractItem methods
@Override
public int getType() {
return R.id.header_text;
}
@Override
public int getLayoutRes() {
return R.layout.header_item;
}
@Override
public void bindView(ViewHolder holder) {
super.bindView(holder);
holder.textView.setText(title);
}
protected static class ViewHolder extends RecyclerView.ViewHolder {
@BindView(R.id.header_text) TextView textView;
public ViewHolder(View itemView) {
super(itemView);
ButterKnife.bind(this, itemView);
}
}
}
這里的Header只顯示一個簡的單TextView
柠并。然后開始實例化Adapter變量:
FastItemAdapter fastAdapter = new FastItemAdapter<>();
fastAdapter.setHasStableIds(true);
fastAdapter.withSelectable(true);
HeaderAdapter<Header> headerAdapter = new HeaderAdapter<>();
回憶一下之前我們是如何實例化FastAdapter的:
FastItemAdapter<Mango> fastAdapter = new FastItemAdapter<>();
但是現(xiàn)在我們并沒有直接指定類型,這樣可以使用們的FastAdapter同時保存Mango和Header兩種類型。
當然你也可以選擇實例化一個泛型FastAdapter(generic type FastAdapter):
FastItemAdapter<IItem> fastAdapter = new FastItemAdapter<>();
IItem
是你自定義的Model的基類臼予,所以你可以這樣初始化Adapter亿傅,不過要記得在添加數(shù)據(jù)的時候進行數(shù)據(jù)轉(zhuǎn)換。
組裝adapter(Setting the adapter)
這里和之前有點不一樣瘟栖,我們使用wrap(FastAdapter)
方法,同時把headerAdapter
和fastAdapter
添加到RecyclerView
里谅阿。
recyclerView.setAdapter(headerAdapter.wrap(fastAdapter));
如果你還想添加第三個不同類型的ViewHolder
半哟,同樣可以再使用wrap()
方法進行。
recyclerView.setAdapter(thirdAdapter.wrap(headerAdapter.wrap(fastAdapter)));
添加數(shù)據(jù) Adding data
我想在頂部和中間各添加一個Header view签餐,而每個Header view都應該有一個不同的數(shù)據(jù)集寓涨。比如說我想添加這樣的數(shù)據(jù):
int half = mangoes.size() / 2;
fastAdapter.add(0, new Header("First half").withIdentifier(1));
fastAdapter.add(1, mangoes.subList(0, half));
fastAdapter.add(half + 1, new Header("Second half").withIdentifier(2));
fastAdapter.add(half + 2, mangoes.subList(0, half));
在上面的代碼中,我把mangoes
的前半段數(shù)據(jù)放在了第一個header后面氯檐,后半段數(shù)據(jù)放在了第二個header后面戒良。
請注意half
這個int
類型的變量是不斷增加的,跟其他的List
的position
一樣冠摄。
使用不同的ViewHolders (Manipulating with different ViewHolders)
現(xiàn)在FastAdapter已經(jīng)包含了幾個不同View和數(shù)據(jù)糯崎,但是我們?nèi)绾问褂盟鼈兡兀勘热缣幚睃c擊河泳,還有上面介紹的其他酷炫的功能呢沃呢?
簡單來說,就是使用 Java的關(guān)鍵字:instanceof
.
下面我將介紹一下如何處理FastAdapter的點擊事件:
fastAdapter.withOnClickListener(new FastAdapter.OnClickListener<IItem>() {
@Override
public boolean onClick(View v, IAdapter<IItem> adapter, IItem item, int position) {
if (item instanceof Mango) {
// Do your thing!
} else if (item instanceof Header) {
// Header clicks usually don't do anything. Ignore if you like.
}
return true;
}
});
看到區(qū)別了嗎拆挥?之前我們使用同樣的監(jiān)聽點擊代碼的時候薄霜,onClick()
方法傳遞的參數(shù)是Mango類型,但是當我們使用了泛型的FastAdapter時纸兔,方法的參數(shù)變成了IItem
(基類) 對象惰瓜。
6. 無限滾動 Infinite (endless) scrolling <a name="Infinitescrolling"/>
這個功能主要使用在社交類型的app中,每次需要加載指定數(shù)量的信息汉矿,當你到達了信息底部的時候崎坊,出現(xiàn)一個加載標志,然后再加載指定數(shù)量的信息负甸。
實現(xiàn)這個功能的關(guān)鍵就在RecyclerView
的addOnScrollListener()
方法流强。但是我們知道,真正困難的地方就在于構(gòu)建這個監(jiān)聽器呻待,基于此打月,F(xiàn)astAdapter為我們提供了EndlessRecyclerOnScrollListener()
方法。
我們先寫一個FooterAdapter
蚕捉,我們用這個在列表底部顯示加載數(shù)據(jù)時候的ProgressBar
奏篙。
FooterAdapter<ProgressItem> footerAdapter = new FooterAdapter<>();
ProgressItem在FastAdapter extensions庫中,并不是core庫中的類型,所以需要在工程中加載此庫秘通,前面有說明为严。
recyclerView.addOnScrollListener(new EndlessRecyclerOnScrollListener() {
@Override
public void onLoadMore(int currentPage) {
footerAdapter.clear();
footerAdapter.add(new ProgressItem().withEnabled(false));
// Load your items here and add it to FastAdapter
fastAdapter.add(newMangoes);
}
});
實現(xiàn)無限滾動就是這樣簡單。So easy肺稀!
寫在最后 (Wrapping it up)
我想這是我最長的一篇文章了第股!為你們的耐心和堅持鼓掌。
RecyclerView的魔力就在于它的Adapter话原。FastAdapter也著力于此夕吻,盡量簡化Adapter的使用,同時致力于提供更多的功能繁仁,而不僅僅是為了方便使用涉馅。如果你需要處理大量Adapter,那么FastAdapter將是不二之選黄虱!
Resources
- mikepenz/FastAdapter library on GitHub (library and samples)
Source Code
文章中的代碼可以在我的GitHub Gist找到稚矿。
FastAdapter可以讓你輕快地做到普通的RecyclerView能做到的任何事。我已經(jīng)開始在我的工程中使用它了捻浦,你準備好了嗎晤揣?在下面的評論中留下你的看法吧。