Androidアプリ開発 RecyclerView 追加、更新、削除の実装

2016年11月20日(編集2016年11月20日)
このエントリーをはてなブックマークに追加

Androidアプリで一覧データのUI(ユーザーインターフェース)を作成する場合は、ListViewかRecyclerViewを使います。
この記事は、RecyclerViewでデータ追加、更新、削除の実装方法を記載した記事です。

環境はAndroid 7.1 (API level 25) です。

環境

  • OS X El Capitan
  • android sdk 25
  • Oracle jdk version 1.8.0_72
  • Android Studio 2.2.2

難易度

中級者向け

サンプルコード

Android-RecyclerView-Demo

RecyclerView

RecyclerViewは一覧表示の実装に利用するクラスです。 ListViewを使うより、表現力のある、柔軟なユーザーインターフェースを作成できます。
今回の記事で説明をする登録、更新、削除処理は、一覧表示の実装を理解している必要があります。理解不足の場合、まずはこちらの記事を読んで理解してください。

この記事では、以下のような処理を実装します。

  • メニュー → 登録
  • 列ボタン → 削除
  • 列タップ → 更新
sample app

実装

まずはActivityを実装します。このクラスは、FragmentとDialogFragmentとメニューをコントロールします。

{project_folder}/ui/CustomRecyclerViewActivity.java
public class CustomRecyclerViewActivity extends AppCompatActivity {

    private CustomRecyclerViewFragment customRecyclerViewFragment;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_custom_recycler_view);
        Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
        setSupportActionBar(toolbar);

        FragmentManager fragmentManager = getSupportFragmentManager();
        customRecyclerViewFragment = (CustomRecyclerViewFragment) fragmentManager.findFragmentById(R.id.custom_recycler_view_frag);
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        // Set Menu
        MenuInflater inflater = getMenuInflater();
        inflater.inflate(R.menu.custom_recycler_view, menu);
        return super.onCreateOptionsMenu(menu);
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        switch (item.getItemId()) {
            case R.id.action_insert:
                showDialog();
                break;
        }
        return super.onOptionsItemSelected(item);
    }

    /**
     * Dialog display
     */
    private void showDialog() {
        CustomDialogFragment newFragment = CustomDialogFragment.newInstance();
        newFragment.show(getFragmentManager(), "dialog");
    }
}
        

続いて、Fragmentを実装します。RecyclerViewはFragmentに実装します。

{project_folder}/ui/CustomRecyclerViewFragment.java
public class CustomRecyclerViewFragment extends Fragment {

    private CustomRecyclerViewFragmentAdapter recyclerViewFragmentAdapter;
    private OnFragmentInteractionListener listener;
    private List<Item> list;

    /**
     * Create object with singleton.
     *
     * @return object
     */
    public static CustomRecyclerViewFragment newInstance() {
        CustomRecyclerViewFragment fragment = new CustomRecyclerViewFragment();
        return fragment;
    }

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        super.onCreateView(inflater, container, savedInstanceState);
        View view = inflater.inflate(R.layout.fragment_item_list, container, false);
        list = new ArrayList<Item>();

        // Set the adapter
        if (view instanceof RecyclerView) {
            Context context = view.getContext();
            RecyclerView recyclerView = (RecyclerView) view;
            recyclerView.setLayoutManager(new LinearLayoutManager(context));
            recyclerViewFragmentAdapter = new CustomRecyclerViewFragmentAdapter(list, listener);
            recyclerView.setAdapter(recyclerViewFragmentAdapter);
        }
        return view;
    }

    @Override
    public void onAttach(Activity activity) {
        super.onAttach(activity);
        if (activity instanceof OnFragmentInteractionListener) {
            listener = (OnFragmentInteractionListener) activity;
        } else {
            throw new RuntimeException(activity.toString()
                    + " must implement OnFragmentInteractionListener");
        }
    }

    @Override
    public void onAttach(Context context) {
        super.onAttach(context);
        if (context instanceof OnFragmentInteractionListener) {
            listener = (OnFragmentInteractionListener) context;
        } else {
            throw new RuntimeException(context.toString()
                    + " must implement OnFragmentInteractionListener");
        }
    }

    @Override
    public void onStart() {
        super.onStart();
        // list = getList() ;
    }

    @Override
    public void onDetach() {
        super.onDetach();
        listener = null;
    }

    /**
     * Insert object to RecyclerView
     *
     * @param item
     */
    public void insertToRecyclerView(Item item) {
        if (list != null) {
            int index = list.indexOf(item);
            if (-1 == index) {
                list.add(0, item);
                recyclerViewFragmentAdapter.notifyItemInserted(0);
            }
        }
    }

    /**
     * Update object to RecyclerView
     *
     * @param item
     */
    public void updateToRecyclerView(Item item) {
        if (list != null) {
            int index = list.indexOf(item);
            if (-1 != index) {
                recyclerViewFragmentAdapter.notifyItemChanged(index, item);
            }
        }
    }

    /**
     * Delete object from RecyclerView
     *
     * @param item
     */
    public void deleteFromRecyclerView(Item item) {
        if (list != null) {
            int index = list.indexOf(item);
            if (-1 != index) {
                boolean isDelete = list.remove(item);
                if (isDelete) {
                    recyclerViewFragmentAdapter.notifyItemRemoved(index);
                }
            }
        }
    }

    public interface OnFragmentInteractionListener {
        void onClickItem(Item item);

        void onClickDelete(Item item);
    }
}
        

■ 実装の解説

1. 登録

RecyclerViewで登録処理を実装する場合は、RecyclerView.AdapterクラスのnotifyItemInsertedメソッドを使用します。

public void insertToRecyclerView(Item item) {
    if (list != null) {
        int index = list.indexOf(item);
        // check whether object has or not
        if (-1 == index) {
            list.add(0, item);
            recyclerViewFragmentAdapter.notifyItemInserted(0);
        }
    }
}
        

list.indexOfでオブジェクトがないことを確認します。結果が-1の場合は、リストにオブジェクトがないので、リストにデータを新規で追加します。
このチェックは、IndexOutOfBoundsException対策の実装です。必ず実装してください。端末の差異や動作の遅延によるアプリのクラッシュを防ぐことができるようになります。

notifyItemInsertedの引数はリストのポジションです。上記は0を設定しているので、RecyclerViewへ0の位置データの変更が通知されます。RecyclerViewにheaderを実装している場合はさらに+1する必要があります。また、listへの追加位置は0となります。

上記のコードの動作を確認してみましょう。

insert

データの登録がアニメーションと共に実行されます。

2. 更新

RecyclerViewで更新処理を実装する場合は、RecyclerView.AdapterクラスのnotifyItemChangedメソッドを使用します。

public void updateToRecyclerView(Item item) {
    if (list != null) {
        int index = list.indexOf(item);
        if (-1 != index) {
            recyclerViewFragmentAdapter.notifyItemChanged(index, item);
        }
    }
}
        

list.indexOfでオブジェクトがあることを確認します。結果が-1でない場合は、リストにオブジェクトがあるので、notifyItemChangedでリストの変更をRecyclerViewに通知します。

notifyItemChangedの第1引数はポジションです。第2引数はオブジェクトです。ポジションには、list.indexOfの結果を設定します。 オブジェクトの内容の変更は、ActivityでもFragmentでもDialogでも、どこで実行しても構いません。notifyItemChangedでRecyclerViewに更新の通知をして、初めてリスト表示は変更されます。

上記のコードの動作を確認してみましょう。

update

データの更新が実行されました。

3. 削除

RecyclerViewで削除処理を実装する場合は、RecyclerView.AdapterクラスのnotifyItemRemovedメソッドを使用します。

public void deleteFromRecyclerView(Item item) {
    if (list != null) {
        int index = list.indexOf(item);
        if (-1 != index) {
            boolean isDelete = list.remove(item);
            if (isDelete) {
                recyclerViewFragmentAdapter.notifyItemRemoved(index);
            }
        }
    }
}
        

notifyItemRemovedの第1引数はポジションです。list.indexOfで削除するindexの位置を取得します。list.removeで削除結果を取得し、notifyItemRemovedのポジションに、list.indexOfの結果を設定します。

上記のコードの動作を確認してみましょう。

delete

データの削除が実行されました。

実装のポイント

登録、更新、削除の実装のポイントは、

処理をFragmentに集約する

ことです。

処理をFragmentに集約するのは、クラスの扱う責任の範囲をはっきりさせるためです。AcitivtyやAdapterで処理を呼びだすのはやめましょう。callbackやPub/Subを使ってFragment内で処理を実行しましょう。

また、notifyItemXXXXの処理の前に、list.indexOfメソッドでオブジェクトの存在をチェックをします。
このチェックは、IndexOutOfBoundsExceptionを防ぐための処理です。list.indexOfはオブジェクトの位置を取得できます。この結果で取得したポジションで処理を実行します。クリック時に取得したpositionを使いまわしてはいけません。

結論

ListViewは、notifyUpdate()だけ利用していれば大丈夫でした。RecyClerViewはnotifyDataSetChanged()メソッドだけではダメで、処理ごとに制御が必要となります。
RecyclerViewは覚えることが多いですが、軽量で表現力のあるUIを作成できるので、是非色々なパターンのUIを作成してみてださい。

関連サービス

Validation View Generator

RecyclerView Generator

関連記事

タグ検索で調べてみよう

Android7.0 UI