[Android] Implementation of side-scrolling ListView using RecyclerView

What i made

ListView is familiar on Android, but it cannot be scrolled horizontally. So we'll create a listview-like widget that inherits from RecyclerView and scrolls horizontally.

What is RecyclerView?

A widget that nicely displays multiple views. It's pretty flexible, so you can do most things with it. For details, see [Android] Basic implementation of RecyclerView.

Finished product

HorizontalListView.java



import android.content.Context;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.util.AttributeSet;
import android.view.View;
import android.view.ViewGroup;

import java.util.ArrayList;
import java.util.List;

public class HorizontalListView extends RecyclerView{

    public interface OnItemClickListener{
        void onItemClick(View view, int position);
    }

    public HorizontalListView(Context context){
        super(context);
        initialize(context);
    }

    public HorizontalListView(Context context, AttributeSet set){
        super(context, set);
        initialize(context);
    }

    public HorizontalListView(Context context, AttributeSet set, int defaultAttr){
        super(context, set, defaultAttr);
        initialize(context);
    }

    private void initialize(Context context){
        LinearLayoutManager manager = new LinearLayoutManager(context);
        manager.setOrientation(LinearLayoutManager.HORIZONTAL);
        setLayoutManager(manager);
    }

    private OnItemClickListener mListener;

    public void setOnItemClickListener(OnItemClickListener listener){
        mListener = listener;
        Adapter adapter = getAdapter();
        if ( adapter instanceof ArrayAdapter ){
            ArrayAdapter a = (ArrayAdapter)adapter;
            a.mListener = this.mListener;
        }
    }

    @Override
    public void setAdapter(RecyclerView.Adapter adapter){
        super.setAdapter(adapter);
        if ( adapter instanceof ArrayAdapter ){
            ArrayAdapter a = (ArrayAdapter)adapter;
            a.mListener = this.mListener;
        }
    }

    @Override
    protected void onDetachedFromWindow(){
        super.onDetachedFromWindow();
        setOnItemClickListener(null);
        setAdapter(null);
        setLayoutManager(null);
    }

    private static class SimpleViewHolder extends ViewHolder{
        private SimpleViewHolder(View view){
            super(view);
        }
    }

    public static abstract class ArrayAdapter<E> extends RecyclerView.Adapter<ViewHolder> {

        public ArrayAdapter(List<E> list){
            mDataList = new ArrayList<>(list.size());
            mViews = new ArrayList<>(list.size());
            mDataList.addAll(list);
        }

        private List<E> mDataList;
        private OnItemClickListener mListener;
        private List<View> mViews;

        /**
         *Instantiate a View that is a list element
         * @param parent
         * @return null not acceptable
         */
        public abstract View getView(ViewGroup parent);

        /**
         *Reflect the data displayed in the list in View
         * @param view View of reflection destination
         * @param data Data to be reflected{@link #getItem(int)}But can be obtained
         * @param position Position on the list
         */
        public abstract void onBindView(View view, E data, int position);

        @Override
        public final ViewHolder onCreateViewHolder(ViewGroup parent, int viewType){
            return new SimpleViewHolder(getView(parent));

        }

        @Override
        public final void onBindViewHolder(final ViewHolder holder, int position){

            final View view = holder.itemView;
            E data = getItem(position);
            onBindView(view, data, position);

            mViews.add(holder.itemView);
            holder.itemView.setOnClickListener(new OnClickListener(){
                @Override
                public void onClick(View v){
                    if ( mListener != null ){
                        mListener.onItemClick(view, holder.getAdapterPosition());
                    }
                }
            });
        }

        @Override
        public void onDetachedFromRecyclerView(RecyclerView recyclerView){
            super.onDetachedFromRecyclerView(recyclerView);
            for ( View view : mViews ) view.setOnClickListener(null);
            mViews = null;
            mListener = null;
            mDataList = null;
        }

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

        public E getItem(int position){
            return mDataList.get(position);
        }
    }
}

Implementation description

Realization of side-scrolling

All you have to do is set the LinearLayoutManager with the horizontal orientation to RecyclerView.

private void initialize(Context context){
    LinearLayoutManager manager = new LinearLayoutManager(context);
    manager.setOrientation(LinearLayoutManager.HORIZONTAL);
    setLayoutManager(manager);
}

Preparation of adapter

Like ListView, RecyclerView requires an adapter to manage the data and the View to display. Therefore, we prepared HorizontalListView.ArrayAdapter by inheriting RecyclerView.Adapter. The following two should be implemented at the time of use.

    public abstract View getView(ViewGroup parent);

    public abstract void onBindView(View view, E data, int position);

Inflate the View used for display with getView (ViewGroup), and reflect the contents of data E in View with onBindView (View, E, int). When using ListView, I gave it a similar API so that it can be operated in the same way as customizing the layout with an adapter that inherits android.widget.ArrayAdapter.

Preparing callbacks

Like ListView, I want a callback that notifies me when an element of the list is clicked. Let's make it.

public interface OnItemClickListener{
    void onItemClick(View view, int position);
}

Notifies the clicked View and display position. As an implementation, set OnClickListener to the View that the HorizontalListView.ArrayAdapter created earlier gets and displays with getView (ViewGroup), and relays the received callback. However, note that it will not work unless the adapter set with RecyclerView # setAdapter () is HorizontalListView.ArrayAdapter.

Recommended Posts

[Android] Implementation of side-scrolling ListView using RecyclerView
Easy implementation of Android file browsing
Implementation of validation using regular expressions
Implementation of user authentication function using devise (2)
Implementation of user authentication function using devise (1)
Implementation of tabs using TabLayout and ViewPager
Implementation of user authentication function using devise (3)
[Rails] Implementation of search function using gem's ransack
[Rails 6] Implementation of inquiry function using Action Mailer
Minimal template code for Android development, RecyclerView implementation
[Android] [Java] Manage the state of CheckBox of ListView
[FCM] Implementation of message transmission using FCM + Spring boot
[Rails] Implementation of image enlargement function using lightbox2
Implementation of GKAccessPoint
[Rails] Implementation of batch processing using whenever (gem)
Translator using Microsoft Translator Text API on Android ~ Implementation ~
[Rails] Implementation of PV number ranking using impressionist
[Rails] Implementation of image slide show using Bootstrap 3
Story of test automation using Appium [Android / java]
Implementation of Google Sign-In using Google OAuth 2.0 authentication (server edition)
[Android] Display of input candidates using List Popup Window
<Android> Change the background color of the List row of ListView
[Rails] Implementation of multi-layer category function using ancestry "Preparation"
[Rails] Implementation of multi-layer category function using ancestry "seed"
Implementation of flash messages
Implementation of search function
Example of using vue.config.js
Applied implementation of chat-space
Summary of using FragmentArgs
Implementation of pagination function
Summary of using DBFlow
Database implementation using Realm
Definition of Android constants
[Rails] Implementation of multi-layer category function using ancestry "Editing form"
How to easily implement in-app purchase using itemstore <Implementation: Android>
[Rails] Implementation of automatic address input using jpostal and jp_prefecture
[Rails] Implementation of multi-layer category function using ancestry "Creation form"
[Rails] Implementation of tagging function using intermediate table (without Gem)