529

SOで同様の質問の例をたくさん見つけましたが、残念ながら私の要件を満たす答えはありません。

ポートレートとランドスケープのレイアウトが異なり、バックスタックを使用しています。これにより、setRetainState()構成変更ルーチンを使用できなくなり、トリックを使用できなくなります。

TextViewsでユーザーに特定の情報を表示しますが、デフォルトのハンドラーには保存されません。アクティビティのみを使用してアプリケーションを作成する場合、次のことがうまく機能しました。

TextView vstup;

@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.whatever);
    vstup = (TextView)findViewById(R.id.whatever);
    /* (...) */
}

@Override
public void onSaveInstanceState(Bundle state) {
    super.onSaveInstanceState(state);
    state.putCharSequence(App.VSTUP, vstup.getText());
}

@Override
public void onRestoreInstanceState(Bundle state) {
    super.onRestoreInstanceState(state);
    vstup.setText(state.getCharSequence(App.VSTUP));
}

sを使用Fragmentすると、これは非常に特定の状況でのみ機能します。具体的には、ひどく壊れているのは、フラグメントを置き換え、それをバックスタックに配置し、新しいフラグメントが表示されている間に画面を回転させることです。私が理解したところによると、古いフラグメントはonSaveInstanceState()置き換えられたときにへの呼び出しを受け取りませんが、何らかの形でにリンクされたままでActivityあり、このメソッドは後でView存在しなくなったときに呼び出されるため、私TextViewの結果のいずれかを検索するとになりNullPointerExceptionます。

また、 'sで問題がなかったとしても、sで自分への参照を保持するのTextViewsは良い考えではないことがわかりました。その場合、実際には状態が保存されますが、フラグメントが非表示になっているときに画面を2回回転すると、新しいインスタンスでは呼び出されないため、問題が再発します。FragmentActivityonSaveInstanceState()onCreateView()

状態onDestroyView()をいくつかのBundle型クラスのメンバー要素(実際には1つだけではなく、より多くのデータTextView)に保存して保存することを考えましonSaveInstanceState()たが、他にも欠点があります。主に、フラグメント現在表示されている場合、2つの関数を呼び出す順序が逆になるため、2つの異なる状況を考慮する必要があります。よりクリーンで正しい解決策が必要です!

4

6 に答える 6

91

これは非常に古い答えです。

私はもう Android 向けに書いていないので、最近のバージョンでの機能は保証されておらず、アップデートもありません。

これは私が現時点で使用している方法です...非常に複雑ですが、少なくとも考えられるすべての状況を処理します。興味のある方はどうぞ。

public final class MyFragment extends Fragment {
    private TextView vstup;
    private Bundle savedState = null;

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        View v = inflater.inflate(R.layout.whatever, null);
        vstup = (TextView)v.findViewById(R.id.whatever);

        /* (...) */

        /* If the Fragment was destroyed inbetween (screen rotation), we need to recover the savedState first */
        /* However, if it was not, it stays in the instance from the last onDestroyView() and we don't want to overwrite it */
        if(savedInstanceState != null && savedState == null) {
            savedState = savedInstanceState.getBundle(App.STAV);
        }
        if(savedState != null) {
            vstup.setText(savedState.getCharSequence(App.VSTUP));
        }
        savedState = null;

        return v;
    }

    @Override
    public void onDestroyView() {
        super.onDestroyView();
        savedState = saveState(); /* vstup defined here for sure */
        vstup = null;
    }

    private Bundle saveState() { /* called either from onDestroyView() or onSaveInstanceState() */
        Bundle state = new Bundle();
        state.putCharSequence(App.VSTUP, vstup.getText());
        return state;
    }

    @Override
    public void onSaveInstanceState(Bundle outState) {
        super.onSaveInstanceState(outState);
        /* If onDestroyView() is called first, we can use the previously savedState but we can't call saveState() anymore */
        /* If onSaveInstanceState() is called first, we don't have savedState, so we need to call saveState() */
        /* => (?:) operator inevitable! */
        outState.putBundle(App.STAV, (savedState != null) ? savedState : saveState());
    }

    /* (...) */

}

またはView、変数のパッシブ s に表示されたデータを保持Viewし、それらを表示するためだけに s を使用して、2 つのものを同期させることが常に可能です。ただし、最後の部分はあまりきれいではないと思います。

于 2013-03-09T18:43:51.960 に答える
5

DroidTのおかげで、私はこれを作りました:

Fragment が onCreateView() を実行しない場合、そのビューはインスタンス化されません。したがって、バックスタックのフラグメントがそのビューを作成しなかった場合は、最後に保存された状態を保存します。それ以外の場合は、保存/復元したいデータで独自のバンドルを構築します。

1) このクラスを拡張します。

import android.os.Bundle;
import android.support.v4.app.Fragment;

public abstract class StatefulFragment extends Fragment {

    private Bundle savedState;
    private boolean saved;
    private static final String _FRAGMENT_STATE = "FRAGMENT_STATE";

    @Override
    public void onSaveInstanceState(Bundle state) {
        if (getView() == null) {
            state.putBundle(_FRAGMENT_STATE, savedState);
        } else {
            Bundle bundle = saved ? savedState : getStateToSave();

            state.putBundle(_FRAGMENT_STATE, bundle);
        }

        saved = false;

        super.onSaveInstanceState(state);
    }

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

        if (state != null) {
            savedState = state.getBundle(_FRAGMENT_STATE);
        }
    }

    @Override
    public void onDestroyView() {
        savedState = getStateToSave();
        saved = true;

        super.onDestroyView();
    }

    protected Bundle getSavedState() {
        return savedState;
    }

    protected abstract boolean hasSavedState();

    protected abstract Bundle getStateToSave();

}

2)フラグメントには、これが必要です:

@Override
protected boolean hasSavedState() {
    Bundle state = getSavedState();

    if (state == null) {
        return false;
    }

    //restore your data here

    return true;
}

3) たとえば、onActivityCreated で hasSavedState を呼び出すことができます。

@Override
public void onActivityCreated(Bundle state) {
    super.onActivityCreated(state);

    if (hasSavedState()) {
        return;
    }

    //your code here
}
于 2015-08-27T22:26:14.230 に答える
-8
final FragmentTransaction ft = getFragmentManager().beginTransaction();
ft.hide(currentFragment);
ft.add(R.id.content_frame, newFragment.newInstance(context), "Profile");
ft.addToBackStack(null);
ft.commit();
于 2016-12-21T06:45:31.127 に答える