RecyclerView Adapter in Android, made Fast and Easy

Fastadapter使RecyclerView更加简便高效

翻译自文章 http://blog.grafixartist.com/recyclerview-adapter-android-made-fast-easy/

使用Android RecyclerView最麻烦的莫过于使用其[adapter][]了,如果界面再复杂一些,adapter里面需要包含多个[RecyclerView.ViewHolder][viewholder]就更复杂了,这里面还不包括处理各种点击事件,点击拖动等等其他一些很酷的功能。如果你正为此发愁,那么这篇文章就是为你准备的。当然如果你熟悉[RecyclerView.Adapter][adapter]的标准写法了,但是简单的重复写相同的代码是很浪费时间的,那有没有更好的办法呢?

[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

稳定、高效、简单的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.AdapterRecyclerView.ViewHolder。所以让你的类实现AbstractItem这个接口:

  1. getType() – 返回一个唯一的ID。这里这个ID一定要是唯一的不能重复,推荐的使用方法是在app/res/values/目录下新建一个文件,在其中指定。参考Android文档说明。
  2. getLayoutRes() – 返回你布局文件的id,(R.layout.yours)
  3. 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.

  1. 点击事件 Click listener
  2. 数据过滤 Filtering data with Search
  3. 拖拽功能 Drag and drop
  4. 多项选择 Multi-select with CAB (Contextual Action Bar) and Undo action
  5. 添加列表头 Adding Header view (multiple ViewHolders)
  6. 无限滚动 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提供了两个新的接口preClickpreLongClick来处理当前的选择点击事件(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;
}

UndoHelperremove()方法,才是最终执行删除对象的地方。删除执行以后会出现一个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)方法,同时把headerAdapterfastAdapter添加到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类型的变量是不断增加的,跟其他的Listposition一样。

使用不同的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中,每次需要加载指定数量的信息,当你到达了信息底部的时候,出现一个加载标志,然后再加载指定数量的信息。

实现这个功能的关键就在RecyclerViewaddOnScrollListener()方法。但是我们知道,真正困难的地方就在于构建这个监听器,基于此,FastAdapter为我们提供了EndlessRecyclerOnScrollListener()方法。

我们先写一个FooterAdapter,我们用这个在列表底部显示加载数据时候的ProgressBar

FooterAdapter footerAdapter = new FooterAdapter<>();

ProgressItemFastAdapter 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能做到的任何事。我已经开始在我的工程中使用它了,你准备好了吗?在下面的评论中留下你的看法吧。

你可能感兴趣的:(RecyclerView Adapter in Android, made Fast and Easy)