Fastadapter使RecyclerView更加简便高效
翻译自文章 http://blog.grafixartist.com/recyclerview-adapter-android-made-fast-easy/
使用Android RecyclerView最麻烦的莫过于使用其[adapter][]了,如果界面再复杂一些,adapter里面需要包含多个[RecyclerView.ViewHolder][viewholder]就更复杂了,这里面还不包括处理各种点击事件,点击拖动等等其他一些很酷的功能。如果你正为此发愁,那么这篇文章就是为你准备的。当然如果你熟悉[RecyclerView.Adapter][adapter]的标准写法了,但是简单的重复写相同的代码是很浪费时间的,那有没有更好的办法呢?
[adapter]:
Say hello to FastAdapter !
The bullet proof, fast and easy to use adapter library, which minimizes developing time to a fraction… – Mike Penz
稳定、高效、简单的adapter库,能够有效节省开发时间
Mike Penz 不仅编写了FastAdapter,而且还编写了MaterialDrawer和AboutLibraries两个比较火的开源库,FastAdapter中的Demo也有集成这些功能。
FastAdapter不仅仅只是减少了你的开发时间,而且它还提供了许多非常棒的功能,是时候用FastAdapter替换原来简单形式的RecyclerView Adapters了。点击事件(Click listeners),多项选择(Multi-selection),过滤器(filtering),拖拽功能(drag and drop),加入表头(headers)等等,只要你能说出来的,FastAdapter都能提供。
那么,还等什么?上车吧,骚年!
(滴~~~,学生卡!!)
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)
用作者最喜欢的芒果为例,写个相关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
}
实现Adapter(Implementing the Adapter)
FastAdapter使用上面你的定义的模型类(model class)来创建RecyclerView.Adapter
和RecyclerView.ViewHolder
。所以让你的类实现AbstractItem
这个接口:
-
getType()
– 返回一个唯一的ID。这里这个ID一定要是唯一的不能重复,推荐的使用方法是在app/res/values/
目录下新建一个文件,在其中指定。参考Android文档说明。 -
getLayoutRes()
– 返回你布局文件的id,(R.layout.yours)
-
bindView()
– 和RecyclerView的onBindViewHolder()
方法一样
public class Mango extends AbstractItem {
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 fastAdapter = new FastItemAdapter<>();
recyclerView.setAdapter(fastAdapter);
// Fetch your data here
List mangoes = loadMangoes();
fastAdapter.add(mangoes);
就是这么简单了,这里所做的和普通的RecyclerView.Adapter
没有什么不一样。
来回顾一下我们以前所做的事情,我们没有做一下这些
- 建立一个
RecyclerView.Adapter
的子类 - 加载每一项的布局文件
- 还有烦人的
getItemCount()
由此可见FastAdapter确实能够很方便的建立一个列表。但这还不是全部,FastAdapter还能提供更多的功能。
功能列表(Feature List)
Let’s see how some popular RecyclerView features are done with FastAdapter. Use the below index to navigate.
- 点击事件 Click listener
- 数据过滤 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
FastAdapter设置为可选择模式后设置点击监听
fastAdapter.withSelectable(true);
fastAdapter.withOnClickListener(new FastAdapter.OnClickListener() {
@Override
public boolean onClick(View v, IAdapter adapter, Mango item, int position) {
// Handle click here
return true;
}
});
2. 数据过滤 Filtering data with Search
如果你使用了SearchView
,FastAdapter提供了接口可以为你过滤查询结果
// Call this in your Search listener
fastAdapter.filter("yourSearchTerm");
fastAdapter.withFilterPredicate(new IItemAdapter.Predicate() {
@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
首先建立一个SimpleDragCallback
的实例,其次用这个实例初始化ItemTouchHelper
,最后把ItemTouchHelper
绑定到RecyclerView
。
SimpleDragCallback dragCallback = new SimpleDragCallback(this);
ItemTouchHelper touchHelper = new ItemTouchHelper(dragCallback);
touchHelper.attachToRecyclerView(recyclerView);
在Activity中实现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
在Activity
中建立一个内部类ActionBarCallBack
,然后让其实现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
方法用来处理其他事件了,比如说点击进入细节展示页面(detail navigation)。所以FastAdapter提供了两个新的接口preClick
和preLongClick
来处理当前的选择点击事件(CAB)。
fastAdapter.withOnPreClickListener(new FastAdapter.OnClickListener() {
@Override
public boolean onClick(View v, IAdapter adapter, Mango item, int position) {
Boolean res = actionModeHelper.onClick(item);
return res != null ? res : false;
}
});
fastAdapter.withOnPreLongClickListener(new FastAdapter.OnLongClickListener() {
@Override
public boolean onLongClick(View v, IAdapter 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
。同时它允许我们处理常规的点击事件。只是注意当你同时使用withOnClickListener()
的时候,也要返回false
。
最后,在values/styles.xml文件中的AppTheme里开启windowActionModeOverlay
选项:
- true
撤销功能(Undo Action)
在CAB模式中删除项目,请结合UndoHelper
一起使用。这个类可以帮助我们实现删除撤销功能。
UndoHelper undoHelper = new UndoHelper(fastAdapter, new UndoHelper.UndoListener() {
@Override
public void commitRemove(Set positions, ArrayList> removed) {
Log.d("remove", "removed: " + removed.size());
}
});
在这个接口的实现方法中我们记录了删除对象的个数。UndoHelper.UndoListener
同时也能帮助我们得到被删除对象在列表中的位置。
还记得我们创建的那个内部类ActionBarCallBack
吗?我们现在来修改下其中的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()
方法,才是最终执行删除对象的地方。删除执行以后会出现一个SnackBar提醒我们删除成功,同时也给我了我们一个便捷的撤销最后一次删除操作的接口。
5. 添加列表头 Adding Header view (multiple ViewHolders)
你可以选择修改之前创建的Mango类,但是基于代码整洁的考虑,我们这里新建立一个类去加载Header view。
public class Header extends AbstractItem {
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 headerAdapter = new HeaderAdapter<>();
回忆一下之前我们是如何实例化FastAdapter的:
FastItemAdapter fastAdapter = new FastItemAdapter<>();
但是现在我们并没有直接指定类型,这样可以使用们的FastAdapter同时保存Mango和Header两种类型。
当然你也可以选择实例化一个泛型FastAdapter(generic type FastAdapter):
FastItemAdapter fastAdapter = new FastItemAdapter<>();
IItem
是你自定义的Model的基类,所以你可以这样初始化Adapter,不过要记得在添加数据的时候进行数据转换。
组装adapter(Setting the adapter)
这里和之前有点不一样,我们使用wrap(FastAdapter)
方法,同时把headerAdapter
和fastAdapter
添加到RecyclerView
里。
recyclerView.setAdapter(headerAdapter.wrap(fastAdapter));
如果你还想添加第三个不同类型的ViewHolder
,同样可以再使用wrap()
方法进行。
recyclerView.setAdapter(thirdAdapter.wrap(headerAdapter.wrap(fastAdapter)));
添加数据 Adding data
我想在顶部和中间各添加一个Header view,而每个Header view都应该有一个不同的数据集。比如说我想添加这样的数据:
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
的前半段数据放在了第一个header后面,后半段数据放在了第二个header后面。
请注意half
这个int
类型的变量是不断增加的,跟其他的List
的position
一样。
使用不同的ViewHolders (Manipulating with different ViewHolders)
现在FastAdapter已经包含了几个不同View和数据,但是我们如何使用它们呢?比如处理点击,还有上面介绍的其他酷炫的功能呢?
简单来说,就是使用 Java的关键字:instanceof
.
下面我将介绍一下如何处理FastAdapter的点击事件:
fastAdapter.withOnClickListener(new FastAdapter.OnClickListener() {
@Override
public boolean onClick(View v, IAdapter 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;
}
});
看到区别了吗?之前我们使用同样的监听点击代码的时候,onClick()
方法传递的参数是Mango类型,但是当我们使用了泛型的FastAdapter时,方法的参数变成了IItem
(基类) 对象。
6. 无限滚动 Infinite (endless) scrolling
这个功能主要使用在社交类型的app中,每次需要加载指定数量的信息,当你到达了信息底部的时候,出现一个加载标志,然后再加载指定数量的信息。
实现这个功能的关键就在RecyclerView
的addOnScrollListener()
方法。但是我们知道,真正困难的地方就在于构建这个监听器,基于此,FastAdapter为我们提供了EndlessRecyclerOnScrollListener()
方法。
我们先写一个FooterAdapter
,我们用这个在列表底部显示加载数据时候的ProgressBar
。
FooterAdapter 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);
}
});
实现无限滚动就是这样简单。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能做到的任何事。我已经开始在我的工程中使用它了,你准备好了吗?在下面的评论中留下你的看法吧。