[Android]RecyclerViewのアイテムに条件によって異なるレイアウトを適用する

投稿者:

RecyclerViewを使って自動車メーカー毎に製造している車のモデル名を表示するリストを作ってみる。
完成イメージは次の通り

これを実現するには、メーカー名を表示する行と、モデル名を表示する行に別々のレイアウトを適用しなければならない。

各アイテムのViewはアダプタのonCreateViewHolder()で生成されるが、条件によって異なるレイアウトを適用したいときは引数のviewTypeを見て判断する。viewTypeは特に何もしなければゼロが入っているだけだ。ここの値はアダプタが暗黙的にgetItemViewType()で取得しているため、getItemViewType()をオーバーライドし、条件によってに異なるviewTypeを返せばonCreateViewHolder()でも条件分岐させることができる。

サンプルコード

レイアウト【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_maufacturer_caption.xml】
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/item01"
    android:layout_width="match_parent"
    android:layout_height="wrap_content">

    <TextView
        android:id="@+id/text_manufacturer"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:textSize="20sp"
        android:textStyle="bold"
        android:padding="10dp"
        android:text="MANUFACTURER"
        android:gravity="center"
        android:background="@android:color/holo_orange_dark"/>

</LinearLayout>
レイアウト【listitem_maufacturer_item.xml】
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/item01"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:clickable="true"
    android:focusable="true"
    android:background="?attr/selectableItemBackground">

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

</LinearLayout>

レイアウトファイルの役割は以下の通り

  • activity_main.xml = メインアクティビティのレイアウト
  • listitem_maufacturer_caption.xml = メーカー名のレイアウト
  • listitem_maufacturer_item.xml =モデル名のレイアウト
JAVAファイル【Manufacturer.java】

Manufacturer.javaはメーカー名とモデル名をセットで格納するクラス。
モデル名は配列にして複数の製品を登録できるようにしてある。

/* 自動車メーカーとその製品を登録するクラス */
class Manufacturer {
    //メンバ変数
    private String mManufacturerName;   //メーカー名
    private String[] mCarModels;    //製品名の配列
    private int mCarModelCount; //製品の個数

    //コンストラクタ
    Manufacturer(String name, String[] carModels) {
        this.mManufacturerName = name;
        this.mCarModels = carModels;
        this.mCarModelCount = carModels.length;
    }

    //ゲッター
    String getManufacturerName() {
        return mManufacturerName;
    }

    int getCarModelCount() {
        return mCarModelCount;
    }

    String[] getCarModels() {
        return mCarModels;
    }
}
JAVAファイル【ManufacturerListAdapter.java】

ManufacturerListAdapter.javaはRecyclerViewのアダプタ。
ViewHolderとリスト表示用の定義もインナークラスとして記述してある。

public class ManufacturerListAdapter extends RecyclerView.Adapter {
    //定数
    private final int VIEWTYPE_CAPTION_ITEM = 1;
    private final int VIEWTYPE_NORMAL_ITEM = 0;

    //メンバ変数
    private Manufacturer[] manufacturers;
    private ArrayList<ListData> itemList = new ArrayList<>();

    //コンストラクタ
    ManufacturerListAdapter(Manufacturer[] manufacturers) {
        this.manufacturers = manufacturers;
        init();
    }

    // コンストラクタ引数で渡されたデータを表示用リストに変換
    private void init () {
        for (Manufacturer m : manufacturers) {
            //見出し(メーカー名)
            itemList.add(new ListData(m.getManufacturerName(), VIEWTYPE_CAPTION_ITEM));
            //通常アイテム(モデル名)
            for (int i = 0; i < m.getCarModelCount(); i++) {
                itemList.add(new ListData(m.getCarModels()[i], VIEWTYPE_NORMAL_ITEM));
            }
        }
    }

    @NonNull
    @Override
    public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup viewGroup, int viewType) {
        RecyclerView.ViewHolder vh = null;
        LayoutInflater inflater = LayoutInflater.from(viewGroup.getContext());

        //viewTypeによって返すViewHolderを変える
        if (viewType== VIEWTYPE_CAPTION_ITEM) {
            //メーカー名
            vh = new CaptionViewHolder(inflater.inflate(R.layout.listitem_maufacturer_caption,viewGroup, false));
        } else {
            //モデル名
            vh = new ItemViewHolder(inflater.inflate(R.layout.listitem_maufacturer_item,viewGroup, false));
        }
        return vh;
    }

    @Override
    public void onBindViewHolder(@NonNull RecyclerView.ViewHolder viewHolder, int position) {
        //データの紐づけ
        ListData data = itemList.get(position);
        //viewTypeによってViewHolderの型が異なる
        if (data.viewType == VIEWTYPE_CAPTION_ITEM) {
            ((CaptionViewHolder) viewHolder).caption.setText(data.text);
        } else {
            ((ItemViewHolder) viewHolder).item.setText(data.text);
        }

    }

    @Override
    public int getItemCount() {
        //リストの行数を返す(見出しも含む)
        return itemList.size();
    }

    @Override
    public int getItemViewType(int position) {
        //指定したpositionのviewTypeを返す
        //各行のviewTypeはinit()でitemListに登録したので
        //そこから取り出す
        return itemList.get(position).viewType;
    }

    /* 見出し行のViewHolder */
    class CaptionViewHolder extends RecyclerView.ViewHolder {
        TextView caption;

        CaptionViewHolder(@NonNull View itemView) {
            super(itemView);
            caption = itemView.findViewById(R.id.text_manufacturer);
        }
    }

    /* データ行のViewHolder */
    class ItemViewHolder extends RecyclerView.ViewHolder{
        TextView item;

        ItemViewHolder(@NonNull View itemView) {
            super(itemView);
            item = itemView.findViewById(R.id.text_product);
        }
    }

    /* リスト表示用のインナークラス */
    class ListData {
        String text = "";   //表示するテキスト
        int viewType = 0;   //viewType

        //コンストラクタ
        ListData (String text, int viewType) {
            this.text = text;
            this.viewType = viewType;
        }
    }
}

JAVAファイル【MainActivity.java】
public class MainActivity extends AppCompatActivity {
    //リストに表示するデータ
    Manufacturer[] manufacturers = {
            new Manufacturer("TOYOTA" , new String[]{"CROWN","COROLLA","PRIUS","VITZ"}),
            new Manufacturer("HONDA", new String[]{"LEGEND","CIVIC","FIT","N-WAGON"}),
            new Manufacturer("MAZDA", new String[]{"ATENZA","AXELA","DEMIO"}),
            new Manufacturer("NISSAN", new String[]{"SKYLINE","BLUEBIRD","SUNNY","MARCH"})
    };

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

        RecyclerView recyclerView = findViewById(R.id.recyclerview1);
        //LayoutManagerをセット
        LinearLayoutManager layoutManager = new LinearLayoutManager(this);
        layoutManager.setOrientation(LinearLayoutManager.VERTICAL);
        recyclerView.setLayoutManager(layoutManager);
        //ItemDecorationをセット(区切り線)
        DividerItemDecoration itemDecoration = new DividerItemDecoration(this, layoutManager.getOrientation());
        recyclerView.addItemDecoration(itemDecoration);
        //アダプタをセット
        RecyclerView.Adapter adapter = new ManufacturerListAdapter(manufacturers);
        recyclerView.setAdapter(adapter);
    }
}

Manufacturerクラスの配列をアダプタに渡し、アダプタ内部でテキストとviewTypeをセットにしたListDataクラスのArrayListを作っているのが分かるだろうか。リストはこのArrayListを基に描画されるわけだが、ListDataクラスからviewTypeを取り出し、メーカー名のレイアウトを適用するか、モデル名のレイアウトを適用するかを判断している。

実行結果

上記のコードを実行した結果は下のスクリーンショットのようになる。

このサンプルでは簡単なリストが表示されるだけだが、もちろんここから発展させていくことになる。それはまた別の機会に。

コメントを残す

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

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