サポートFragmentPagerAdapterは古いフラグメントへの参照を保持します


109

最新の情報:

問題を、fragmentManagerが古いフラグメントのインスタンスを保持する問題と、viewpagerがFragmentManagerと同期していない問題に絞り込みました。この問題を参照してください... http://code.google.com/p/android/issues/detail?id=19211#makechanges。これを解決する方法はまだわかりません。助言がありますか...

私は長い間これをデバッグしようとしました、そしてどんな助けも大いに感謝されるでしょう。私はそのようなフラグメントのリストを受け入れるFragmentPagerAdapterを使用しています:

List<Fragment> fragments = new Vector<Fragment>();
fragments.add(Fragment.instantiate(this, Fragment1.class.getName())); 
...
new PagerAdapter(getSupportFragmentManager(), fragments);

実装は標準です。フラグメントにActionBarSherlockとv4計算ライブラリを使用しています。

私の問題は、アプリを終了して他のいくつかのアプリケーションを開いて戻った後、フラグメントがFragmentActivity(つまりgetActivity() == null)への参照を失うことです。なぜこれが起こっているのか理解できません。手動で設定しようとしましたsetRetainInstance(true);が、これは役に立ちません。これはFragmentActivityが破棄されたときに発生すると考えましたが、ログメッセージが表示される前にアプリを開いた場合にも発生します。何かアイデアはありますか?

@Override
protected void onDestroy(){
    Log.w(TAG, "DESTROYDESTROYDESTROYDESTROYDESTROYDESTROYDESTROY");
    super.onDestroy();
}

アダプター:

public class PagerAdapter extends FragmentPagerAdapter {
    private List<Fragment> fragments;

    public PagerAdapter(FragmentManager fm, List<Fragment> fragments) {
        super(fm);

        this.fragments = fragments;

    }

    @Override
    public Fragment getItem(int position) {

        return this.fragments.get(position);

    }

    @Override
    public int getCount() {

        return this.fragments.size();

    }

}

私のフラグメントの1つが取り除かれたが、取り除かれたが、それでも機能しないことがわかった...

public class MyFragment extends Fragment implements MyFragmentInterface, OnScrollListener {
...

@Override
public void onCreate(Bundle savedInstanceState){
    super.onCreate(savedInstanceState);
    handler = new Handler();    
    setHasOptionsMenu(true);
}

@Override
public void onAttach(Activity activity) {
    super.onAttach(activity);
    Log.w(TAG,"ATTACHATTACHATTACHATTACHATTACH");
    context = activity;
    if(context== null){
        Log.e("IS NULL", "NULLNULLNULLNULLNULLNULLNULLNULLNULLNULLNULL");
    }else{
        Log.d("IS NOT NULL", "NOTNOTNOTNOTNOTNOTNOTNOT");
    }

}

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

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
    View v = inflater.inflate(R.layout.my_fragment,container, false);

    return v;
}


@Override
public void onResume(){
    super.onResume();
}

private void callService(){
    // do not call another service is already running
    if(startLoad || !canSet) return;
    // set flag
    startLoad = true;
    canSet = false;
    // show the bottom spinner
    addFooter();
    Intent intent = new Intent(context, MyService.class);
    intent.putExtra(MyService.STATUS_RECEIVER, resultReceiver);
    context.startService(intent);
}

private ResultReceiver resultReceiver = new ResultReceiver(null) {
    @Override
    protected void onReceiveResult(int resultCode, final Bundle resultData) {
        boolean isSet = false;
        if(resultData!=null)
        if(resultData.containsKey(MyService.STATUS_FINISHED_GET)){
            if(resultData.getBoolean(MyService.STATUS_FINISHED_GET)){
                removeFooter();
                startLoad = false;
                isSet = true;
            }
        }

        switch(resultCode){
        case MyService.STATUS_FINISHED: 
            stopSpinning();
            break;
        case SyncService.STATUS_RUNNING:
            break;
        case SyncService.STATUS_ERROR:
            break;
        }
    }
};

public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
    menu.clear();
    inflater.inflate(R.menu.activity, menu);
}

@Override
public void onPause(){
    super.onPause();
}

public void onScroll(AbsListView arg0, int firstVisible, int visibleCount, int totalCount) {
    boolean loadMore = /* maybe add a padding */
        firstVisible + visibleCount >= totalCount;

    boolean away = firstVisible+ visibleCount <= totalCount - visibleCount;

    if(away){
        // startLoad can now be set again
        canSet = true;
    }

    if(loadMore) 

}

public void onScrollStateChanged(AbsListView arg0, int state) {
    switch(state){
    case OnScrollListener.SCROLL_STATE_FLING: 
        adapter.setLoad(false); 
        lastState = OnScrollListener.SCROLL_STATE_FLING;
        break;
    case OnScrollListener.SCROLL_STATE_IDLE: 
        adapter.setLoad(true);
        if(lastState == SCROLL_STATE_FLING){
            // load the images on screen
        }

        lastState = OnScrollListener.SCROLL_STATE_IDLE;
        break;
    case OnScrollListener.SCROLL_STATE_TOUCH_SCROLL:
        adapter.setLoad(true);
        if(lastState == SCROLL_STATE_FLING){
            // load the images on screen
        }

        lastState = OnScrollListener.SCROLL_STATE_TOUCH_SCROLL;
        break;
    }
}

@Override
public void onDetach(){
    super.onDetach();
    if(this.adapter!=null)
        this.adapter.clearContext();

    Log.w(TAG, "DETACHEDDETACHEDDETACHEDDETACHEDDETACHEDDETACHED");
}

public void update(final int id, String name) {
    if(name!=null){
        getActivity().getSupportActionBar().setTitle(name);
    }

}

}

updateメソッドは、ユーザーが別のフラグメントを操作し、getActivityがnullを返したときに呼び出されます。これが他のフラグメントが呼び出しているメソッドです...

((MyFragment) pagerAdapter.getItem(1)).update(id, name);

デフォルトのフラグメントまでアプリを起動するのではなく、アプリが破棄されて再度作成されると、アプリが起動し、viewpagerが最後の既知のページに移動すると思います。これは奇妙に思えますが、アプリをデフォルトのフラグメントにロードするだけではいいのではないですか?


27
Androidのフラグメントは最低です。
Hamidreza Sadegh、2015

13
神様私はあなたのログメッセージを愛しています:D
oli.G

回答:


120

の外部でフラグメントへの参照をインスタンス化して保持しPagerAdapter.getItem、ViewPagerとは無関係にそれらの参照を使用しようとしているため、問題が発生しています。Seraphが言うように、特定の時間にフラグメントがViewPagerでインスタンス化/追加されたことが保証されます-これは実装の詳細と見なされます。ViewPagerはページの遅延読み込みを行います。デフォルトでは、現在のページと左右のページのみが読み込まれます。

アプリをバックグラウンドに置くと、フラグメントマネージャーに追加されたフラグメントは自動的に保存されます。アプリが強制終了された場合でも、アプリを再起動するとこの情報が復元されます。

ここで、フラグメントA、B、Cのいくつかのページを表示したことを考慮してください。これらはフラグメントマネージャーに追加されていることがわかります。あなたが使っているのでFragmentPagerAdapterおりFragmentStatePagerAdapter、していないため、他のページにスクロールしても、これらのフラグメントは追加されます(ただし、切り離される可能性があります)。

次に、アプリケーションのバックグラウンドを作成すると、アプリケーションが強制終了されることを考慮してください。戻ってきたとき、AndroidはフラグメントマネージャーにフラグメントA、B、Cを使用していたことを記憶しているので、フラグメントを再作成して追加します。ただし、フラグメントマネージャーに追加されたものは、アクティビティのフラグメントリストにあるものではなくなりました。

getPositionその特定のページ位置にすでにフラグメントが追加されている場合、FragmentPagerAdapterは呼び出しを試みません。実際、Androidによって再作成されたフラグメントは削除されないため、への呼び出しに置き換えることはできませんgetPosition。ハンドルを取得することも、不明なタグが追加されているため、参照を取得するのが非常に困難です。これは仕様によるものです。ビューページャーが管理しているフラグメントをいじるのはお勧めできません。必要に応じて、フラグメント内ですべてのアクションを実行し、アクティビティと通信し、特定のページへの切り替えを要求する必要があります。

さて、行方不明の活動の問題に戻りましょう。pagerAdapter.getItem(1)).update(id, name)これがすべて発生した後に呼び出すと、リスト内のフラグメントが返されます。フラグメントはまだフラグメントマネージャーに追加されていないため、アクティビティ参照がありません。私はあなたの更新メソッドがいくつかの共有データ構造(おそらくアクティビティによって管理される)を変更し、特定のページに移動すると、この更新されたデータに基づいてそれ自体を描画することをお勧めします。


1
非常にエレガントで、コードをリファクタリングする可能性が高いため、私はあなたのソリューションを気に入っています。ソリューションでは、すべてのデータをFragmentActivityに保持し、各フラグメントを使用して表示ロジックを処理するだけで十分です。私が言ったようにこれを好みますが、フラグメント間の相互作用は非常に重く、管理するのは面倒です。とにかく詳しい説明ありがとうございます。あなたはこれを私よりもずっとよく説明しました。
モーリシー

28
ショート:アダプターの外部でフラグメントへの参照を保持しないでください
passsy

2
では、FragmentPagerAdapterをインスタンス化しなかった場合、ActivityインスタンスはどのようにFragmentPagerAdapterインスタンスにアクセスするのでしょうか。将来のインスタンスはFragmentPagerAdapterとそのすべてのフラグメントインスタンスを再インスタンス化するだけではありませんか?FragmentPagerAdapterは、フラグメント間の通信を管理するためにすべてのフラグメントインターフェイスを実装する必要がありますか?
エリックH.

「ハンドルを取得することも、不明なタグが追加されているため、参照を取得するのが非常に困難です。これは仕様によるものです。ビューページャーが管理しているフラグメントをいじることは避けてください。 」は偽です。呼び出しによって参照を取得するのは非常に簡単で、instantiateItem実際にonCreateアクティビティの中で行う必要があります。詳細はこちらを参照してください。stackoverflow.com/questions/14035090/...
morgwai

「破壊と再作成」シナリオでは、あなたが実際に再作成して示したものに別々の断片を扱うに終わるかもしれないので、一般的には、ロードすることなく、自分自身をインスタンス化断片に危険なのです
HMAC

108

私に役立つ簡単な解決策を見つけました。

フラグメントアダプターがFragmentPagerAdapterではなくFragmentStatePagerAdapterを拡張し、メソッドonSaveをオーバーライドしてnullを返すようにする

@Override
public Parcelable saveState()
{
    return null;
}

これは、androidがフラグメントを再作成するのを防ぎます


ある日、私は別のより良い解決策を見つけました。

setRetainInstance(true)すべてのフラグメントを呼び出し、それらへの参照をどこかに保存します。これは、アクティビティの静的変数で行いました。singleTaskとして宣言されており、フラグメントは常に同じままでかまいません。

この方法では、androidはフラグメントを再作成せず、同じインスタンスを使用します。


4
おかげで、ありがとう、ありがとう本当にありがとう、Mik、私はこの問題を過去10日間追跡して、非常に多くの方法を試しました。

7
フラグメントやアクティビティへの静的参照があると、メモリリークが非常に簡単に発生する可能性があるため、非常に危険です。もちろん、注意すれば、不要になったときにnullに設定することで、非常に簡単に処理できます。
Android開発者

これでうまくいきました。フラグメントとそれぞれのビューは、アプリがクラッシュして再起動した後もリンクを維持します。ありがとう!
swebal 2015

で使用setRetainInstance(true)していFragmentPagerAdapterます。すべてうまくいきます。しかし、デバイスを回転させても、アダプターにはフラグメントが残っていますが、フラグメントは表示されません。フラグメントのライフサイクルメソッドも呼び出されません。誰か助けてもらえますか?
Jonas

1
あなたは私の日を救った!これまでに3日間このバグを検索してきました。SingleTaskアクティビティ内に2つのフラグメントがあり、「Dont keep Activities」フラグが有効になっているViewpagerがありました。どうもありがとう!!!!
マトリックス

29

このようにFragmentPagerAdapter経由ではなく、FragmentManagerから直接フラグメントにアクセスすることで、この問題を解決しました。まず、FragmentPagerAdapterによって自動生成されたフラグメントのタグを理解する必要があります...

private String getFragmentTag(int pos){
    return "android:switcher:"+R.id.viewpager+":"+pos;
}

次に、そのフラグメントへの参照を取得し、必要なことを行います...

Fragment f = this.getSupportFragmentManager().findFragmentByTag(getFragmentTag(1));
((MyFragmentInterface) f).update(id, name);
viewPager.setCurrentItem(1, true);

私の断片の中に私は設定しました setRetainInstance(false); saveedInstanceStateバンドルに手動で値を追加できるように。

@Override
public void onSaveInstanceState(Bundle outState) {
    if(this.my !=null)
        outState.putInt("myId", this.my.getId());

    super.onSaveInstanceState(outState);
}

OnCreateでそのキーを取得し、必要に応じてフラグメントの状態を復元します。(少なくとも私にとっては)理解するのが難しい簡単な解決策。


私はあなたと同様の問題を抱えていますが、私はあなたの説明を完全に理解していませんが、詳細を提供できますか?フラグメントをリストに格納するアダプターも持っていますが、最近のアプリからアプリを再開すると、いくつかの切り離されたフラグメントがあるため、アプリがクラッシュします。問題はここにあるstackoverflow.com/questions/11631408/...
ゲオルギーGobozov

3
フラグメント参照での「私の」は何ですか?
Josh

このソリューションは良いアプローチではありませんがFragmentByTag、ViewPagerで使用する最も簡単な方法です。
Youngjae 2015

ここでの方法はどのようにアクセスフラグメントにタグを割り当てる内部道との互換性に依存せず:stackoverflow.com/questions/14035090/...
morgwai

26

グローバルな動作テスト済みソリューション。

getSupportFragmentManager()同じフラグメントへの参照が見つかるため、null参照が保持されることがあり、Viewページャーは新規作成しません。したがって、この使用法をgetChildFragmentManager()克服するには、問題を簡単な方法で解決します。

これを行わないでください:

new PagerAdapter(getSupportFragmentManager(), fragments);

これを行う:

new PagerAdapter(getChildFragmentManager() , fragments);


6
これは、pagerAdapterをFragment内でホストしていてインスタンス化している場合にのみ可能であり、アクティビティ内ではありません。この場合、あなたは正しいです、あなたはデフォルトでchildFragmentManagerを使うべきです。SupportFragmentManagerの使用は、デフォルトでは間違っているでしょう
Klitos G.

非常に正確です。ハックを使わずに私の問題を解決しました。ありがとう!KotlinのバージョンがFragmentStatePagerAdapter(activity!!.supportFragmentManager)、見やすくなりましたFragmentStatePagerAdapter(childFragmentManager):)
ec

7

ViewPagerでフラグメント間の相互作用を試みないでください。他のフラグメントが添付されている、または存在することさえ保証できません。アクションバーのタイトルをフラグメントから変更する代わりに、アクティビティから行うことができます。これには、標準のインターフェイスパターンを使用します。

public interface UpdateCallback
{
    void update(String name);
}

public class MyActivity extends FragmentActivity implements UpdateCallback
{
    @Override
    public void update(String name)
    {
        getSupportActionBar().setTitle(name);
    }

}

public class MyFragment extends Fragment
{
    private UpdateCallback callback;

    @Override
    public void onAttach(SupportActivity activity)
    {
        super.onAttach(activity);
        callback = (UpdateCallback) activity;
    }

    @Override
    public void onDetach()
    {
        super.onDetach();
        callback = null;
    }

    public void updateActionbar(String name)
    {
        if(callback != null)
            callback.update(name);
    }
}

確かに今すぐコードが含まれます...私はすでにそれらのメソッドをログに記録しています。onDetachは、fragmentActivityでonStopが呼び出されたときに呼び出されます。onAttachは、FragmentActivityのonCreateの直前に呼び出されます。
Maurycy

申し訳ありませんが、問題は解決しません。代わりに名前をコールバックに設定しました。フラグメントにロジックを入れることはできませんか?私のフラグメントは、アクティビティへの参照を必要とするサービスやその他の要素を起動します。私が抱えている問題を回避するには、すべてのアプリケーションロジックをメインアクティビティに移動する必要があります。これは少し不要なようです。
モーリシー

わかりましたので、これを完全にテストし、すべてのコードをコメント化しました...タイトルを変更するためのコールバックを除きます。アプリが最初に起動され、その画面に移動すると、タイトル名が変更されます。いくつかのアプリケーションをロードした後、タイトルが戻ってきても変更されません。古いアクティビティインスタンスでコールバックが受信されているようです。別の説明は思いつかない
モーリー

これをfragmentManagerとこの問題の問題に絞り込みました... code.google.com/p/android/issues/detail?id=19211。これを解決する方法はまだわかりません
Maurycy

5

ビューページャーを破棄するときにフラグメントを削除できます。私の場合onDestroyView()は、フラグメントから削除します。

@Override
public void onDestroyView() {

    if (getChildFragmentManager().getFragments() != null) {
        for (Fragment fragment : getChildFragmentManager().getFragments()) {
            getChildFragmentManager().beginTransaction().remove(fragment).commitAllowingStateLoss();
        }
    }

    super.onDestroyView();
}

ありがとう、あなたの解決策は機能します。アダプタにViewPagerも基づいている必要がありますchildFragmentManager(ではありませんfragmentManager)。また、別のバリアントも機能します。を使用せずにonDestroyViewViewPagerアダプタの作成前に子フラグメントを削除します。
CoolMind

4

同様の問題を数時間探した後、別の解決策があると思います。これは少なくとも私にとってはうまくいき、数行変更するだけで済みます。

これは私が抱えていた問題です。2つのFragmentを持つFragmentStatePagerAdapterを使用するビューページャーのアクティビティがあります。アクティビティを強制的に破棄する(開発者向けオプション)か、画面を回転させるまで、すべてが正常に機能します。メソッドgetItem内で作成された後、2つのフラグメントへの参照を保持します。

その時点でアクティビティが再度作成され、すべてがこの時点で正常に動作しますが、getItemが再度呼び出されないため、フラグメントへの参照が失われました。

これは私がFragmentStatePagerAdapterの中でその問題を修正した方法です:

    @Override
    public Object instantiateItem(ViewGroup container, int position) {
        Object aux = super.instantiateItem(container, position);

        //Update the references to the Fragments we have on the view pager
        if(position==0){
            fragTabOne = (FragOffersList)aux;
        }
        else{
            fragTabTwo = (FragOffersList) aux;
        }

        return aux;
    }

アダプターが内部ですでに参照を持っている場合は、getItemを再度呼び出すことはないため、変更しないでください。代わりに、フラグメントごとに呼び出されるこの他のメソッドinstantiateItem()を調べることで、使用されているフラグメントを取得できます。

それが誰にも役立つことを願っています。


フラグメントが多い場合はどうなりますか?それぞれの参照を保存しますか?記憶に悪いのでは?
Android開発者

それはすべて、正確に達成しようとしていることに依存します。通常、これらのビューページャーでは、一度に3つ以上のフラグメントを保持できません。真ん中にあるものと両側にあるもの。私が欲しいもののために私は本当にフラグメントへの参照が必要ですが、私は2つだけを扱う必要があることを知っています。
ランスロット2014

0

FragmentManagerはonResume()メソッドが呼び出されるとすぐにFragmentsを復元するので、フラグメントをアクティビティに呼び出して、リストに追加します。私のインスタンスでは、このすべてをPagerAdapter実装に格納しています。作成時にフラグメント引数に追加されるため、各フラグメントはその位置を認識しています。これで、特定のインデックスでフラグメントを操作する必要があるときはいつでも、アダプタのリストを使用するだけで済みます。

以下は、フラグメントがフォーカスに移動するとフラグメントを拡大し、フォーカスが外れるとフラグメントを縮小するカスタムViewPagerのアダプターの例です。ここにあるAdapterクラスとFragmentクラスの他に、親アクティビティがアダプタ変数を参照できるようにして、設定するだけです。

アダプタ

public class GrowPagerAdapter extends FragmentPagerAdapter implements OnPageChangeListener, OnScrollChangedListener {

public final String TAG = this.getClass().getSimpleName();

private final int COUNT = 4;

public static final float BASE_SIZE = 0.8f;
public static final float BASE_ALPHA = 0.8f;

private int mCurrentPage = 0;
private boolean mScrollingLeft;

private List<SummaryTabletFragment> mFragments;

public int getCurrentPage() {
    return mCurrentPage;
}

public void addFragment(SummaryTabletFragment fragment) {
    mFragments.add(fragment.getPosition(), fragment);
}

public GrowPagerAdapter(FragmentManager fm) {
    super(fm);

    mFragments = new ArrayList<SummaryTabletFragment>();
}

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

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

@Override
public void onPageScrollStateChanged(int state) {}

@Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {

    adjustSize(position, positionOffset);
}

@Override
public void onPageSelected(int position) {
    mCurrentPage = position;
}

/**
 * Used to adjust the size of each view in the viewpager as the user
 * scrolls.  This provides the effect of children scaling down as they
 * are moved out and back to full size as they come into focus.
 * 
 * @param position
 * @param percent
 */
private void adjustSize(int position, float percent) {

    position += (mScrollingLeft ? 1 : 0);
    int secondary = position + (mScrollingLeft ? -1 : 1);
    int tertiary = position + (mScrollingLeft ? 1 : -1);

    float scaleUp = mScrollingLeft ? percent : 1.0f - percent;
    float scaleDown = mScrollingLeft ? 1.0f - percent : percent;

    float percentOut = scaleUp > BASE_ALPHA ? BASE_ALPHA : scaleUp;
    float percentIn = scaleDown > BASE_ALPHA ? BASE_ALPHA : scaleDown;

    if (scaleUp < BASE_SIZE)
        scaleUp = BASE_SIZE;

    if (scaleDown < BASE_SIZE)
        scaleDown = BASE_SIZE;

    // Adjust the fragments that are, or will be, on screen
    SummaryTabletFragment current = (position < mFragments.size()) ? mFragments.get(position) : null;
    SummaryTabletFragment next = (secondary < mFragments.size() && secondary > -1) ? mFragments.get(secondary) : null;
    SummaryTabletFragment afterNext = (tertiary < mFragments.size() && tertiary > -1) ? mFragments.get(tertiary) : null;

    if (current != null && next != null) {

        // Apply the adjustments to each fragment
        current.transitionFragment(percentIn, scaleUp);
        next.transitionFragment(percentOut, scaleDown);

        if (afterNext != null) {
            afterNext.transitionFragment(BASE_ALPHA, BASE_SIZE);
        }
    }
}

@Override
public void onScrollChanged(int l, int t, int oldl, int oldt) {

    // Keep track of which direction we are scrolling
    mScrollingLeft = (oldl - l) < 0;
}
}

断片

public class SummaryTabletFragment extends BaseTabletFragment {

public final String TAG = this.getClass().getSimpleName();

private final float SCALE_SIZE = 0.8f;

private RelativeLayout mBackground, mCover;
private TextView mTitle;
private VerticalTextView mLeft, mRight;

private String mTitleText;
private Integer mColor;

private boolean mInit = false;
private Float mScale, mPercent;

private GrowPagerAdapter mAdapter;
private int mCurrentPosition = 0;

public String getTitleText() {
    return mTitleText;
}

public void setTitleText(String titleText) {
    this.mTitleText = titleText;
}

public static SummaryTabletFragment newInstance(int position) {

    SummaryTabletFragment fragment = new SummaryTabletFragment();
    fragment.setRetainInstance(true);

    Bundle args = new Bundle();
    args.putInt("position", position);
    fragment.setArguments(args);

    return fragment;
}

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
    super.onCreateView(inflater, container, savedInstanceState);

    mRoot = inflater.inflate(R.layout.tablet_dummy_view, null);

    setupViews();
    configureView();

    return mRoot;
}

@Override
public void onViewStateRestored(Bundle savedInstanceState) {
    super.onViewStateRestored(savedInstanceState);

    if (savedInstanceState != null) {
        mColor = savedInstanceState.getInt("color", Color.BLACK);
    }

    configureView();
}

@Override
public void onSaveInstanceState(Bundle outState)  {

    outState.putInt("color", mColor);

    super.onSaveInstanceState(outState);
}

@Override
public int getPosition() {
    return getArguments().getInt("position", -1);
}

@Override
public void setPosition(int position) {
    getArguments().putInt("position", position);
}

public void onResume() {
    super.onResume();

    mAdapter = mActivity.getPagerAdapter();
    mAdapter.addFragment(this);
    mCurrentPosition = mAdapter.getCurrentPage();

    if ((getPosition() == (mCurrentPosition + 1) || getPosition() == (mCurrentPosition - 1)) && !mInit) {
        mInit = true;
        transitionFragment(GrowPagerAdapter.BASE_ALPHA, GrowPagerAdapter.BASE_SIZE);
        return;
    }

    if (getPosition() == mCurrentPosition && !mInit) {
        mInit = true;
        transitionFragment(0.00f, 1.0f);
    }
}

private void setupViews() {

    mCover = (RelativeLayout) mRoot.findViewById(R.id.cover);
    mLeft = (VerticalTextView) mRoot.findViewById(R.id.title_left);
    mRight = (VerticalTextView) mRoot.findViewById(R.id.title_right);
    mBackground = (RelativeLayout) mRoot.findViewById(R.id.root);
    mTitle = (TextView) mRoot.findViewById(R.id.title);
}

private void configureView() {

    Fonts.applyPrimaryBoldFont(mLeft, 15);
    Fonts.applyPrimaryBoldFont(mRight, 15);

    float[] size = UiUtils.getScreenMeasurements(mActivity);
    int width = (int) (size[0] * SCALE_SIZE);
    int height = (int) (size[1] * SCALE_SIZE);

    RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(width, height);
    mBackground.setLayoutParams(params);

    if (mScale != null)
        transitionFragment(mPercent, mScale);

    setRandomBackground();

    setTitleText("Fragment " + getPosition());

    mTitle.setText(getTitleText().toUpperCase());
    mLeft.setText(getTitleText().toUpperCase());
    mRight.setText(getTitleText().toUpperCase());

    mLeft.setOnClickListener(new OnClickListener() {

        @Override
        public void onClick(View v) {

            mActivity.showNextPage();
        }
    });

    mRight.setOnClickListener(new OnClickListener() {

        @Override
        public void onClick(View v) {

            mActivity.showPrevPage();
        }
    });
}

private void setRandomBackground() {

    if (mColor == null) {
        Random r = new Random();
        mColor = Color.rgb(r.nextInt(255), r.nextInt(255), r.nextInt(255));
    }

    mBackground.setBackgroundColor(mColor);
}

public void transitionFragment(float percent, float scale) {

    this.mScale = scale;
    this.mPercent = percent;

    if (getView() != null && mCover != null) {

        getView().setScaleX(scale);
        getView().setScaleY(scale);

        mCover.setAlpha(percent);
        mCover.setVisibility((percent <= 0.05f) ? View.GONE : View.VISIBLE);
    }
}

@Override
public String getFragmentTitle() {
    return null;
}
}

0

私の解決策:私はほとんどすべてのビューを static。これで私のアプリは完璧に相互作用します。どこからでも静的メソッドを呼び出すことができるのは良いスタイルではないかもしれませんが、機能しないコードをいじってみませんか?私はここで多くの質問とその回答をSOで読みましたが、解決策はありませんでした(私にとって)。

私はそれがメモリをリークし、ヒープを浪費する可能性があることを知っています、そして私のコードは他のプロジェクトに適合しませんが、私はこれについて怖くはありません-私はさまざまなデバイスと条件でアプリをテストしました、まったく問題なく、Androidプラットフォームはこれを処理できるようです。UIは毎秒更新され、S2 ICS(4.0.3)デバイスでも、アプリは数千のジオマーカーを処理できます。


0

私は同じ問題に直面しましたが、私のViewPagerは、使用してアダプターを作成および設定するTopFragment内にありました setAdapter(new FragmentPagerAdapter(getChildFragmentManager()))

このonAttachFragment(Fragment childFragment)ようなTopFragmentをオーバーライドすることで、この問題を修正しました。

@Override
public void onAttachFragment(Fragment childFragment) {
    if (childFragment instanceof OnboardingDiamondsFragment) {
        mChildFragment = (ChildFragment) childFragment;
    }

    super.onAttachFragment(childFragment);
}

すでに知られているように(上記の回答を参照)、childFragmentManagerが自身を再作成すると、viewPager内にあったフラグメントも作成されます。
重要な部分は、その後、彼はonAttachFragmentを呼び出すことですです。これで、新しく再作成されたフラグメントへの参照ができました。

これが私のようなこの古いQを手に入れるのに役立つことを願っています:)


0

SparceArrayにフラグメントを保存することで問題を解決しました:

public abstract class SaveFragmentsPagerAdapter extends FragmentPagerAdapter {

    SparseArray<Fragment> fragments = new SparseArray<>();

    public SaveFragmentsPagerAdapter(FragmentManager fm) {
        super(fm);
    }

    @Override
    public Object instantiateItem(ViewGroup container, int position) {
        Fragment fragment = (Fragment) super.instantiateItem(container, position);
        fragments.append(position, fragment);
        return fragment;
    }

    @Nullable
    public Fragment getFragmentByPosition(int position){
        return fragments.get(position);
    }

}

0

ちょうどあなたが知っているので...

これらのクラスの不平不満に加えて、共有する価値があるかなり興味深いバグがあります。

ViewPagerを使用してアイテムのツリーを移動しています(アイテムを選択すると、ビューページャーが右にスクロールしてアニメーション表示され、次のブランチが表示されて戻り、ViewPagerが反対方向にスクロールして前のノードに戻ります)。 。

FragmentStatePagerAdapterの最後からフラグメントをプッシュしてポップすると問題が発生します。アイテムが変更されたことに気付くほどスマートであり、アイテムが変更されたときにフラグメントを作成して置き換えるのに十分スマートです。しかし、フラグメントの状態を破棄するほどスマートではないか、またはアダプターのサイズが変更されたときに内部的に保存されたフラグメントの状態をトリミングするほどスマートではありません。したがって、アイテムをポップし、新しいアイテムを最後にプッシュすると、新しいアイテムのフラグメントは古いアイテムのフラグメントの保存された状態を取得し、コードに絶対的な混乱を引き起こしました。私のフラグメントは、インターネットから再フェッチするために多くの作業を必要とする可能性のあるデータを運ぶため、状態を保存しないことは実際には選択肢ではありませんでした。

明確な回避策はありません。私はこのようなものを使いました:

  public void onSaveInstanceState(Bundle outState) {
    IFragmentListener listener = (IFragmentListener)getActivity();
    if (listener!= null)
    {
        if (!listener.isStillInTheAdapter(this.getAdapterItem()))
        {
            return; // return empty state.
        }

    }
    super.onSaveInstanceState(outState);

    // normal saving of state for flips and 
    // paging out of the activity follows
    ....
  }

新しいフラグメントインスタンスは、savedState Bundleを引き続き取得しますが、少なくとも古いデータを保持しないため、不完全なソリューションです。


0

人々はコメントを読む傾向がないので、ここに私がここに書いものをほとんど複製する答えがあります

この問題の根本的な原因は、AndroidシステムgetItemが実際に表示されているフラグメントを取得するために呼び出さないという事実ですinstantiateItem。このメソッドは最初に、の特定のタブのフラグメントインスタンスを検索して再利用しようとしますFragmentManager。この検索が失敗した場合にのみ(これは、FragmentManagerが新しく作成されたときに初めて発生します)、次にgetItem呼び出されます。たとえば、ユーザーがデバイスを回転させるたびに(重い可能性がある)フラグメントを再作成しないのは明らかな理由があります。
これを解決するにFragment.instantiateは、アクティビティででフラグメントを作成する代わりに、それぞれのコンストラクターを使用してフラグメントが実際に作成される場所で行う必要があります。pagerAdapter.instantiateItemこれらの呼び出しをすべて、startUpdate/finishUpdateフラグメントトランザクションを開始/コミットするメソッド呼び出しで囲む必要があります。getItem

List<Fragment> fragments = new Vector<Fragment>();

@Override protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.myLayout);
        ViewPager viewPager = (ViewPager) findViewById(R.id.myViewPager);
        MyPagerAdapter adapter = new MyPagerAdapter(getSupportFragmentManager());
        viewPager.setAdapter(adapter);
        ((TabLayout) findViewById(R.id.tabs)).setupWithViewPager(viewPager);

        adapter.startUpdate(viewPager);
        fragments.add(adapter.instantiateItem(viewPager, 0));
        fragments.add(adapter.instantiateItem(viewPager, 1));
        // and so on if you have more tabs...
        adapter.finishUpdate(viewPager);
}

class MyPagerAdapter extends FragmentPagerAdapter {

        public MyPagerAdapter(FragmentManager manager) {super(manager);}

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

        @Override public Fragment getItem(int position) {
            if (position == 0) return new Fragment0();
            if (position == 1) return new Fragment1();
            return null;  // or throw some exception
        }

        @Override public CharSequence getPageTitle(int position) {
            if (position == 0) return getString(R.string.tab0);
            if (position == 1) return getString(R.string.tab1);
            return null;  // or throw some exception
        }
}
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.