RecyclerViewによるリスト表示

2018-12-15 22:34:14

RecyclerViewは複数のデータをリスト表示する場合に利用する

似たような機能を持ったクラスとしてListViewがあるが、全ての要素に対してレイアウトが生成されるため効率が良くないのだ
RecyclerViewでは表示するのに必要な最低限のレイアウトが作られ、スクロール時にはすでに表示した例愛とを再利用する

ListViewでも同様なのだが、アイテムごとのレイアウトを管理するためにAdapterクラスを作らなければならない

ソースコード

2018-12-16 10:35:18

https://github.com/android-samples00/Sample_RecyclerView

完成図

2018-12-16 09:44:11

RecyclerViewの配置

2018-12-16 09:35:10

今回はEmptyActivityで作っていくのを前提とする

まずはactivity_main.xmlにRecyclerViewを配置する

activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

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


初期状態でRecyclerViewのモジュールがロードされるようになっていないので、レイアウトエディタのRecyclerViewの後ろに表示されているダウンロードっぽいアイコンをクリックしておく必要がある。するとbuild.gradleに自動的に必要なモジュールが足される

ルートレイアウトはFrameLayoutに変更している
RecyclerViewはIDをrecyclerViewとしている

アイテム用レイアウトの作成

2018-12-16 09:48:00

item_layout.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"
    android:background="#333333">

    <TextView
        android:id="@+id/item_no"
        android:layout_width="80dp"
        android:layout_height="wrap_content"
        android:layout_margin="1dp"
        android:background="#ffffff"
        android:text="番号" />

    <TextView
        android:id="@+id/item_label"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_margin="1dp"
        android:layout_weight="1"
        android:background="#ffffff"
        android:text="ラベル" />
</LinearLayout>


番号とテキストを表示させるためのレイアウトを作っている
これをAdapterから呼び出して、表示内容の設定を行う

Itemデータ用クラスとAdapterの作成

2018-12-16 09:40:30

今回は解説用なので全てをMainActivity上に記述する

表示するデータを事前に作成するため、ItemDataというデータ用クラスを作る

class ItemData{
    public int number;
    public String label;
}

class ItemAdapter extends RecyclerView.Adapter {
    interface OnItemClickListener {
        void onItemClick(ItemData item);
    }

    //表示用データの保存用
    List<ItemData> mItemList;
    public void setItemList(List<ItemData> itemList){
        mItemList = itemList;
    }

    @NonNull
    @Override
    public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup viewGroup, int i) {
        //レイアウトを読み出す
        View view = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.item_layout, viewGroup, false);

        //一般的なやり方だと、下の部分でアイテムレイアウト内の項目のインスタンスを取り出しておくことになるが、
        //後から取り出しても大して遅くはならないので、あえて処理しない
        return new RecyclerView.ViewHolder(view){};
    }

    @Override
    public void onBindViewHolder(@NonNull RecyclerView.ViewHolder viewHolder, int i) {
        //番号に対応したアイテムデータを取り出す
        ItemData item = mItemList.get(i);
        viewHolder.itemView.setTag(item);

        //レイアウトにデータを出力
        TextView no = viewHolder.itemView.findViewById(R.id.item_no);
        TextView label = viewHolder.itemView.findViewById(R.id.item_label);
        no.setText(String.valueOf(item.number));
        label.setText(item.label);

    }

    @Override
    public int getItemCount() {
        return mItemList!=null?mItemList.size():0;
    }
}





MainActivityでのRecyclerView初期化処理

2018-12-16 09:50:08

MainActivityは以下のような内容になる
これでリスト表示は完成となる

public class MainActivity extends AppCompatActivity {

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

        //サンプル用データ(ひたすら足し算)を作成
        ArrayList<ItemData> itemList = new ArrayList();
        for(int i=1,a=0;i<=100;i++) {
            ItemData item = new ItemData();
            item.number = i;
            item.label = String.format("%d+%d=%d",a,i,a+=i);
            itemList.add(item);
        }

        //アダプターの作成
        ItemAdapter adapter = new ItemAdapter();
        adapter.setItemList(itemList);

        //RecyclerViewの処理
        RecyclerView recyclerView = findViewById(R.id.recyclerView);
        //縦方向にアイテムを表示
        recyclerView.setLayoutManager(new LinearLayoutManager(this));
        //RecyclerViewにアダプターを設定
        recyclerView.setAdapter(adapter);
    }
}



タップ処理の追加

2018-12-16 10:01:41

各アイテムのタップ処理は自分で管理する必要がある

まずはコールバック用のインタフェイスをAdapterクラスに用意する
そしてタップイベントを受け取ったら、それをMainActivity側にルーティングする

class ItemAdapter extends RecyclerView.Adapter implements View.OnClickListener {
    interface OnItemClickListener {
        void onItemClick(ItemData item);
    }

    //表示用データの保存用
    List<ItemData> mItemList;
    public void setItemList(List<ItemData> itemList){
        mItemList = itemList;
    }

    //アイテムがタップされた時のイベント処理用
    OnItemClickListener mItemClickListener;
    public void setOnItemClickListener(OnItemClickListener listener){
        mItemClickListener = listener;
    }

    @NonNull
    @Override
    public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup viewGroup, int i) {
        //レイアウトを読み出す
        View view = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.item_layout, viewGroup, false);
        view.setOnClickListener(this);  //アイテムがタップされたイベントを拾う

        //一般的なやり方だと、下の部分でアイテムレイアウト内の項目のインスタンスを取り出しておくことになるが、
        //後から取り出しても大して遅くはならないので、あえて処理しない
        return new RecyclerView.ViewHolder(view){};
    }

    @Override
    public void onBindViewHolder(@NonNull RecyclerView.ViewHolder viewHolder, int i) {
        //番号に対応したアイテムデータを取り出す
        ItemData item = mItemList.get(i);
        viewHolder.itemView.setTag(item);

        //レイアウトにデータを出力
        TextView no = viewHolder.itemView.findViewById(R.id.item_no);
        TextView label = viewHolder.itemView.findViewById(R.id.item_label);
        no.setText(String.valueOf(item.number));
        label.setText(item.label);

    }

    @Override
    public int getItemCount() {
        return mItemList!=null?mItemList.size():0;
    }

    @Override
    public void onClick(View view) {
        ItemData item = (ItemData) view.getTag();
        if(mItemClickListener != null)
            mItemClickListener.onItemClick(item);

    }
}


MainActivityのonCreateの中のItemAdapterを作成している部分を以下のように書き換え
        //アダプターの作成
        ItemAdapter adapter = new ItemAdapter();
        adapter.setItemList(itemList);
        adapter.setOnItemClickListener(new ItemAdapter.OnItemClickListener() {
            @Override
            public void onItemClick(ItemData item) {
                Toast.makeText(MainActivity.this,
                        String.format("%d番の%sが押されました",item.number,item.label),
                        Toast.LENGTH_SHORT).show();
            }
        });


これでアイテムがタップされるとメッセージが表示されるようになる

データの更新と再表示

2018-12-16 10:08:21

データの内容を変更し、再表示が必要な場合は以下のようにAdapterクラスに変更を通知する

adapter.notifyDataSetChanged();