[解決済み] ViewPagerとフラグメント - フラグメントの状態を保存する正しい方法は何ですか?
質問
フラグメントは、UIロジックをいくつかのモジュールに分離するのに非常に適しているようです。しかし
ViewPager
そのライフサイクルは、私にはまだ霧の中です。だから、Guruの考えがとても必要なのです。
編集
以下の解決策を参照してください;-)
スコープ
メインアクティビティには
ViewPager
というフラグメントがあります。これらのフラグメントは、他の(サブメイン)アクティビティのために少し異なるロジックを実装することができるので、フラグメントのデータはアクティビティ内のコールバックインターフェースを介して入力されます。そして、最初の起動ではすべてうまくいきますが、しかし!...
問題点
アクティビティが再作成されるとき(例えば、方向転換のとき)、そのアクティビティに含まれる
ViewPager
の断片があります。このコード(下にあります)では、アクティビティが作成されるたびに、新しい
ViewPager
フラグメントアダプタはフラグメントと同じですが(多分これが問題)、FragmentManagerはすでにこれらのフラグメントをすべてどこかに保存しており(どこに?)、それらのための再作成機構を開始します。そこで、再作成機構は、quot;old"フラグメントのonAttachやonCreateViewなどを、Activityの実装メソッドを介してデータを開始するための私のコールバックインターフェースコールを呼び出します。しかし、このメソッドは、ActivityのonCreateメソッドによって作成された、新しく作成されたフラグメントを指しています。
課題
私の使い方が悪いのかもしれませんが、Android 3 Proの本にもあまり載っていません。そこで お願い ありがとうございました。
コード
主な活動内容
public class DashboardActivity extends BasePagerActivity implements OnMessageListActionListener {
private MessagesFragment mMessagesFragment;
@Override
protected void onCreate(Bundle savedInstanceState) {
Logger.d("Dash onCreate");
super.onCreate(savedInstanceState);
setContentView(R.layout.viewpager_container);
new DefaultToolbar(this);
// create fragments to use
mMessagesFragment = new MessagesFragment();
mStreamsFragment = new StreamsFragment();
// set titles and fragments for view pager
Map<String, Fragment> screens = new LinkedHashMap<String, Fragment>();
screens.put(getApplicationContext().getString(R.string.dashboard_title_dumb), new DumbFragment());
screens.put(getApplicationContext().getString(R.string.dashboard_title_messages), mMessagesFragment);
// instantiate view pager via adapter
mPager = (ViewPager) findViewById(R.id.viewpager_pager);
mPagerAdapter = new BasePagerAdapter(screens, getSupportFragmentManager());
mPager.setAdapter(mPagerAdapter);
// set title indicator
TitlePageIndicator indicator = (TitlePageIndicator) findViewById(R.id.viewpager_titles);
indicator.setViewPager(mPager, 1);
}
/* set of fragments callback interface implementations */
@Override
public void onMessageInitialisation() {
Logger.d("Dash onMessageInitialisation");
if (mMessagesFragment != null)
mMessagesFragment.loadLastMessages();
}
@Override
public void onMessageSelected(Message selectedMessage) {
Intent intent = new Intent(this, StreamActivity.class);
intent.putExtra(Message.class.getName(), selectedMessage);
startActivity(intent);
}
BasePagerActivityの別名ヘルパー
public class BasePagerActivity extends FragmentActivity {
BasePagerAdapter mPagerAdapter;
ViewPager mPager;
}
アダプタ
public class BasePagerAdapter extends FragmentPagerAdapter implements TitleProvider {
private Map<String, Fragment> mScreens;
public BasePagerAdapter(Map<String, Fragment> screenMap, FragmentManager fm) {
super(fm);
this.mScreens = screenMap;
}
@Override
public Fragment getItem(int position) {
return mScreens.values().toArray(new Fragment[mScreens.size()])[position];
}
@Override
public int getCount() {
return mScreens.size();
}
@Override
public String getTitle(int position) {
return mScreens.keySet().toArray(new String[mScreens.size()])[position];
}
// hack. we don't want to destroy our fragments and re-initiate them after
@Override
public void destroyItem(View container, int position, Object object) {
// TODO Auto-generated method stub
}
}
フラグメント
public class MessagesFragment extends ListFragment {
private boolean mIsLastMessages;
private List<Message> mMessagesList;
private MessageArrayAdapter mAdapter;
private LoadMessagesTask mLoadMessagesTask;
private OnMessageListActionListener mListener;
// define callback interface
public interface OnMessageListActionListener {
public void onMessageInitialisation();
public void onMessageSelected(Message selectedMessage);
}
@Override
public void onAttach(Activity activity) {
super.onAttach(activity);
// setting callback
mListener = (OnMessageListActionListener) activity;
mIsLastMessages = activity instanceof DashboardActivity;
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
inflater.inflate(R.layout.fragment_listview, container);
mProgressView = inflater.inflate(R.layout.listrow_progress, null);
mEmptyView = inflater.inflate(R.layout.fragment_nodata, null);
return super.onCreateView(inflater, container, savedInstanceState);
}
@Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
// instantiate loading task
mLoadMessagesTask = new LoadMessagesTask();
// instantiate list of messages
mMessagesList = new ArrayList<Message>();
mAdapter = new MessageArrayAdapter(getActivity(), mMessagesList);
setListAdapter(mAdapter);
}
@Override
public void onResume() {
mListener.onMessageInitialisation();
super.onResume();
}
public void onListItemClick(ListView l, View v, int position, long id) {
Message selectedMessage = (Message) getListAdapter().getItem(position);
mListener.onMessageSelected(selectedMessage);
super.onListItemClick(l, v, position, id);
}
/* public methods to load messages from host acitivity, etc... */
}
解決方法
馬鹿な解決策は、(ホストアクティビティーの)onSaveInstanceState内でputFragmentを使用してフラグメントを保存し、onCreate内でgetFragmentを使用してフラグメントを取得することです。でも、そんなことをしてもダメなような気がするのですが...。以下のコードを見てください。
@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
getSupportFragmentManager()
.putFragment(outState, MessagesFragment.class.getName(), mMessagesFragment);
}
protected void onCreate(Bundle savedInstanceState) {
Logger.d("Dash onCreate");
super.onCreate(savedInstanceState);
...
// create fragments to use
if (savedInstanceState != null) {
mMessagesFragment = (MessagesFragment) getSupportFragmentManager().getFragment(
savedInstanceState, MessagesFragment.class.getName());
StreamsFragment.class.getName());
}
if (mMessagesFragment == null)
mMessagesFragment = new MessagesFragment();
...
}
解決方法は?
このとき
FragmentPagerAdapter
はフラグメントを FragmentManager に追加する際、フラグメントが配置される特定の位置に基づいた特別なタグを使用します。
FragmentPagerAdapter.getItem(int position)
は、その位置のフラグメントが存在しない場合にのみ呼び出されます。回転した後、Androidはこの特定の位置のフラグメントをすでに作成/保存していることに気づくので、単純に
FragmentManager.findFragmentByTag()
新しいものを作るのではありません。このようなことは
FragmentPagerAdapter
そのため、フラグメントの初期化コードを
getItem(int)
メソッドを使用します。
を使用していなかったとしても
FragmentPagerAdapter
でいちいち新しいフラグメントを作成するのは良くない。
Activity.onCreate(Bundle)
. お気づきのように、フラグメントをFragmentManagerに追加すると、ローテート後に再作成されるので、再度追加する必要はありません。このような操作は、フラグメントを扱う際によくあるエラーの原因となります。
フラグメントを扱うときの通常のアプローチはこうです。
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
...
CustomFragment fragment;
if (savedInstanceState != null) {
fragment = (CustomFragment) getSupportFragmentManager().findFragmentByTag("customtag");
} else {
fragment = new CustomFragment();
getSupportFragmentManager().beginTransaction().add(R.id.container, fragment, "customtag").commit();
}
...
}
を使用する場合
FragmentPagerAdapter
の場合、フラグメントの管理をアダプタに委ねることになるので、上記の手順を実行する必要はありません。デフォルトでは、現在の位置の前後にあるフラグメントをひとつずつプリロードするだけです (しかし、これは
FragmentStatePagerAdapter
). これは
ViewPager.setOffscreenPageLimit(int)
. このため、アダプタ外のフラグメントのメソッドを直接呼び出すと、フラグメントが生きていない可能性があり、有効であることが保証されません。
長い話を短くすると、あなたの解決策である
putFragment
というのは、それほどおかしなことではなく、また、通常のフラグメントの使い方(上記)とさほど変わりはありません。なぜなら、フラグメントを追加するのはアダプターであって、あなた個人ではないからです。ただ、そのためには
offscreenPageLimit
は、必要なフラグメントを常に読み込むのに十分な高さであり、フラグメントの存在に依存しているためです。これは、ViewPagerの遅延ロード機能を回避しますが、あなたのアプリケーションに必要なものであると思われます。
もう一つの方法は
FragmentPageAdapter.instantiateItem(View, int)
を返す前に、スーパーコールから返されたフラグメントへの参照を保存します (フラグメントが既に存在する場合は、それを見つけるロジックがあります)。
のソースの一部をご覧ください。 FragmentPagerAdapter (短い)と ビューページャー (長い)。
関連
-
adb devices OffLine Solution(オフラインソリューション
-
を作ってください。*** makeするルールがない エラーの原因、分析、解決策
-
android:EMSのプロパティ
-
Androidの美しいSeekBarスタイルのカスタマイズ
-
Android Get set image.setImageResource(R.drawable.xxx) リソース
-
SpinnerのOnItemSelectedListenerのonItemSelectedメソッドの4つのパラメーターの意味
-
Android Studioのgitの使用とgitの設定パス
-
Android studio 制約レイアウト ConstraintLayout
-
[解決済み] FragmentのsetRetainInstance(boolean)を理解する。
-
[解決済み】ViewPagerを動的に更新しますか?
最新
-
nginxです。[emerg] 0.0.0.0:80 への bind() に失敗しました (98: アドレスは既に使用中です)
-
htmlページでギリシャ文字を使うには
-
ピュアhtml+cssでの要素読み込み効果
-
純粋なhtml + cssで五輪を実現するサンプルコード
-
ナビゲーションバー・ドロップダウンメニューのHTML+CSSサンプルコード
-
タイピング効果を実現するピュアhtml+css
-
htmlの選択ボックスのプレースホルダー作成に関する質問
-
html css3 伸縮しない 画像表示効果
-
トップナビゲーションバーメニュー作成用HTML+CSS
-
html+css 実装 サイバーパンク風ボタン
おすすめ
-
cygwinのダウンロード、インストールチュートリアル、およびCDTの「makeプログラムがパスに見つからない」バグの解消
-
デフォルトのアクティビティが見つからない場合の対処法
-
GIF、Lottie、SVGA
-
アプリはGoogle検索でインデックスされません Androidmanifestのクソみたいな黄色い警告
-
Android ProgressBarのスタイルカラーを変更する
-
アンドロイドスタジオ学習入門
-
Android Studio常见错误之:Rendering Problems/The following classes could not be instantiated
-
android studioが新しいプロジェクトを作成しますが、プロジェクトの同期に成功するまでデザインエディタが使用できません。
-
[解決済み】Android. Fragment getActivity()がNULLを返すことがある
-
[解決済み] AndroidのViewPagerからフラグメントページを削除する