RecyclerView Adapter in Android

made Fast and Easy字数2326 阅读79 评论0 喜欢1Fastadapter使RecyclerView更加简便高效翻译自文章 http://blog.grafixartist.com/recyclerview-adapter-android-made-fast-easy/使用Android RecyclerView最麻烦的莫过于使用其adapter了,如果界面再复杂一些,adapter里面需要包含多个RecyclerView.ViewHolder就更复杂了,这里面还不包括处理各种点击事件,点击拖动等等其他一些很酷的功能。如果你正为此发愁,那么这篇文章就是为你准备的。当然如果你熟悉RecyclerView.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 classescompile 'com.mikepenz:materialize:1.0.0@aar'最新的配置还请看FastAdapter首页说明。建立模型类(Creating the Model class)用作者最喜欢的芒果为例,写个相关apppublic 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)FastItemAdapterfastAdapter = new FastItemAdapter<>();recyclerView.setAdapter(fastAdapter);// Fetch your data hereListmangoes = 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) scrolling1. 点击事件 Click listenerFastAdapter设置为可选择模式后设置点击监听fastAdapter.withSelectable(true);fastAdapter.withOnClickListener(new FastAdapter.OnClickListener() {            @Override            public boolean onClick(View v, IAdapteradapter, Mango item, int position) {              // Handle click here                return true;            }        });2. 数据过滤 Filtering data with Search如果你使用了SearchView,FastAdapter提供了接口可以为你过滤查询结果// Call this in your Search listenerfastAdapter.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, IAdapteradapter, 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, IAdapteradapter, 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(Setpositions, ArrayList> removed) {                Log.d("remove", "removed: " + removed.size());            }        });在这个接口的实现方法中我们记录了删除对象的个数。UndoHelper.UndoListener同时也能帮助我们得到被删除对象在列表中的位置。还记得我们创建的那个内部类ActionBarCallBack吗?我们现在来修改下其中的onActionItemClicked()方法:@Overridepublic 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的:FastItemAdapterfastAdapter = new FastItemAdapter<>();但是现在我们并没有直接指定类型,这样可以使用们的FastAdapter同时保存Mango和Header两种类型。当然你也可以选择实例化一个泛型FastAdapter(generic type FastAdapter):FastItemAdapterfastAdapter = 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, IAdapteradapter, 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。FooterAdapterfooterAdapter = 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能做到的任何事。我已经开始在我的工程中使用它了,你准备好了吗?在下面的评论中留下你的看法吧。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,736评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,167评论 1 291
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,442评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,902评论 0 204
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,302评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,573评论 1 216
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,847评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,562评论 0 197
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,260评论 1 241
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,531评论 2 245
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,021评论 1 258
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,367评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,016评论 3 235
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,068评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,827评论 0 194
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,610评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,514评论 2 269

推荐阅读更多精彩内容