[Android]RecyclerViewを使ってListViewを作る

投稿者:

RecyclerViewはより柔軟なリストを可能にしたオブジェクトだ。ListViewに似ているが、アイテムを縦に並べるだけじゃなくて、タイル状に並べたり、横に並べることもできる。ドラッグ&ドラッグで移動させたり、スワイプで削除するなど、Playストアで配信されているアプリの多くが採用している動作を実現できる。機能が多く、より柔軟なUIを作成できる反面、単純にリストを表示したい場合でも手続きが多く、コードの量も多くなる傾向にあるように思う。

この記事では取っ掛かりとしてRecyclerViewでListViewとほぼ同等のUIを実現していく。筆者自身RecyclerViewを自在に使いこなせているわけではないので、誤りや理解の及んでいない部分があるかもしれない。その辺のところはご了承ください。

RecycleViewの構成要素

ListViewでは、UI担当の「ListView」とデータとレイアウト担当の「Adapter」をセットにして操作するが、RecycleViewではそれよりも要素が増えている。
基本的なものは以下のようになっている

class説明
RecyclerViewRecyclerViewの本体。UI担当。
RecyclerView.Adapterデータとレイアウトを紐付けするクラス。レイアウトの生成にViewHolderを使うところ以外ListViewのArrayAdapter等とほぼ同じ役割。
RecyclerView.ViewHolderアイテムのViewを保持する
RecyclerView.ItemDecorationアイテムに枠や罫線等の装飾を施す
RecyclerView.LayoutMangerアイテムの配置を管理。スクロール時のViewの移動も担当

RecyclerView.ViewHolderの補足

ViewHolderはアイテムのViewを保持して、アイテムが再作成されたときにfindViewById()を呼び出さないで済む仕組み。findViewById()は負荷が高いので、これを回避出来れば高パフォーマンスに繋がる。

RecyclerView.ItemDecorationの補足

ListViewではアイテムとアイテムの間に自動的に区切り線が表示されていたが、RecyclerViewではそれがなく、線を引きたければRecyclerView.ItemDecorationで描画しなければならない。ネット上の古い記事ではRecyclerView.ItemDecorationを継承した自作クラスを載せているところもあるが、現在は区切り線を引くDividerItemDecorationクラスが用意されている。

RecyclerViewを使うための準備

build.gradleにライブラリを追加しておく。

dependencies {
     ~
     implementation 'com.android.support:recyclerview-v7:28.0.0'
     ~
 }

上の例のライブラリバージョンはこの記事を書いている時点のものなので注意。常に最新のバージョンを使おう。

コードサンプル

レイアウト【activity_main.xml】
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <android.support.v7.widget.RecyclerView
        android:id="@+id/recyclerview1"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />

</LinearLayout>
レイアウト【listitem_recyclerview1.xml】
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content">

    <TextView
        android:id="@+id/text01"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:textSize="18sp"
        android:padding="10dp"/>

</LinearLayout>

レイアウトファイルの意味合いは以下の通り

  • 【activity_main.xml】= アクティビティのレイアウト
  • 【listitem_recyclerview1.xml】 = RecyclerViewに表示するアイテムのレイアウト
JAVA【MainActivity.java】
public class MainActivity extends AppCompatActivity {
    //リストに表示するデータ
    private final static String[] listDatas = {
            "ONE","TWO","THREE","FOUR",
            "FIVE","SIX","SEVEN","EIGHT",
            "NINE","TEN","ELEVEN","TWELVE",
            "THIRTEEN","FOURTEEN","FIFTEEN","SIXTEEN",
            "SEVENTEEN","EIGHTEEN","NINETEEN","TWENTY"    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        final RecyclerView recyclerView = findViewById(R.id.recyclerview1);
        //LayoutManagerを設定
        //縦方向のリストはLinearLayoutManagerを使う
        LinearLayoutManager layoutManager = new LinearLayoutManager(this);
        layoutManager.setOrientation(LinearLayoutManager.VERTICAL); //縦方向に設定
        recyclerView.setLayoutManager(layoutManager);
        //ItemDecorationを設定
        //DividerItemDecorationはリストに罫線を引くためのItemDecoration
        DividerItemDecoration itemDecoration = new DividerItemDecoration(this, layoutManager.getOrientation());
        recyclerView.addItemDecoration(itemDecoration);
        //アダプタのセット
        SampleRecyclerAdapter adapter = new SampleRecyclerAdapter(listDatas);
        recyclerView.setAdapter(adapter);
    }

    /* -------------------------------------------------------
    * RecyclerViewのアダプタ
    * --------------------------------------------------------*/
    class SampleRecyclerAdapter extends RecyclerView.Adapter<SampleRecyclerAdapter.SampleViewHolder> {
        //メンバ変数
        String[] itemDatas; //リストのデータを保持する変数

        //コンストラクタ
        SampleRecyclerAdapter(String[] itemDatas) {
            //データをコンストラクタで受け取りメンバ変数に格納
            this.itemDatas = itemDatas;
        }

        @NonNull
        @Override
        public SampleViewHolder onCreateViewHolder(@NonNull ViewGroup viewGroup, int viewType) {
            //onCreateViewHolder()ではリスト一行分のレイアウトViewを生成する
            //作成したViewはViewHolderに格納してViewHolderをreturnで返す

            //レイアウトXMLからViewを生成
            View view = LayoutInflater.from(viewGroup.getContext())
                    .inflate(R.layout.listitem_recyclerview1, viewGroup, false);
            //ViewHolderを生成してreturn
            SampleViewHolder holder = new SampleViewHolder(view);
            return holder;
        }

        @Override
        public void onBindViewHolder(@NonNull SampleViewHolder sampleViewHolder, int position) {
            //onBindViewHolder()ではデータとレイアウトの紐づけを行なう
            sampleViewHolder.text01.setText(itemDatas[position]);
        }

        @Override
        public int getItemCount() {
            //データ個数を返す
            return itemDatas.length;
        }

        /* ViewHolder(インナークラス) */
        class SampleViewHolder extends RecyclerView.ViewHolder {
            TextView text01;

            SampleViewHolder(@NonNull View itemView) {
                super(itemView);
                text01 = itemView.findViewById(R.id.text01);
            }
        }
    }
}

RecyclerView.AdapterはMainActivityのインナークラスとして実装している。RecyclerView.Adapterの必須メソッドは。onCreateViewHolder()、onBindViewHolder()、getItemCount()の三つ。

onCreateViewHolder()は引数のviewType毎に、初めてアイテムを描画するときに一回だけ呼ばれる。ここでViewを生成し、それをViewHolderに格納して返す。

onBindViewHolder()はデータとレイアウトの紐づけを行なう。例ではTextViewに文字列を渡している。

getItemCount()はデータ個数を返す。大抵はListや配列の長さを返せばOK。

例ではRecyclerView.ViewHolderもインナークラスとして実装している。TextViewを外から参照可能な変数に格納してアクセスしやすいようにしているが、セッターやゲッターを使っても良いと思う。

実行結果

アプリを実行すると次のような画面が表示される。

RecyclerViewによるリスト

この段階ではリストをスクロールできるものの、各項目をタップしても何の反応も無い。

ユーザのアクションに対する応答をどう実装していくかは別の記事にしていくつもりだ。

1件のコメント

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

このサイトはスパムを低減するために Akismet を使っています。コメントデータの処理方法の詳細はこちらをご覧ください