フラグメントを保持するこのアクティビティを持っています。このフラグメントレイアウトは、いくつかのフラグメント(実際には2つ)を持つビューページャーで構成されています。
ビューページャーが作成されると、そのアダプターが作成され、getItem
が呼び出されて、私のサブフラグメントが作成されます。すごい。
画面を回転すると、フレームワークがフラグメントの再作成を処理し、アダプタがメインフラグメントからonCreate
に再び作成されますが、getItem
呼び出されないので、私のアダプターは2つのフラグメントの代わりに間違った参照(実際にはnull)を保持します。
私が見つけたのは、フラグメントマネージャー(つまり、子フラグメントマネージャー)にmActive
というフラグメントの配列が含まれていることです。もちろん、コードからはアクセスできません。ただし、このgetFragment
メソッドがあります。
@Override
public Fragment getFragment(Bundle bundle, String key) {
int index = bundle.getInt(key, -1);
if (index == -1) {
return null;
}
if (index >= mActive.size()) {
throwException(new IllegalStateException("Fragement no longer exists for key "
+ key + ": index " + index));
}
Fragment f = mActive.get(index);
if (f == null) {
throwException(new IllegalStateException("Fragement no longer exists for key "
+ key + ": index " + index));
}
return f;
}
タイプミスにコメントしません:)
これは、アダプターコンストラクターで、フラグメントへの参照を更新するために実装したハックです。
// fm holds a reference to a FragmentManager
Bundle hack = new Bundle();
try {
for (int i = 0; i < mFragments.length; i++) {
hack.putInt("hack", i);
mFragments[i] = fm.getFragment(hack, "hack");
}
} catch (Exception e) {
// No need to fail here, likely because it's the first creation and mActive is empty
}
私は誇りに思いません。これは機能しますが、醜いです。画面を回転させた後に有効なアダプターを使用する実際の方法は何ですか?
PS:これが 完全なコードです
同じ問題が発生しました-ページャーアダプターのFragmentPagerAdapter
をサブクラス化していると思います(getItem()
はFragmentPagerAdapter
に固有なので)。
私の解決策は、代わりにPagerAdapter
をサブクラス化し、フラグメントの作成/削除を自分で処理することでした(FragmentPagerAdapter
コードの一部を再実装):
public class ListPagerAdapter extends PagerAdapter {
FragmentManager fragmentManager;
Fragment[] fragments;
public ListPagerAdapter(FragmentManager fm){
fragmentManager = fm;
fragments = new Fragment[5];
}
@Override
public void destroyItem(ViewGroup container, int position, Object object) {
assert(0 <= position && position < fragments.length);
FragmentTransaction trans = fragmentManager.beginTransaction();
trans.remove(fragments[position]);
trans.commit();
fragments[position] = null;
}
@Override
public Fragment instantiateItem(ViewGroup container, int position){
Fragment fragment = getItem(position);
FragmentTransaction trans = fragmentManager.beginTransaction();
trans.add(container.getId(),fragment,"fragment:"+position);
trans.commit();
return fragment;
}
@Override
public int getCount() {
return fragments.length;
}
@Override
public boolean isViewFromObject(View view, Object fragment) {
return ((Fragment) fragment).getView() == view;
}
public Fragment getItem(int position){
assert(0 <= position && position < fragments.length);
if(fragments[position] == null){
fragments[position] = ; //make your fragment here
}
return fragments[position];
}
}
お役に立てれば。
私の答えはジョシュアハントの答えに似ていますが、finishUpdate
メソッドでトランザクションをコミットすると、パフォーマンスが大幅に向上します。更新ごとに2つのトランザクションではなく1つのトランザクション。これがコードです:
private class SuchPagerAdapter extends PagerAdapter{
private final FragmentManager mFragmentManager;
private SparseArray<Fragment> mFragments;
private FragmentTransaction mCurTransaction;
private SuchPagerAdapter(FragmentManager fragmentManager) {
mFragmentManager = fragmentManager;
mFragments = new SparseArray<>();
}
@Override
public Object instantiateItem(ViewGroup container, int position) {
Fragment fragment = getItem(position);
if (mCurTransaction == null) {
mCurTransaction = mFragmentManager.beginTransaction();
}
mCurTransaction.add(container.getId(),fragment,"fragment:"+position);
return fragment;
}
@Override
public void destroyItem(ViewGroup container, int position, Object object) {
if (mCurTransaction == null) {
mCurTransaction = mFragmentManager.beginTransaction();
}
mCurTransaction.detach(mFragments.get(position));
mFragments.remove(position);
}
@Override
public boolean isViewFromObject(View view, Object fragment) {
return ((Fragment) fragment).getView() == view;
}
public Fragment getItem(int position) {
return YoursVeryFragment.instantiate();
}
@Override
public void finishUpdate(ViewGroup container) {
if (mCurTransaction != null) {
mCurTransaction.commitAllowingStateLoss();
mCurTransaction = null;
mFragmentManager.executePendingTransactions();
}
}
@Override
public int getCount() {
return countOfPages;
}
}
上記のソリューションはなぜそれほど複雑なのですか?やり過ぎのようです。 FragmentPagerAdapterから拡張されたクラス内の新しい参照を古い参照に置き換えるだけで解決します
@Override
public Object instantiateItem(ViewGroup container, int position) {
frags[position] = (Fragment) super.instantiateItem(container, position);
return frags[position];
}
すべてのアダプターのコードは次のようになります
public class RelationsFragmentsAdapter extends FragmentPagerAdapter {
private final String titles[] = new String[3];
private final Fragment frags[] = new Fragment[titles.length];
public RelationsFragmentsAdapter(FragmentManager fm) {
super(fm);
frags[0] = new FriendsFragment();
frags[1] = new FriendsRequestFragment();
frags[2] = new FriendsDeclinedFragment();
Resources resources = AppController.getAppContext().getResources();
titles[0] = resources.getString(R.string.my_friends);
titles[1] = resources.getString(R.string.my_new_friends);
titles[2] = resources.getString(R.string.followers);
}
@Override
public CharSequence getPageTitle(int position) {
return titles[position];
}
@Override
public Fragment getItem(int position) {
return frags[position];
}
@Override
public int getCount() {
return frags.length;
}
@Override
public Object instantiateItem(ViewGroup container, int position) {
frags[position] = (Fragment) super.instantiateItem(container, position);
return frags[position];
}
}
問題は、FragmentPageAdapter
のgetItem()
の名前が間違っていることです。 createItem()
という名前になっているはずです。 getItem()
の動作方法はフラグメントの作成用であり、フラグメントをクエリ/検索するために呼び出すのは安全ではないためです。
私の推奨は、現在のFragmentPagerAdapterのコピーを作成し、それをそのように変更することです。
追加:
public abstract Fragment createFragment(int position);
そして、getItemを次のように変更します。
public Fragment getItem(int position) {
if(containerId!=null) {
final long itemId = getItemId(position);
String name = makeFragmentName(containerId, itemId);
return mFragmentManager.findFragmentByTag(name);
} else {
return null;
}
}
最後にそれをinstantiateItemに追加します。
if(containerId==null)
containerId = container.getId();
else if(containerId!=container.getId())
throw new RuntimeException("Container id not expected to change");
this Gist の完全なコード
この実装はより安全で使いやすく、Googleエンジニアによるオリジナルのアダプターと同じパフォーマンスを備えていると思います。
私のコード:
public class SampleAdapter extends FragmentStatePagerAdapter {
private Fragment mFragmentAtPos2;
private FragmentManager mFragmentManager;
private Fragment[] mFragments = new Fragment[3];
public SampleAdapter(FragmentManager mgr) {
super(mgr);
mFragmentManager = mgr;
Bundle hack = new Bundle();
try {
for (int i = 0; i < mFragments.length; i++) {
hack.putInt("hack", i);
mFragments[i] = mFragmentManager.getFragment(hack, "hack");
}
} catch (Exception e) {
// No need to fail here, likely because it's the first creation and mActive is empty
}
}
public void switchFrag(Fragment frag) {
if (frag == null) {
Dbg.e(TAG, "- switch(frag) frag is NULL");
return;
} else Dbg.v(TAG, "- switch(frag) - frag is " + frag.getClass());
// We have to check for mFragmentAtPos2 null in case of first time (only Mytrips fragment being instatiante).
if (mFragmentAtPos2!= null)
mFragmentManager.beginTransaction()
.remove(mFragmentAtPos2)
.commit();
mFragmentAtPos2 = frag;
notifyDataSetChanged();
}
@Override
public int getCount() {
return(3);
}
@Override
public int getItemPosition(Object object) {
Dbg.v(TAG,"getItemPosition : "+object.getClass());
if (object instanceof MyTripsFragment
|| object instanceof FindingDriverFragment
|| object instanceof BookingAcceptedFragment
|| object instanceof RideStartedFragment
|| object instanceof RideEndedFragment
|| object instanceof ContactUsFragment
)
return POSITION_NONE;
else return POSITION_UNCHANGED;
}
@Override
public Fragment getItem(int position) {
Dbg.v("SampleAdapter", "getItem called on: "+position);
switch (position) {
case 0:
if (snapbookFrag==null) {
snapbookFrag = new SnapBookingFragment();
Dbg.e(TAG, "snapbookFrag created");
}
return snapbookFrag;
case 1:
if(bookingFormFrag==null) {
bookingFormFrag = new BookingFormFragment();
Dbg.e(TAG, "bookingFormFrag created");
}
return bookingFormFrag;
case 2:
if (mFragmentAtPos2 == null) {
myTripsFrag = new MyTripsFragment();
mFragmentAtPos2 = myTripsFrag;
return mFragmentAtPos2;
}
return mFragmentAtPos2;
default:
return(new SnapBookingFragment());
}
}
}
simekadam's ソリューションを参照すると、mFragmentsがinstantiateItemに入力されておらず、内部にmFragments.put(position, fragment);
が必要です。そうしないと、次のエラーが発生します。 フラグメントを削除しようとしていますビューは私にmNextAnimでNullPointerExceptionを与えます 。