ListViewでFragmentを使用しています。カスタムローダー(インターネットから)で受信したデータによって、このリストビューに関連付けられているArrayAdapterを埋めます。カスタムArrayAdapterは、無限スクロール(ページング)をサポートします。
ユーザーがデバイスを回転し、ListViewでスクロール位置を維持するときに、ArrayAdapterにアイテムを保存する最良の方法は何ですか?
ArrayAdapterを使用して非視覚的なフラグメントを作成し、setRetainInstanceメソッドを使用して値を保存することを考えています。
より良い解決策のための提案はありますか?
AndroidフレームワークとFragmentライフサイクルを使用するには、FragmentにonSaveInstanceState
メソッドを実装する必要があります。簡単にするために、できるストリング値の配列があると仮定しました取得(通常、ArrayAdapterを拡張してビュー構築をカプセル化し、基礎となるデータセット全体にアクセスするための便利なメソッドを提供します):
_public void onSaveInstanceState(Bundle savedState) {
super.onSaveInstanceState(savedState);
// Note: getValues() is a method in your ArrayAdapter subclass
String[] values = mAdapter.getValues();
savedState.putStringArray("myKey", values);
}
_
その後、次のようにonCreateメソッド(またはonCreateViewまたはonActivityCreated- the Fragment JavaDoc を参照)でデータを取得できます。
_public void onCreate (Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (savedInstanceState != null) {
String[] values = savedInstanceState.getStringArray("myKey");
if (values != null) {
mAdapter = new MyAdapter(values);
}
}
...
}
_
これにより、デバイスのローテーションや他のアプリケーションへのユーザー切り替えなど、データを失うことなく、すべてのライフサイクルイベントが適切に処理されます。 onSaveInstanceState
を使用せずメモリを使用する危険性は、Android=メモリを回収する危険性です。保存された状態はこの影響を受けませんが、インスタンス変数または非表示フラグメントを使用するとデータの損失。
savedStateInstance
がnullの場合、復元する状態はありません。
if (values != null)
は、配列が保存されていない可能性を防ぐためだけのものですが、ArrayAdapterをコーディングしてnullデータセットを処理する場合、これは必要ありません。
行が単一のデータ項目ではなく、独自のクラスのインスタンスである場合の最終的な解決策は、そのクラスにParcelableインターフェイスを実装することです。その後、savedState.putParcelableArray("myKey", myArray)
を使用できます。 Parcelableの実装方法を知っておくとどれほど便利か、驚くでしょう。インテント内でクラスを渡すことができ、よりクリーンなコードを書くことができます。
デバイスが回転すると、アプリが再起動されます。そのため、アプリが破棄される前にonSaveInstance
が呼び出されます。配列アダプターをonSaveInstance
に保存し、アプリの再起動時にonCreate
が最後に呼び出されると、配列アダプターを取得してリストビューに設定できます。
アダプタのアイテムをonSaveInstance
に保存するのは非常に簡単です。
ここで、場所を保存する必要があります(スクロール)。あなたはこれを行うことができます
簡単な方法
とにかくscreenOrientationを変更すると混乱するため、わずかなエラーを許容し、onSaveInstance
でlistView.getFirstVisiblePosition()
を使用して最初の表示項目を保存します。
次に、onRestoreInstance
でこのインデックスを取得して、listview.setSelection(position)
を使用して同じアイテムにスクロールできます。もちろん、listview.smoothScrollToPosition(position)
を使用できますが、それは奇妙です。
難しい道
より正確な位置にするには、さらに1レベル下に移動する必要があります。スクロール位置(インデックスではない)を取得し、その位置を保存します。次に、復元後、この位置にスクロールして戻ります。 onSaveInstance
に、listview.getScrollY()
から返された位置を保存します。 onRestoreInstance
でこの位置を取得すると、listview.scrollTo(0, position)
を使用してこの位置にスクロールできます。
なぜそれは本当に難しい方法ですか?
シンプル。リストビューはまだ測定とレイアウトに合格していないため、おそらくこの位置にスクロールバックすることはできません。 getViewObserver().addOnGlobalLayoutListener
を使用してアダプターを設定した後、レイアウトを待つことで、これを克服できます。このコールバックで、runnableをポストしてその位置にスクロールします。
一般に、画面の向きが変更された場合、ユーザーはおそらく指を後ろに動かすため、最初の「簡単な方法」を使用することをお勧めします。 「ギブオアテイク」と同じアイテムを見せればいいだけです。
向きが変わると、アクティビティのライフサイクルはonPause
を呼び出し、次にonRestart
を呼び出します
@Override
protected void onRestart() {
super.onRestart();
mAdapter.getFilter().filter(getFilterSettings());
mAdapter.notifyDataSetChanged();
}