Android で RecyclerView を使って横スワイプで要素を削除する方法のメモ

# 動作例:

![recyclerview_sample.gif](https://qiita-image-store.s3.amazonaws.com/0/131418/c7262ff6-5d41-b099-e6c1-f8e7942e056d.gif)


- コードは [GitHub](https://github.com/Hachimori/AndroidSamples/tree/master/RecyclerViewSample) に置きました。
- 以下の手順で動作確認可能です。
  1. AndroidStudio 起動
  2. File > New > Project from Version Control > GitHub を選択。 Git Repository URL を https://github.com/Hachimori/AndroidSamples.git に指定してリポジトリをクローンする
  3. File > Open として、RecyclerViewSample のプロジェクトを開く


# 準備
- build.gradle (Module: app) の dependencies に以下を追加

  ```
  compile 'com.android.support:recyclerview-v7:23.4.0'
  ```

# 実装 (レイアウトファイル)
- 以下のような感じで書く

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

# 実装 (Java)
- RecyclerView のビューを取得

  ```java
  mRecyclerView = (RecyclerView) view.findViewById(R.id.recyclerview);
  ```

- RecycleView に、 LinearLayoutManager をセットする (LinearLayoutManager の代わりに GridLayoutManager をセットすると、グリッド形式で表示できる)

  ```java
  mRecyclerView.setLayoutManager(new LinearLayoutManager(getActivity()));
  ```      

- CustomAdapter クラスを実装
  - 実装例:

      ```java
      private class CustomAdapter extends RecyclerView.Adapter<CustomAdapter.ViewHolder> {
          
          private List<Student> mStudentList;
          
          
          class ViewHolder extends RecyclerView.ViewHolder {
              TextView name;
              TextView age;
              TextView gender;
              Button add;
              
              public ViewHolder(View v) {
                  super(v);
                  
                  // 名前
                  name = (TextView) v.findViewById(R.id.name);
                  
                  // 年齢
                  age = (TextView) v.findViewById(R.id.age);
                  
                  // 性別
                  gender = (TextView) v.findViewById(R.id.gender);
                  
                  // "下に追加する" ボタン
                  add = (Button) v.findViewById(R.id.add);
              }
          }
          
          public CustomAdapter(List<Student> studentList) {
              mStudentList = studentList;
          }
           
          @Override
          public ViewHolder onCreateViewHolder(ViewGroup viewGroup, int viewType) {
              
              View v = LayoutInflater
                      .from(viewGroup.getContext())
                      .inflate(R.layout.recyclerview_sample_item, viewGroup, false);
              
              return new ViewHolder(v);
          }
          
          @Override
          public void onBindViewHolder(final ViewHolder viewHolder, int position) {
              
              // 文字列をセット
              viewHolder.name.setText(mStudentList.get(position).getName());
              viewHolder.age.setText(String.valueOf(mStudentList.get(position).getAge()));
              viewHolder.gender.setText(mStudentList.get(position).getGender());
              
              // "下に追加" ボタンが押されたら、下にランダムな要素を追加する
              viewHolder.add.setOnClickListener(new View.OnClickListener() {
                  @Override
                  public void onClick(View v) {
                      
                      // ランダムな要素の作成
                      Random rnd = new Random(); 
                      Student toAdd = new Student("name" + rnd.nextInt(10), 18 + rnd.nextInt(5), rnd.nextBoolean() ? "male" : "female");
                      
                      // 要素の追加
                      addItem(viewHolder.getAdapterPosition() + 1, toAdd);
                  }
              });
          }
          
          @Override
          public int getItemCount() {
              return mStudentList.size();
          }
      
          /**
           * 要素を挿入する
           * 
           * @param position 挿入する位置
           * @param student 挿入する要素
           */
          public void addItem(int position, Student student) {
              
              mStudentList.add(position, student);
              notifyItemInserted(position);
          }
      
          /**
           * 要素を削除する
           * 
           * @param position 削除する位置
           */
          public void remove(int position) {
              
              mStudentList.remove(position);
              notifyItemRemoved(position);
          }
      }
      
      private class Student {
          
          /** 名前 **/ 
          private String mName;
          
          /** 年齢 **/ 
          private int mAge;
          
          /** 性別 **/ 
          private String mGender;
          
          public Student(String name, int age, String gender) {
              mName = name;
              mAge = age;
              mGender = gender;
          }
          
          public String getName() {
              return mName;
          }
          
          public int getAge() {
              return mAge;
          }
          
          public String getGender() {
              return mGender;
          }
      }
      ```
  - 以下、実装のポイント
      - CustomAdapter に ```RecyclerView.Adapter<CustomAdapter.ViewHolder>``` を継承させる
          - CustomAdapter.ViewHolder は、 RecylerView の要素に表示させる View への参照を保持したクラス。CustomAdapter の内部クラスとして定義する
          - ViewHolderパターンの参考: [Android GridViewのパフォーマンスを上げよう(1/2)](http://dev.classmethod.jp/smartphone/gridview-performanceup-1/)
      - onCreateViewHolder() メソッド: LayoutInflater でリスト要素の View を作り、ViewHolder のインスタンスにして返す
      - onBindViewHolder() メソッド: 引数にある ViewHolder に対して、表示する内容の文字列などを設定する
      - getItemCount() メソッド: リストの要素数を返す
      - addItem() メソッド: 指定した箇所に要素を挿入する 
      - remove() メソッド: 指定した箇所の要素を削除する

- RecycleView に CustomAdapter をセットする

  ```java
  CustomAdapter adapter = new CustomAdapter(new ArrayList<Student>() {{
      add(new Student("name1", 20, "male"));
      add(new Student("name2", 22, "female"));
      add(new Student("name3", 18, "female"));
      add(new Student("name4", 21, "male"));
      add(new Student("name5", 21, "male"));
  }});
  mRecyclerView.setAdapter(adapter);
  ```

- ItemTouchHelper.SimpleCallback のメソッドをオーバーライドして、RecyclerView の要素がスワイプされたら削除されるようにする

  ```java
  ItemTouchHelper.SimpleCallback callback = new ItemTouchHelper.SimpleCallback(0, ItemTouchHelper.LEFT | ItemTouchHelper.RIGHT) {
      @Override
      public boolean onMove(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, RecyclerView.ViewHolder target) {
          return false;
      }
      
      @Override
      public void onSwiped(RecyclerView.ViewHolder viewHolder, int direction) {
          
          // 横にスワイプされたら要素を消す
          int swipedPosition = viewHolder.getAdapterPosition();
          CustomAdapter adapter = (CustomAdapter) mRecyclerView.getAdapter();
          adapter.remove(swipedPosition);
      }
  };
  ```

- 実装した ItemTouchHelper.SimpleCallback を RecyclerView にアタッチする

  ```java
  (new ItemTouchHelper(callback)).attachToRecyclerView(mRecyclerView);
  ```

# 参考
- [RecyclerView | Android Developers](https://developer.android.com/reference/android/support/v7/widget/RecyclerView.html)
- [RecyclerView “swipe to delete”. No 3rd party lib necessary.](http://nemanjakovacevic.net/blog/english/2016/01/12/recyclerview-swipe-to-delete-no-3rd-party-lib-necessary/)
- [nemanja-kovacevic/recycler-view-swipe-to-delete](https://github.com/nemanja-kovacevic/recycler-view-swipe-to-delete)
- [jyotman94/android-add-remove-list](https://github.com/jyotman94/android-add-remove-list)