Why you should stop using ListView

If you’ve been an Android developer for any length of time, you’re probably familiar with ListView. It’s a ViewGroup that displays a scrollable vertical list of views. It’s simple enough. You’ve probably also heard of RecyclerView, ListView’s newer, cooler cousin which might be good, but you probably don’t need to use it. Right?

I’ll get straight to the point: there is no real reason to continue using ListViews. This is especially true if you’re working on a new UI or app from scratch, because RecyclerViews are so much better. You could potentially save hours of time in the future by making this one choice – I know because I learned this the hard way.

But RecyclerView is too complex!

I understand why you might feel that way. RecyclerView is certainly more powerful and more flexible than ListView. It allows for broader functionality and actually performs better without additional boilerplate code (I’ll get to that in a bit). But it isn’t really any more complex. If you just want a vertical list, RecyclerView can certainly give you one. Consider the following code for a ListView adapter that displays a title and a description for each item:

public class MyListViewAdapter extends BaseAdapter {

    private Context mContext;
    private List<Pair<String, String>> mItems;

    public MyListViewAdapter(Context context, List<Pair<String, String>> items) {
        mContext = context;
        mItems = items;
    }

    @Override
    public int getCount() {
        return mItems.size();
    }

    @Override
    public Pair<String, String> getItem(int position) {
        return mItems.get(position);
    }

    @Override
    public long getItemId(int position) {
        return position;
    }

    @Override
    public View getView(int position, View convertView, ViewGroup container) {
        MyViewHolder holder;

        if (convertView == null) {
            LayoutInflater inflater = (LayoutInflater)
                    mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
            convertView = inflater.inflate(R.layout.item_list, container, false);
            holder = new MyViewHolder();
            holder.titleTextView = convertView.findViewById(R.id.text_title);
            holder.descriptionTextView = convertView.findViewById(R.id.text_description);
        } else {
            holder = (MyViewHolder) convertView.getTag();
        }

        holder.titleTextView.setText(getItem(position).first);
        holder.descriptionTextView.setText(getItem(position).second);

        return convertView;
    }

    private static class MyViewHolder {
        private TextView titleTextView;
        private TextView descriptionTextView;
    }
}

Here’s code that does the same thing using a RecyclerView adapter:

public class MyRecyclerViewAdapter
        extends RecyclerView.Adapter<MyRecyclerViewAdapter.MyViewHolder> {
    private Context mContext;
    private List<Pair<String, String>> mItems;

    public MyRecyclerViewAdapter(Context context, List<Pair<String, String>> items) {
        mContext = context;
        mItems = items;
    }

    @Override
    public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        return new MyViewHolder(LayoutInflater.from(mContext)
                .inflate(R.layout.item_list, parent, false));
    }

    @Override
    public void onBindViewHolder(MyViewHolder holder, int position) {
        holder.titleTextView.setText(getItem(position).first);
        holder.descriptionTextView.setText(getItem(position).second);
    }

    @Override
    public int getItemCount() {
        return mItems.size();
    }

    public Pair<String, String> getItem(int position) {
        return mItems.get(position);
    }

    static class MyViewHolder extends RecyclerView.ViewHolder {
        private TextView titleTextView;
        private TextView descriptionTextView;

        public MyViewHolder(View itemView) {
            super(itemView);
            titleTextView = itemView.findViewById(R.id.text_title);
            descriptionTextView = itemView.findViewById(R.id.text_description);
        }
    }
}

As you can see, RecyclerView is using less code than ListView in this example. You also probably noticed that the ListView example above is using a common ListView pattern called the “ViewHolder pattern”. It is basically a way of making your ListView render views more efficiently by reusing previously rendered views using a class called the ViewHolder. This makes sure that your views aren’t rendered from scratch every time the user scrolls to them.

Performance may not seem like a priority in this example, so using ViewHolder could seem unnecessary, but consider what would happen if your adapter fetched some data over the internet, let’s say an image, and rendered it. Every time your user would scroll up or down a view that had already been viewed, it would fetch the data and render it again. Not only is this a terrible waste of the user’s mobile network (which is, or should be, assumed to be limited like most things in mobile devices), but it also makes for bad UX in general and goes against user expectations.

My point is, as a rule of thumb, if you’re using a ListView, chances are you are using a ViewHolder to recycle views and if you’re not, you probably should. Even if your adapter does not need internet access, rendering views, especially complex ones, can be an expensive task.

This is where RecyclerView shines. If you look at the RecyclerView example above, you can probably tell that the RecyclerView uses the ViewHolder pattern as well, but it does so automagically. You don’t need to check any views for null, all you need to do is create a ViewHolder class that extends RecyclerView.ViewHolder, declare and assign references to views in the ViewHolder (which makes more sense than the ListView approach), return a ViewHolder from the onCreateViewHolder method and modify view for each item as needed in the onBindViewHolder method. RecyclerView handles the recycling for you (hence the name). It’s a much cleaner, nicer approach compared with what you normally do with a ListView.

You can use RecyclerView for other things too

RecyclerView is more general-purpose than ListView. This means that a RecyclerView can do everything a ListView can do and more, but not vice versa. For example, a ListView can only be used for vertical lists, but RecyclerView supports horizontal lists as well. You can even create a grid using GridLayoutManager with RecyclerView. And in all cases, the recycling of views is handled automatically for you.

Automatic recycling = less headache

If you’re planning on making a complex list with input fields for each view with changing data depending on user interaction, you’d be better off using RecyclerView. With increasing code complexity, the code that would handle recycling in a ListView also gets more complex. Having one less thing to worry about can be save you hours of time and allow you to focus on what your main task is. I know this because I’ve experienced the pain of trying to fix a problem in ListView that just vanished when I switched over to RecyclerView. I don’t know what exactly the problem was because I never needed to figure it out, but I know it had to do with the way I was recycling views in my ListView manually.

So even if you’re already using ListView and are comfortable with it, you might still want to consider switching to RecyclerView if the code is likely to undergo any development in the future.

Conclusion

RecyclerView > ListView

RecyclerView offers broader functionality and less boilerplate than ListView, and it’s not more complex. In fact, the reduced boilerplate makes it arguably less complex.

I’m actually surprised ListView still hasn’t been deprecated, but I suspect it will be eventually.

Leave a Comment

This site uses Akismet to reduce spam. Learn how your comment data is processed.