24

getItemPosition(Object object)ページを並べ替える目的でオーバーライドすると、 FragmentStatePagerAdapter が正しく動作しないと思います。

以下は簡単な例です。初期状態では、ページの順番は{A, B, C}です。を呼び出すtoggleState()と、ページの順序が {A, C, B} に変わります。をオーバーライドgetItemPosition(Object object)することで、現在表示されているページ (A、B、または C) が変更されないようにします。

public static class TestPagerAdapter extends FragmentStatePagerAdapter {
    private boolean mState = true;

    public TestPagerAdapter(FragmentManager fragmentManager) {
        super(fragmentManager);
    }

    @Override
    public int getCount() {
        return 3;
    }

    private void toggleState() {
        mState = !mState;
        notifyDataSetChanged();
    }

    private String getLabel(int position) {
        switch (position) {
            case 0:
                return "A";
            case 1:
                return mState ? "B" : "C";
            default:
                return mState ? "C" : "B";
        }
    }

    @Override
    public int getItemPosition(Object object) {
        String label = ((TestFragment) object).getLabel();
        if (label.equals("A")) {
            return 0;
        } else if (label.equals("B")) {
            return mState ? 1 : 2;
        } else {
            return mState ? 2 : 1;
        }
    }

    @Override
    public CharSequence getPageTitle(int position) {
        return getLabel(position);
    }

    @Override
    public Fragment getItem(int position) {
        return TestFragment.newInstance(getLabel(position));
    }
}

間違っていると思われる 2 つの異なる動作に遭遇しました。

  1. すぐに呼び出すとtoggleState()(ページ A を表示しているときに、他のページにスワイプする前に)、アプリがクラッシュします。

    java.lang.IndexOutOfBoundsException: Invalid index 2, size is 2
      at java.util.ArrayList.throwIndexOutOfBoundsException(ArrayList.java:251)
      at java.util.ArrayList.set(ArrayList.java:477)
      at android.support.v4.app.FragmentStatePagerAdapter.destroyItem(FragmentStatePagerAdapter.java:136)
      at android.support.v4.view.ViewPager.populate(ViewPager.java:867)
      at android.support.v4.view.ViewPager.setCurrentItemInternal(ViewPager.java:469)
      at android.support.v4.view.ViewPager.setCurrentItemInternal(ViewPager.java:441)
      at android.support.v4.view.ViewPager.dataSetChanged(ViewPager.java:766)
      at android.support.v4.view.ViewPager$PagerObserver.onChanged(ViewPager.java:2519)
      at android.database.DataSetObservable.notifyChanged(DataSetObservable.java:37)
      at android.support.v4.view.PagerAdapter.notifyDataSetChanged(PagerAdapter.java:276)
      at com.ugglynoodle.test.testfragmentstatepageradapter.MainActivity$TestPagerAdapter.toggleState(MainActivity.java:55)
      ...
    

    のソースを見ると、これは136 行で呼び出す前に (113 ~ 115 行FragmentStatePagerAdapterのように) のサイズを最初にチェックすることで修正されます。mFragmentsset()

  2. 最初にページ B にスワイプしてからgetItem(2)呼び出されると、ページ C が作成され、mFragmentsサイズが 3 になりました (これにより、上記のクラッシュがすぐに発生するのを防ぐことができます)。次に、スワイプしてページ A に戻ると、ページ C が破棄されます (2 ページ離れているため、デフォルトのオフスクリーン ページ制限 1 を使用しているため)。今、私は電話しますtoggleState()。ページ B は現在破棄されています。ただし、ページ C は再作成されません。つまり、右にスワイプすると空のページが表示されます。

まず、私が正しくてこれらが実際にバグなのか、それとも私が何か間違ったことをしているのかを知ることができればうれしいです. それらがバグである場合、誰かが回避策を提案できますか (サポート ライブラリを自分でデバッグして再構築する以外に)。確かに、誰かがgetItemPosition(Object object)正常にオーバーライドしたに違いありません (すべてを に設定することは別としてPOSITION_NONE)?

サポート ライブラリの現在のリビジョン (10) を使用しています。

4

4 に答える 4

37

FragmentStatePagerAdapter のソースを見て、何が問題なのかを正確に把握しました。FragmentStatePagerAdapter は、フラグメントと保存された状態を ArrayLists:mFragmentsとにキャッシュしmSavedStateます。しかし、フラグメントが並べ替えられた場合、 と の要素を並べ替えるメカニズムはありませmFragmentsmSavedState。したがって、アダプターはページャーに間違ったフラグメントを提供します。

これについて問題を提出し、修正された実装 (NewFragmentStatePagerAdapter.java) を問題に添付しました。getItemId()修正では、 FragmentStatePagerAdapter に関数を追加しました。(これは、FragmentPagerAdapter での並べ替えの実装を反映しています。) アダプターの位置による itemIds の配列が常に格納されます。次に、notifyDataSetChanged()アダプタは itemIds 配列が変更されたかどうかを確認します。ある場合は、それmFragmentsmSavedState応じて並べ替えられます。その他の変更はdestroyItem()、 、saveState()およびにありrestoreState()ます。

このクラスを使用するには、getItemPosition()getItemId()一貫して実装する必要がありますgetItem()

于 2012-09-29T08:16:05.167 に答える
1

私にとって、問題の答えの1つが機能しました。回答 #20 #21。ソリューションへのリンクhttps://gist.github.com/ypresto/8c13cb88a0973d071a64。最良の解決策は、ページの更新と並べ替えにも機能します。このソリューションでのみ、Adapter は (メソッド destroyItem で) アイテムを破棄するときに IndexOutOfBoundsExeption をスローしませんでした。これは、他のソリューションの既知のバグです。

于 2016-10-11T20:27:56.083 に答える