Android開発、RecyclerView実装のための最小限テンプレートコード

# RecyclerViewの実装方法についてまとめてみた

ネットでRecyclerViewを検索して出てくるサンプルコードをビルドしても全然成功しませんでした。
そんなに残っていないのなら自分が残してやろうということでRecycerViewについてまとめようと思いました。
(それに仕事で使わないといけないからね)

## RecyclerViewって何？

Android開発では昔、ListViewまでの実装をやったことがありました。
ですが、最近のAndroidではListViewはあまり使わないらしくRecyclerViewを使うことが多いらしいです。
ListViewでの実装はそこまで大変ではなかったのですが、ネットで検索してもまともなRecyclerViewのサンプルコードがありませんでしたので自分でテンプレを作ることにしました。
RecyclerViewはListViewの上位版みたいな感じでわたし的にはiOSのUICollectionView的なイメージでしかありません。
多量のリストデータを表示するのに適しているそうで、画面部品を再利用する形で効率よくスクロールできるように作られています。



## テンプレ用ソースコードについて

```RecyclerView.java
public class ScrollListActivity extends AppCompatActivity {

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

        android.support.v7.widget.Toolbar toolbar = findViewById(R.id.toolbar);
        toolbar.setLogo(R.mipmap.ic_launcher);
        setSupportActionBar(toolbar);
        CollapsingToolbarLayout toolbarLayout = findViewById(R.id.toolbarLayout);
        toolbarLayout.setTitle(getString(R.string.toolbar_title));
        toolbarLayout.setExpandedTitleColor(Color.WHITE);
        toolbarLayout.setCollapsedTitleTextColor(Color.LTGRAY);

        RecyclerView lvMenu = findViewById(R.id.lvMenu);
        LinearLayoutManager layout = new LinearLayoutManager(ScrollListActivity.this);
        lvMenu.setLayoutManager(layout);
        List<Map<String,Object>> menuList = createTeishokuList();
        RecyclerListAdapter adapter = new RecyclerListAdapter(menuList);
        lvMenu.setAdapter(adapter);
    }

    private List<Map<String, Object>> createTeishokuList() {
        List<Map<String, Object>> menuList = new ArrayList<>();

        Map<String, Object> menu = new HashMap<>();
        menu.put("name", "から揚げ定食");
        menu.put("price", 800);
        menu.put("desc", "若鳥のから揚げにサラダ、ご飯とお味噌汁が付きます。");
        menuList.add(menu);

        menu = new HashMap<>();
        menu.put("name", "ハンバーグ定食");
        menu.put("price", 850);
        menu.put("desc", "手ごねハンバーグにサラダ、ご飯とお味噌汁が付きます。");
        menuList.add(menu);

        //以下データ登録の繰り返し。
        menu = new HashMap<>();
        menu.put("name", "生姜焼き定食");
        menu.put("price", 850);
        menu.put("desc", "すりおろし生姜を使った生姜焼きにサラダ、ご飯とお味噌汁が付きます。");
        menuList.add(menu);

        menu = new HashMap<>();
        menu.put("name", "ステーキ定食");
        menu.put("price", 1000);
        menu.put("desc", "国産牛のステーキにサラダ、ご飯とお味噌汁が付きます。");
        menuList.add(menu);

        menu = new HashMap<>();
        menu.put("name", "野菜炒め定食");
        menu.put("price", 750);
        menu.put("desc", "季節の野菜炒めにサラダ、ご飯とお味噌汁が付きます。");
        menuList.add(menu);

        menu = new HashMap<>();
        menu.put("name", "とんかつ定食");
        menu.put("price", 900);
        menu.put("desc", "ロースとんかつにサラダ、ご飯とお味噌汁が付きます。");
        menuList.add(menu);

        menu = new HashMap<>();
        menu.put("name", "ミンチかつ定食");
        menu.put("price", 850);
        menu.put("desc", "手ごねミンチカツにサラダ、ご飯とお味噌汁が付きます。");
        menuList.add(menu);

        menu = new HashMap<>();
        menu.put("name", "チキンカツ定食");
        menu.put("price", 900);
        menu.put("desc", "ボリュームたっぷりチキンカツにサラダ、ご飯とお味噌汁が付きます。");
        menuList.add(menu);

        menu = new HashMap<>();
        menu.put("name", "コロッケ定食");
        menu.put("price", 850);
        menu.put("desc", "北海道ポテトコロッケにサラダ、ご飯とお味噌汁が付きます。");
        menuList.add(menu);

        menu = new HashMap<>();
        menu.put("name", "焼き魚定食");
        menu.put("price", 850);
        menu.put("desc", "鰆の塩焼きにサラダ、ご飯とお味噌汁が付きます。");
        menuList.add(menu);

        menu = new HashMap<>();
        menu.put("name", "焼肉定食");
        menu.put("price", 950);
        menu.put("desc", "特性たれの焼肉にサラダ、ご飯とお味噌汁が付きます。");
        menuList.add(menu);

        return menuList;
    }

    private class RecyclerListViewHolder extends RecyclerView.ViewHolder {
        public TextView _tvMenuName;
        public TextView _tvMenuPrice;
        public RecyclerListViewHolder(View itemView) {
            super(itemView);
            _tvMenuName = itemView.findViewById(R.id.tvMenuName);
            _tvMenuPrice = itemView.findViewById(R.id.tvMenuPrice);
        }
    }

    private class RecyclerListAdapter extends RecyclerView.Adapter<RecyclerListViewHolder> {
        private List<Map<String, Object>> _listData;
        public RecyclerListAdapter(List<Map<String, Object>> listData) {
            _listData = listData;
        }

        // ビューホルダーオブジェクトを生成するメソッド
        @Override
        public RecyclerListViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
            LayoutInflater inflater = LayoutInflater.from(ScrollListActivity.this);
            View view = inflater.inflate(R.layout.row, parent, false);
            RecyclerListViewHolder holder = new RecyclerListViewHolder(view);
            return holder;
        }

        // ビューホルダー内の各画面部品に表示するデータを割り当てるメソッド
        @Override
        public void onBindViewHolder(RecyclerListViewHolder holder, int position) {
            Map<String, Object> item = _listData.get(position);
            String menuName = (String)item.get("name");
            int menuPrice = (Integer)item.get("price");
            String menuPriceStr = String.valueOf(menuPrice);
            holder._tvMenuName.setText(menuName);
            holder._tvMenuPrice.setText(menuPriceStr);
        }

        // データの件数を返すメソッド
        @Override
        public int getItemCount() {
            return _listData.size();
        }
    }
}
```

```activity_scroll_list.xml
<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.CoordinatorLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <android.support.design.widget.AppBarLayout
        android:id="@+id/appbar"
        android:layout_width="match_parent"
        android:layout_height="180dp"
        android:elevation="10dp">

        <android.support.design.widget.CollapsingToolbarLayout
            android:id="@+id/toolbarLayout"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            app:layout_scrollFlags="scroll|exitUntilCollapsed">

            <android.support.v7.widget.Toolbar
                android:id="@+id/toolbar"
                android:layout_width="match_parent"
                android:layout_height="?attr/actionBarSize"
                app:layout_collapseMode="pin"
                android:background="@color/colorPrimary"/>
        </android.support.design.widget.CollapsingToolbarLayout>
    </android.support.design.widget.AppBarLayout>

   <android.support.v7.widget.RecyclerView
       android:id="@+id/lvMenu"
       android:scrollbars="vertical"
       android:layout_width="match_parent"
       android:layout_height="match_parent"
       app:layout_behavior="@string/appbar_scrolling_view_behavior"/>

    <android.support.design.widget.FloatingActionButton
        android:id="@+id/fabEmail"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_margin="20dp"
        app:layout_anchor="@id/appbar"
        app:layout_anchorGravity="bottom|end"
        app:srcCompat="@android:drawable/ic_dialog_email"/>
</android.support.design.widget.CoordinatorLayout>

```

```row.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:orientation="vertical">

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

	<LinearLayout
		android:layout_width="match_parent"
		android:layout_height="match_parent"
		android:layout_marginBottom="10dp"
		android:layout_marginLeft="10dp"
		android:orientation="horizontal">

		<TextView
			android:id="@+id/tvMenuPrice"
			android:layout_width="wrap_content"
			android:layout_height="wrap_content"
			android:textSize="14sp"/>

		<TextView
			android:layout_width="wrap_content"
			android:layout_height="wrap_content"
			android:text="@string/tv_menu_unit"
			android:textSize="14sp"/>

	</LinearLayout>

</LinearLayout>

```

## RecyclerViewを使う上で重要なクラス・メソッドについて

```RecyclerView.java
private class RecyclerListViewHolder extends RecyclerView.ViewHolder {}
```

RecyclerView.ViewHolder クラスを継承したViewHolderクラスを作成する必要があります。
iOS的なイメージでは`UITableViewCell`のようなViewクラスの生成でしょうか。
各アイテムの画面部品を保持しているオブジェクトでビューホルダーと呼ぶのだそうです。


```RecyclerView.java
private class RecyclerListAdapter extends RecyclerView.Adapter<RecyclerListViewHolder> {}
```

こちらがRecyclerViewには独自のAdapterが存在していないため自分でカスタムして実装する必要があるのだそうです。recyclerView.Adapterは抽象クラスのため、次の３つのメソッドを実装している必要があるそうです。

- onCreateViewHolder()
- onBindViewHolder()
- getItemCount()

onCreateViewHolder: ビューホルダオブジェクトを生成するメソッド  
onBindViewHolder: ビューホルダ内の各画面部品に表示データを入れるメソッド  
getItemCount: データの件数を返すメソッド  



## スクリーンショットについて

![Screenshot_1530530217.png](https://qiita-image-store.s3.amazonaws.com/0/44392/81eaf6e6-8168-8297-551f-846d0be90a52.png)

![Screenshot_1530530220.png](https://qiita-image-store.s3.amazonaws.com/0/44392/a5f57327-3deb-66ee-055d-f2aabb074ee9.png)

## UITableViewCellのような区切り線の実装について

スクリーンショットを見てみるとRecyclerviewの場合はUITableViewCellのような区切り線はデフォルトでは実装されないそうです。
区切り線を入れるためには別に`DividerItemDecoration`クラスが別に必要です。

実装の仕方はActivityの`protected void onCreate(Bundle savedInstanceState) `メソッドの一番下に下記の2行を追加するだけです。

```RecyclerView.java
       // 区切り線用のオブジェクトを生成する
        DividerItemDecoration decoration = new DividerItemDecoration(ScrollListActivity.this, layout.getOrientation());
        lvMenu.addItemDecoration(decoration);
```
これで区切り線が表示されるようになります。

## クリックイベントについて

さらにRecyclerViewを実際にタップして何かアクションを処理する場合には

```RecyclerView.java
private class ItemClickListener implements View.OnClickListener
```

を実装するだけです。
このプライベートクラスメソッドに処理を書いて、次のところにセットしましょう。

```RecyclerView.java
        // ビューホルダーオブジェクトを生成するメソッド
        @Override
        public RecyclerListViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
            LayoutInflater inflater = LayoutInflater.from(ScrollListActivity.this);
            View view = inflater.inflate(R.layout.row, parent, false);
            view.setOnClickListener(new ItemClickListener()); // <- ココに追加
            RecyclerListViewHolder holder = new RecyclerListViewHolder(view);
            return holder;
        }
```

これでクリックイベントが反応するようになります。


## 参考図書

[基礎＆応用力をしっかり育成！ Androidアプリ開発の教科書 なんちゃって開発者にならないための実践ハンズオン](https://www.amazon.co.jp/dp/B078X8H61T/ref=dp-kindle-redirect?_encoding=UTF8&btkr=1)