One RecyclerView to Rule Them All

one-recyclerview-to-rule-them-all-0

When RecyclerView was introduced, we were amazed with all of the new possibilities. We could finally start using ViewHolders by defaults, simply switch between list or grid by using a different LayoutManager, define our own custom click listeners, and a lot more.

What was missing were some basic ListView features – empty view support for example, and this began to frustrate me. When I started working on a project where headers and footers with RecyclerView had to be used I knew it was the right time to do something regarding the problem.

I decided to write my own wrapper around RecyclerView and RecyclerAdapter. The wrapper needed to be as simple as possible and offer a limited set of functionalities, I envisioned it to mimic ListView and ArrayAdapter behaviors. I didn’t want to overcomplicate. We don’t want to save the world – we just want to speed up our development process.

title

That’s how MjolnirRecyclerView was born – and after a few tweaks and bug fixes, it’s finally ready to take its place in the hand of the lawful ruler of Asgard. It’s open sourced and available on Github.

Usage

Add the library as a dependency to your build.gradle

	compile ’co.infinum:mjolnirrecyclerview:version@aar’

Define an adapter, which extends MjolnirRecyclerAdapter:

	public class SimpleAdapter extends MjolnirRecyclerAdapter<String> {

    private List<String> items;

    public SimpleAdapter(Context context, List<String> items) {
        super(context, items);
    }

    @Override
    protected MjolnirViewHolder<String> onCreateItemViewHolder(ViewGroup parent, int viewType) {
        View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.list_item_adapter, parent, false);
        return new TestViewHolder(view);
    }

    public class TestViewHolder extends MjolnirViewHolder<String> {

        @BindView(R.id.tv_position)
        TextView tvPosition;

        @BindView(R.id.tv_text)
        TextView tvText;

        @BindView(R.id.root_view)
        View rootView;

        public TestViewHolder(View itemView) {
            super(itemView);
            ButterKnife.bind(this, itemView);
        }

        @Override
        protected void bind(final String item, final int position, List<Object> payloads) {
            tvPosition.setText(String.valueOf(position).concat("."));
            tvText.setText(item);
        }
    }
}

Include MjolnirRecyclerView in XML layout:

	<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
        xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:layout_height="match_parent">

    <co.infinum.mjolnirrecyclerview.MjolnirRecyclerView
            android:id="@+id/recycler_view"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
    />

    <include
            layout="@layout/empty_view"/>
</RelativeLayout>

In your activity, set MjolnirRecyclerAdapter to MjolnirRecyclerView and populate it with data.

	public class SimpleActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_simple);
        ButterKnife.bind(this);

        recyclerView.setLayoutManager(new LinearLayoutManager(this));
        recyclerView.setEmptyView(emptyView);

        adapter = new SimpleAdapter(this);
        recyclerView.setAdapter(adapter);

        adapter.addHeader(R.layout.view_header, false);
        adapter.addFooter(R.layout.view_footer, false);
        adapter.addAll(ITEMS);
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        adapter.cancel();
    }
}

Features

Header and footer support

MjolnirRecyclerAdapter treats header and footer view as two different ViewHolders inside RecyclerAdapter, which are added to the MjolnirRecyclerView. At the moment, it supports only one header and footer.

	   adapter.addHeader(View view)
   adapter.addHeader(int headerViewId)
   adapter.addFooter(View view)
   adapter.addFooter(int footerViewId)

Mjolnir offers full header and footer support for LinearLayoutManager (both vertical and horizontal), and GridLayoutManager.

Empty view support

Add an empty view to the RecyclerView and it will be automatically shown when items are set to the adapter.

	   recyclerView.setEmptyView(View view)

You can also show empty view while the adapter is not set to the MjolnirRecyclerView, which is handy if you want to initialize adapter at some later point in the time.

	* Sets the empty view to the RecyclerView. {@param showIfAdapterNotSet} determines should we show the empty    
 * view if adapter is still not set to the RecyclerView. Default value is set to false.
 *
 * @param emptyView           view which is used as RecyclerView’s empty view.
 * @param showIfAdapterNotSet determines should we show empty view if adapter is still not set to the RecyclerView.
 */
public void setEmptyView(View emptyView, boolean showIfAdapterNotSet){...}

DiffUtil support

DiffUtils is an awesome utility class introduced with Android Support library version 24.2.0. It can easily be used to calculate updates for RecyclerView adapter.

Simply add DiffUtil.Callback in adapter’s update method:

	adapter.update(new ItemDiffUtilResult())

As DiffUtil is a blocking sync action, it’s executed on the background thread inside the MjolnirRecyclerAdapter by using a AsyncTask. As a result of this approach, you need to call cancel() method on your adapter when your activity or fragment is about to be destroyed so that the adapter is not updated if the screen has been destroyed.

	
   @Override
   protected void onDestroy() {
       super.onDestroy();
       adapter.cancel();
   }

ArrayAdapter like methods

Although RecyclerAdapter can be treated as an upgrade to regular ListAdapter, it’s missing some neat utility methods like add(), addAll(), reset(), remove(), set()… MjolnirRecyclerAdapter has full support for those methods.

Next page listener

MjolnirRecyclerAdapter defines OnNextPageListener interface listener, which is called when user scrolls to the end of the adapter data set. You can also define a minimum number of items to have below your current scroll position before onScrolledToNext() page callback is called.

What’s next?

There are some features we plan to add in future releases, like support for a custom number of header and footer views, loading views…

If you would like to get involved in developing new Mjolnir’s features, head forward to its official Github repository and make a pull request with a short description of your changes. Feedback and code contributions are very much welcome.