web-dev-qa-db-ja.com

ユーザーが電話を回転させ、ArrayAdapterのすべてのデータを保持するときにリストビューアイテムを保持するための優れたソリューション

ListViewでFragmentを使用しています。カスタムローダー(インターネットから)で受信したデータによって、このリストビューに関連付けられているArrayAdapterを埋めます。カスタムArrayAdapterは、無限スクロール(ページング)をサポートします。

ユーザーがデバイスを回転し、ListViewでスクロール位置を維持するときに、ArrayAdapterにアイテムを保存する最良の方法は何ですか?

ArrayAdapterを使用して非視覚的なフラグメントを作成し、setRetainInstanceメソッドを使用して値を保存することを考えています。

より良い解決策のための提案はありますか?

41
barbarian

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の実装方法を知っておくとどれほど便利か、驚くでしょう。インテント内でクラスを渡すことができ、よりクリーンなコードを書くことができます。

56
Phil

デバイスが回転すると、アプリが再起動されます。そのため、アプリが破棄される前にonSaveInstanceが呼び出されます。配列アダプターをonSaveInstanceに保存し、アプリの再起動時にonCreateが最後に呼び出されると、配列アダプターを取得してリストビューに設定できます。

6
Vikram Gupta

アダプタのアイテムをonSaveInstanceに保存するのは非常に簡単です。

ここで、場所を保存する必要があります(スクロール)。あなたはこれを行うことができます

簡単な方法

とにかくscreenOrientationを変更すると混乱するため、わずかなエラーを許容し、onSaveInstancelistView.getFirstVisiblePosition()を使用して最初の表示項目を保存します。

次に、onRestoreInstanceでこのインデックスを取得して、listview.setSelection(position)を使用して同じアイテムにスクロールできます。もちろん、listview.smoothScrollToPosition(position)を使用できますが、それは奇妙です。


難しい道

より正確な位置にするには、さらに1レベル下に移動する必要があります。スクロール位置(インデックスではない)を取得し、その位置を保存します。次に、復元後、この位置にスクロールして戻ります。 onSaveInstanceに、listview.getScrollY()から返された位置を保存します。 onRestoreInstanceでこの位置を取得すると、listview.scrollTo(0, position)を使用してこの位置にスクロールできます。


なぜそれは本当に難しい方法ですか?

シンプル。リストビューはまだ測定とレイアウトに合格していないため、おそらくこの位置にスクロールバックすることはできません。 getViewObserver().addOnGlobalLayoutListenerを使用してアダプターを設定した後、レイアウトを待つことで、これを克服できます。このコールバックで、runnableをポストしてその位置にスクロールします。


一般に、画面の向きが変更された場合、ユーザーはおそらく指を後ろに動かすため、最初の「簡単な方法」を使用することをお勧めします。 「ギブオアテイク」と同じアイテムを見せればいいだけです。

5
Sherif elKhatib

向きが変わると、アクティビティのライフサイクルはonPauseを呼び出し、次にonRestartを呼び出します

@Override
protected void onRestart() {
    super.onRestart();
    mAdapter.getFilter().filter(getFilterSettings());
    mAdapter.notifyDataSetChanged();
}
1
mjosh