Android：ListViewの画面回転と復元

## はじめに
ListViewを単純に実装した場合、画面回転しても、表示していたアイテムは保持されません。
そのため画面回転するたびに、アイテムを取得し直す必要があったり、スクロール位置が最上部に戻ってしまったり、といった問題が発生します。

この記事では、画面回転しても、スクロール位置はそのままに、アイテムを取得し直すこともなく、ListViewを復元する方法を記載します。

## 後述のソースコードについて
ListViewを使ってユーザ情報（UserListAdapterItem）を表示します。
今回の記事に関係ない以下の内容についてはソースコードに記載しておりません。

- UserListAdapterItemの中身
- ユーザ情報の一覧を表示するためのカスタムアダプター（UserListAdapter）のgetViewメソッド
- ListViewのアイテムのレイアウトXML
- UserListAdapterItemのリストの新規生成

## ソースコード
```ListViewActivity.java
/**
 * ListViewを表示しているActivity
 */
public class ListViewActivity extends AppCompatActivity {

    private ListView mUserListView;
    private UserListAdapter mUserListAdapter;

    /**
     * ListViewのアイテムをBundleに保存するためのキー
     */
    private static final String BUNDLE_KEY_ITEM_LIST = "item_list";

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

        // ListViewとそのAdapterの初期化
        mUserListView = (ListView) findViewById(R.id.list_user);
        mUserListAdapter = new UserListAdapter(this, new ArrayList<UserListAdapterItem>());
        mUserListView.setAdapter(mUserListAdapter);
    }

    @Override
    protected void onRestoreInstanceState(Bundle savedInstanceState) {
        super.onRestoreInstanceState(savedInstanceState);

        // ListViewのアイテムを復元する
        if (savedInstanceState != null && !savedInstanceState.isEmpty()) {
            List<UserListAdapterItem> itemList = (List) savedInstanceState.getSerializable(BUNDLE_KEY_ITEM_LIST);
            mUserListAdapter.setItemList(itemList);
        }
    }

    @Override
    protected void onPostCreate(@Nullable Bundle savedInstanceState) {
        super.onPostCreate(savedInstanceState);

        // Adapterが空だった場合はListViewに表示するアイテムを新規に取得する
        if (mUserListAdapter.isEmpty()) {
            List<UserListAdapterItem> itemList = createUserListAdapterItemList();
            mUserListAdapter.setItemList(itemList);
        }
    }

    @Override
    protected void onSaveInstanceState(Bundle outState) {
        // 復元用にListViewのアイテムを保存する
        outState.putSerializable(BUNDLE_KEY_ITEM_LIST, mUserListAdapter.getItemList());
        super.onSaveInstanceState(outState);
    }
    
    /**
     * アイテムを生成する処理
     *
     * @return
     */
    private List<UserListAdapterItem> createUserListAdapterItemList() {
        // 記事に関係ないため省略
    }
}
```

```activity_list_view.xml
<?xml version="1.0" encoding="utf-8"?>

<!-- ListViewActivityのレイアウト -->

<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context=".view.activity.ListViewActivity">

    <ListView
        android:id="@+id/list_user"
        android:layout_width="match_parent"
        android:layout_height="match_parent"/>

</LinearLayout>
```

```UserListAdapter.java
/**
 * ListViewでユーザ情報（UserListAdapterItem）を表示するためのカスタムアダプター
 */
public class UserListAdapter extends ArrayAdapter<UserListAdapterItem> {

    private LayoutInflater mInflater;

    public UserListAdapter(Context context, List<UserListAdapterItem> objects) {
        super(context, 0, objects);

        mInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
    }

    @NonNull
    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        // 記事に関係ないため省略
    }

    /**
     * Adapter内のアイテムをすべて取得する。
     * 返却する型がArrayListなのは、Serializableを継承しているため。
     *
     * @return
     */
    public ArrayList<UserListAdapterItem> getItemList() {
        ArrayList<UserListAdapterItem> itemList = new ArrayList<>();
        for (int i = 0, size = getCount(); i < size; i++) {
            UserListAdapterItem item = getItem(i);
            itemList.add(item);
        }
        return itemList;
    }

    /**
     * Adapterにアイテムのリストを設定する。
     *
     * @param itemList
     */
    public void setItemList(List<UserListAdapterItem> itemList) {
        clear();
        addAll(itemList);
    }

}
```

## おわりに
上記のように実装いただければ、画面回転した時に、回転前に表示していたListViewの内容を復元することができます。
しかしこちらの方法では、Android 7 で重大な不具合が発生する可能性があります。
そのため、その解決策を [別の記事](http://qiita.com/h_yama37/items/20ee47f39a5e004d0b32) で紹介したいと思います。