woshidan's blog

そんなことよりコードにダイブ。

RecyclerViewで行ごとに表示を切り替える

Recycler View で行ごとに表示に使うレイアウトを切り替える、というのをやりたくて悪戦苦闘していましたので、とりあえず書いたのをメモ。

参考にしたのは、

Tumbling Dice — [Android]ListViewのレイアウトを動的に切り替える際の問題点

techbooster.booth.pm

全体として出来上がったコードはここ

内容

  • RecyclerViewを利用する側の準備
  • ViewHolderで使うレイアウトの準備
  • ViewHolderの準備
    • 切り替えたいレイアウトに対応するViewHolderのサブクラスを用意する
    • onCreateViewHolder()でViewHolderのサブクラスを返すようにする
    • getItemViewType()でdataの値に応じた値を返すようにする
    • onBindViewHolder()でサブクラスのメソッドを使う
  • 実際に動かした画面

RecyclerViewを利用する側の準備

RecyclerView を利用する側の準備。

まず、利用したいActivityRecyclerViewの要素を入れる。

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent"
    android:layout_height="match_parent" android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    android:paddingBottom="@dimen/activity_vertical_margin" tools:context=".MainActivity">
    <android.support.v7.widget.RecyclerView
        xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        android:id="@+id/recycler_view"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        app:layoutManager="android.support.v7.widget.LinearLayoutManager"
        />
</RelativeLayout>

Activityから、RecyclerViewインスタンスを取得。 AdapterにセットするList<T>の取得を行い、LayoutManagerAdapterをセット。

RecyclerView recyclerView = (RecyclerView) findViewById(R.id.recycler_view);

LinearLayoutManager layoutManager = new LinearLayoutManager(this);
recyclerView.setLayoutManager(layoutManager);

List<SampleItem> data = new ArrayList<>();

// 中略
// SampleItemというオブジェクトのcategoryという値によって
// レイアウトを切り替えるので、catagoryの異なる
// オブジェクトを30個くらい生成

recyclerView.setAdapter(new SampleAdapter(this, data));

ViewHolderで使うレイアウトの準備

とりあえず、各行で切り替えて使おうと思っているレイアウトリソースを下記のような形のものを3つ用意。

<?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">

    <TextView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:padding="3dp"
        android:background="#FF0000" <!-- 2 -> #00FF00 3 -> #0000FF -->
        android:text="New Text"
        android:id="@+id/textView1" /> <!-- textViewn -->
</LinearLayout>

切り替えたいレイアウトに対応するViewHolderのサブクラスを用意する

こういった上のレイアウトファイルに対応したリソースへの参照等を持つ ViewHolder のクラスを作成する。 これらのサブクラスに、切り替えたレイアウトのIDやレイアウト中へのリソースへのを参照を持たせて、Bindの際もこのクラスのメソッドを使わせる。

public class Sample1ItemViewHolder extends ViewHolder {
    public static final int LAYOUT_ID = R.layout.sample_1_item_view;
    public final TextView textView;

    public Sample1ItemViewHolder(View itemView) {
        super(itemView);
        textView = (TextView) itemView.findViewById(R.id.textView1);
    }

    public void onBindItemViewHolder(SampleItem data) {
        this.textView.setText(data.getText());
    }
}

もしかしたら、レイアウトのインスタンスも持っていていいかもしれない。 ViewHolderのサブクラスなのがポイント。

onCreateViewHolder()でViewHolderのサブクラスを返すようにする

@Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
    switch (viewType) {
        case 1:
            return new Sample1ItemViewHolder(inflater.inflate(Sample1ItemViewHolder.LAYOUT_ID, parent, false));
        case 2:
            return new Sample2ItemViewHolder(inflater.inflate(Sample2ItemViewHolder.LAYOUT_ID, parent, false));
        case 3:
            return new Sample3ItemViewHolder(inflater.inflate(Sample3ItemViewHolder.LAYOUT_ID, parent, false));
    }
    return null;
}

getItemViewType()でdataの値に応じた値を返すようにする

どちらかというと、特定の値がBlankのとき、切り替える、とかいう場合もあるかもしれない。

  public int getItemViewType(int position) {
      return data.get(position).getCategory();
  }

onBindViewHolder()でサブクラスのメソッドを使う

こうしておくと、切り替えたいレイアウトを増やしたとき、そのレイアウトに関するデータのbindingはサブクラスだけを見ていれば良いので比較的すっきりする気がします。

@Override
public void onBindViewHolder(ViewHolder holder, int position) {
    switch (getItemViewType(position)) {
        case 1:
            ((Sample1ItemViewHolder)holder).onBindItemViewHolder(data.get(position));
            break;
        case 2:
            ((Sample2ItemViewHolder)holder).onBindItemViewHolder(data.get(position));
            break;
        case 3:
            ((Sample3ItemViewHolder)holder).onBindItemViewHolder(data.get(position));
            break;
    }

}

実際に動かした画面

こうです。

f:id:woshidan:20151102011323p:plain