Androidでは、フラグメント(たとえばFragA
)がバックスタックに追加され、別のフラグメント(たとえばFragB
)が一番上に来ます。さて、打ち返すFragA
とトップに来てonCreateView()
呼ばれます。今、私はFragA
そのFragB
上に押される前に特定の状態にありました。
私の質問は、どうすればFragA
以前の状態に復元できますか?状態を保存する方法はありますか(Bundleのように)、その場合はどのメソッドをオーバーライドする必要がありますか?
Androidでは、フラグメント(たとえばFragA
)がバックスタックに追加され、別のフラグメント(たとえばFragB
)が一番上に来ます。さて、打ち返すFragA
とトップに来てonCreateView()
呼ばれます。今、私はFragA
そのFragB
上に押される前に特定の状態にありました。
私の質問は、どうすればFragA
以前の状態に復元できますか?状態を保存する方法はありますか(Bundleのように)、その場合はどのメソッドをオーバーライドする必要がありますか?
回答:
ではフラグメントガイド FragmentList例を探すことが出来ます:
@Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putInt("curChoice", mCurCheckPosition);
}
これは後でこのように使用できます:
@Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
if (savedInstanceState != null) {
// Restore last state for checked position.
mCurCheckPosition = savedInstanceState.getInt("curChoice", 0);
}
}
私はFragmentsの初心者ですが、問題の解決策のようです;)OnActivityCreatedは、フラグメントがバックスタックから戻った後に呼び出されます。
onSaveInstanceState
対応するアクティビティもシャットダウンしているときにのみ呼び出されます。
フラグメントonSaveInstanceState(Bundle outState)
は、フラグメントのアクティビティがそれ自体およびアタッチされたフラグメントで呼び出すまで呼び出されません。したがって、このメソッドは、何か(通常は回転)によってアクティビティが強制SaveInstanceState
され、後で復元されるまで呼び出されません。ただし、アクティビティが1つだけあり、その中に大量のフラグメントセットがある場合(を頻繁に使用replace
)、アプリケーションが1つの方向でのみ実行onSaveInstanceState(Bundle outState)
される場合、アクティビティが長時間呼び出されない場合があります。
考えられる回避策は3つあります。
最初:
フラグメントの引数を使用して重要なデータを保持します。
public class FragmentA extends Fragment {
private static final String PERSISTENT_VARIABLE_BUNDLE_KEY = "persistentVariable";
private EditText persistentVariableEdit;
public FragmentA() {
setArguments(new Bundle());
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_a, null);
persistentVariableEdit = (EditText) view.findViewById(R.id.editText);
TextView proofTextView = (TextView) view.findViewById(R.id.textView);
Bundle mySavedInstanceState = getArguments();
String persistentVariable = mySavedInstanceState.getString(PERSISTENT_VARIABLE_BUNDLE_KEY);
proofTextView.setText(persistentVariable);
view.findViewById(R.id.btnPushFragmentB).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
getFragmentManager()
.beginTransaction()
.replace(R.id.frameLayout, new FragmentB())
.addToBackStack(null)
.commit();
}
});
return view;
}
@Override
public void onPause() {
super.onPause();
String persistentVariable = persistentVariableEdit.getText().toString();
getArguments().putString(PERSISTENT_VARIABLE_BUNDLE_KEY, persistentVariable);
}
}
2番目ですが、それほど面倒ではない方法-変数をシングルトンで保持します
3番目- replace()
断片化しないでくださいadd()
/ show()
/ / hide()
それらの代わりに。
Fragment.onSaveInstanceState()
呼び出されたことがないときの最良のソリューション。リストビュー内のアイテムまたはそれらのIDのみを含む独自のデータを引数に保存するだけです(他の集中データマネージャーがある場合)。リストビューの位置を保存する必要はありません。保存され、自動的に復元されます。
String persistentVariable = mySavedInstanceState.getString(PERSISTENT_VARIABLE_BUNDLE_KEY);
は常にnull
です。どうしたの?
getArguments()
、ViewPager内のネストされたフラグメントを含め、確実に進むべき道です。私は1つのアクティビティを使用し、多くのフラグメントをイン/アウトでスワップします。これは完全に機能します。次に、提案されたソリューションを確認するための簡単なテストを示します。1)フラグメントAからフラグメントBに移動します。2)デバイスの向きを2回変更します。3)デバイスの戻るボタンを押します。
setArguments(new Bundle());
古いバンドルを上書きします。したがって、フラグメントを1回だけ作成し、毎回新しいフラグメントを作成する代わりに、このインスタンスを使用してください。
ViewPagerを使用してFragmentsを操作する場合、それは非常に簡単です。このメソッドを呼び出すだけですsetOffscreenPageLimit()
。
ドキュメントに合わせる:
保持する必要があるページの数を、アイドル状態のビュー階層の現在のページのいずれかの側に設定します。この制限を超えるページは、必要に応じてアダプターから再作成されます。
ビューを一度だけ膨らませてください。
例は次のとおりです。
public class AFragment extends Fragment {
private View mRootView;
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
if(mRootView==null){
mRootView = inflater.inflate(R.id.fragment_a, container, false);
//......
}
return mRootView;
}
}
Fragment
がそのルートビューを参照すると、それは行われません。一部のGCルート要素による参照は、グローバル静的プロパティと同様に、非UIスレッド変数になります。Fragment
インスタンスはGC-ルートではないので、ゴミを収集することができます。ルートビューも同様です。
私はこれとよく似た問題に取り組みました。以前のフラグメントに頻繁に戻ることがわかっていたので、フラグメント.isAdded()
が本当かどうかを確認しました。本当の場合は、を実行するのではなく、transaction.replace()
を実行しましたtransaction.show()
。これにより、フラグメントがすでにスタック上にある場合にフラグメントが再作成されるのを防ぎます。
Fragment target = <my fragment>;
FragmentTransaction transaction = getFragmentManager().beginTransaction();
transaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN);
if(target.isAdded()) {
transaction.show(target);
} else {
transaction.addToBackStack(button_id + "stack_item");
transaction.replace(R.id.page_fragment, target);
}
transaction.commit();
覚えておくべきもう1つのことは、これによりフラグメント自体の自然な順序は維持されますが、方向(config)の変更時に破棄および再作成されるアクティビティ自体を処理する必要がある場合があることです。ノードのAndroidManifest.xmlでこれを回避するには:
android:configChanges="orientation|screenSize"
Android 3.0以降でscreenSize
は、明らかに必要です。
幸運を
android:configChanges=everythinYouCanThinkOf|moreThingsYouFoundOnTheInternets
マニフェストを追加しないでください。代わりに、たとえば、ここから、speakerdeck.com
私が見つけた最良の解決策は以下の通りです:
onSavedInstanceState():アクティビティがシャットダウンされるときに常にフラグメント内で呼び出されます(アクティビティを別のアクティビティに移動するか、構成を変更します)。したがって、同じアクティビティで複数のフラグメントを呼び出す場合は、次のアプローチを使用する必要があります。
フラグメントのOnDestroyView()を使用して、オブジェクト全体をそのメソッド内に保存します。次にOnActivityCreated():オブジェクトがnullかどうかを確認します(このメソッドが毎回呼び出されるため)。ここでオブジェクトの状態を復元します。
常に動作します!
リストビューを含むフラグメントにハイブリッドアプローチを使用しました。現在のフラグメントを置き換えるのではなく、新しいフラグメントを追加して現在のフラグメントを非表示にするため、パフォーマンスが向上しているようです。私のフラグメントをホストするアクティビティには次のメソッドがあります:
public void addFragment(Fragment currentFragment, Fragment targetFragment, String tag) {
FragmentManager fragmentManager = getSupportFragmentManager();
FragmentTransaction transaction = fragmentManager.beginTransaction();
transaction.setCustomAnimations(0,0,0,0);
transaction.hide(currentFragment);
// use a fragment tag, so that later on we can find the currently displayed fragment
transaction.add(R.id.frame_layout, targetFragment, tag)
.addToBackStack(tag)
.commit();
}
リストアイテムがクリック/タップされるたびに(リストビューを含む)このメソッドをフラグメントで使用します(したがって、詳細フラグメントを起動/表示する必要があります)。
FragmentManager fragmentManager = getActivity().getSupportFragmentManager();
SearchFragment currentFragment = (SearchFragment) fragmentManager.findFragmentByTag(getFragmentTags()[0]);
DetailsFragment detailsFragment = DetailsFragment.newInstance("some object containing some details");
((MainActivity) getActivity()).addFragment(currentFragment, detailsFragment, "Details");
getFragmentTags()
新しいフラグメントを追加すると、さまざまなフラグメントのタグとして使用する文字列の配列を返します(のtransaction.add
メソッドを参照)addFragment
上記方法を)。
リストビューを含むフラグメントでは、これをonPause()メソッドで行います。
@Override
public void onPause() {
// keep the list view's state in memory ("save" it)
// before adding a new fragment or replacing current fragment with a new one
ListView lv = (ListView) getActivity().findViewById(R.id.listView);
mListViewState = lv.onSaveInstanceState();
super.onPause();
}
次に、フラグメントのonCreateView(実際にはonCreateViewで呼び出されるメソッド)で、状態を復元します。
// Restore previous state (including selected item index and scroll position)
if(mListViewState != null) {
Log.d(TAG, "Restoring the listview's state.");
lv.onRestoreInstanceState(mListViewState);
}
アクティビティのさまざまなフラグメントのフィールドの値を保持する簡単な方法
フラグメントのインスタンスを作成し、置換と削除の代わりに追加します
FragA fa= new FragA();
FragB fb= new FragB();
FragC fc= new FragB();
fragmentManager = getSupportFragmentManager();
fragmentTransaction = fragmentManager.beginTransaction();
fragmentTransaction.add(R.id.fragmnt_container, fa);
fragmentTransaction.add(R.id.fragmnt_container, fb);
fragmentTransaction.add(R.id.fragmnt_container, fc);
fragmentTransaction.show(fa);
fragmentTransaction.hide(fb);
fragmentTransaction.hide(fc);
fragmentTransaction.commit();
次に、フラグメントを再度追加および削除する代わりに、フラグメントを表示および非表示にします
fragmentTransaction = fragmentManager.beginTransaction();
fragmentTransaction.hide(fa);
fragmentTransaction.show(fb);
fragmentTransaction.hide(fc);
fragmentTransaction.commit()
;
private ViewPager viewPager;
viewPager = (ViewPager) findViewById(R.id.pager);
mAdapter = new TabsPagerAdapter(getSupportFragmentManager());
viewPager.setAdapter(mAdapter);
viewPager.setOnPageChangeListener(new ViewPager.OnPageChangeListener() {
@Override
public void onPageSelected(int position) {
// on changing the page
// make respected tab selected
actionBar.setSelectedNavigationItem(position);
}
@Override
public void onPageScrolled(int arg0, float arg1, int arg2) {
}
@Override
public void onPageScrollStateChanged(int arg0) {
}
});
}
@Override
public void onTabReselected(Tab tab, FragmentTransaction ft) {
}
@Override
public void onTabSelected(Tab tab, FragmentTransaction ft) {
// on tab selected
// show respected fragment view
viewPager.setCurrentItem(tab.getPosition());
}
@Override
public void onTabUnselected(Tab tab, FragmentTransaction ft) {
}