109

タブの管理と関連するとの接続のすべての詳細を実装するヘルパークラスとして、Activityを使用しているを介してフラグメントを相互に通信させるのに問題があります。AndroidサンプルプロジェクトSupport4Demosによって提供されるものとまったく同じように実装しました。FragmentPagerAdapterViewPagerTabHostFragmentPagerAdapter

FragmentManager主な質問は、IDもタグも持っていないときに特定のフラグメントを取得するにはどうすればよいですか?FragmentPagerAdapterフラグメントを作成し、IDとタグを自動生成しています。

4

14 に答える 14

207

問題の概要

注: この回答ではFragmentPagerAdapter、そのソース コードを参照します。しかし、一般的な解決策は にも適用されFragmentStatePagerAdapterます。

これを読んでいる場合は、FragmentPagerAdapter/が のために作成するFragmentStatePagerAdapterことを意図していることをおそらく既に知っているでしょうが、アクティビティの再作成時に (デバイスのローテーションまたはシステムがアプリを強制終了してメモリを回復するため)、これらは再度作成されることはありませんが、代わりにそのから取得したインスタンス。ここで、作業を行うためにこれらへの参照を取得する必要があるとします。内部で設定されているため、作成されたこれらのまたははありません。問題は、その情報なしでそれらへの参照を取得する方法です...FragmentsViewPagerFragmentsFragmentManagerActivityFragmentsidtagFragmentsFragmentPagerAdapter

現在のソリューションの問題: 内部コードに依存している

this および同様の質問で私が見た多くの解決策は、内部で作成されたタグFragmentを呼び出しFragmentManager.findFragmentByTag()て模倣することにより、既存のものへの参照を取得することに依存しています: 。これに関する問題は、内部ソース コードに依存していることです。これは、誰もが知っているように、永久に同じままであるとは限りません。Google の Android エンジニアは、既存の."android:switcher:" + viewId + ":" + idtagFragments

内部に依存しない代替ソリューションtag

の内部セットに依存しないFragmentsによって返されるへの参照を取得する方法の簡単な例を次に示します。重要なのは、 inではなくそこに参照をオーバーライドして保存することです。FragmentPagerAdaptertagsFragmentsinstantiateItem()getItem()

public class SomeActivity extends Activity {
    private FragmentA m1stFragment;
    private FragmentB m2ndFragment;

    // other code in your Activity...

    private class CustomPagerAdapter extends FragmentPagerAdapter {
        // other code in your custom FragmentPagerAdapter...

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

        @Override
        public Fragment getItem(int position) {
            // Do NOT try to save references to the Fragments in getItem(),
            // because getItem() is not always called. If the Fragment
            // was already created then it will be retrieved from the FragmentManger
            // and not here (i.e. getItem() won't be called again).
            switch (position) {
                case 0:
                    return new FragmentA();
                case 1:
                    return new FragmentB();
                default:
                    // This should never happen. Always account for each position above
                    return null;
            }
        }

        // Here we can finally safely save a reference to the created
        // Fragment, no matter where it came from (either getItem() or
        // FragmentManger). Simply save the returned Fragment from
        // super.instantiateItem() into an appropriate reference depending
        // on the ViewPager position.
        @Override
        public Object instantiateItem(ViewGroup container, int position) {
            Fragment createdFragment = (Fragment) super.instantiateItem(container, position);
            // save the appropriate reference depending on position
            switch (position) {
                case 0:
                    m1stFragment = (FragmentA) createdFragment;
                    break;
                case 1:
                    m2ndFragment = (FragmentB) createdFragment;
                    break;
            }
            return createdFragment;
        }
    }

    public void someMethod() {
        // do work on the referenced Fragments, but first check if they
        // even exist yet, otherwise you'll get an NPE.

        if (m1stFragment != null) {
            // m1stFragment.doWork();
        }

        if (m2ndFragment != null) {
            // m2ndFragment.doSomeWorkToo();
        }
    }
}

またはtags、クラス メンバー変数/への参照の代わりに作業したい場合は、同じ方法でセットをFragments取得することもできます。tagsFragmentPagerAdapterFragmentStatePagerAdaptertagsFragments

@Override
public Object instantiateItem(ViewGroup container, int position) {
    Fragment createdFragment = (Fragment) super.instantiateItem(container, position);
    // get the tags set by FragmentPagerAdapter
    switch (position) {
        case 0:
            String firstTag = createdFragment.getTag();
            break;
        case 1:
            String secondTag = createdFragment.getTag();
            break;
    }
    // ... save the tags somewhere so you can reference them later
    return createdFragment;
}

このメソッドは、内部tagセットの模倣に依存せずFragmentPagerAdapter、代わりにそれらを取得するために適切な API を使用することに注意してください。これtagにより、 の将来のバージョンで変更が加えられた場合SupportLibraryでも、安全を保つことができます。


の設計に応じて、Activity作業Fragmentsしようとしている がまだ存在する場合と存在しない場合があることを忘れないnullでください。そのため、参照を使用する前にチェックを行ってそのことを考慮する必要があります。

また、代わりにを使用している場合はFragmentStatePagerAdapter、 へのハード参照を保持したくありませんFragments。それらのハード参照が多数ある可能性があり、ハード参照はそれらを不必要にメモリに保持するからです。Fragment代わりに、参照を標準の変数ではなく変数に保存しWeakReferenceます。このような:

WeakReference<Fragment> m1stFragment = new WeakReference<Fragment>(createdFragment);
// ...and access them like so
Fragment firstFragment = m1stFragment.get();
if (firstFragment != null) {
    // reference hasn't been cleared yet; do work...
}
于 2015-03-26T01:23:30.640 に答える
86

次の投稿に基づいて、私の質問に対する答えを見つけました:fragmentpageradapterでフラグメントを再利用する

私が学んだことはほとんどありません:

  1. getItem(int position)FragmentPagerAdapterこのメソッドが実際に行うことのかなり誤解を招く名前です。既存のフラグメントを返すのではなく、新しいフラグメントを作成します。createItem(int position)その意味で、メソッドの名前をAndroid SDK のような名前に変更する必要があります。したがって、この方法はフラグメントの取得には役立ちません。
  2. 投稿サポート FragmentPagerAdapterholdsの古いフラグメントへの参照の説明に基づいて、フラグメントの作成をに任せる必要がありFragmentPagerAdapterます。つまり、フラグメントまたはそのタグへの参照がありません。ただし、フラグメント タグがある場合は、FragmentManagerを呼び出して から簡単に参照を取得できますfindFragmentByTag()。特定のページ位置にあるフラグメントのタグを見つける方法が必要です。

解決

次のヘルパー メソッドをクラスに追加して、フラグメント タグを取得し、findFragmentByTag()メソッドに送信します。

private String getFragmentTag(int viewPagerId, int fragmentPosition)
{
     return "android:switcher:" + viewPagerId + ":" + fragmentPosition;
}

ノート!FragmentPagerAdapterこれは、新しいフラグメントを作成するときに使用する方法と同じです。このリンクを参照してくださいhttp://code.google.com/p/openintents/source/browse/trunk/compatibility/AndroidSupportV2/src/android/support/v2/app/FragmentPagerAdapter.java#104

于 2012-12-26T10:40:08.550 に答える
10

現在のフラグメントへの参照を取得するために機能するこのメソッドを作成しました。

public static Fragment getCurrentFragment(ViewPager pager, FragmentPagerAdapter adapter) {
    try {
        Method m = adapter.getClass().getSuperclass().getDeclaredMethod("makeFragmentName", int.class, long.class);
        Field f = adapter.getClass().getSuperclass().getDeclaredField("mFragmentManager");
        f.setAccessible(true);
        FragmentManager fm = (FragmentManager) f.get(adapter);
        m.setAccessible(true);
        String tag = null;
        tag = (String) m.invoke(null, pager.getId(), (long) pager.getCurrentItem());
        return fm.findFragmentByTag(tag);
    } catch (NoSuchMethodException e) {
        e.printStackTrace();
    } catch (IllegalArgumentException e) {
        e.printStackTrace();
    } catch (IllegalAccessException e) {
        e.printStackTrace();
    } catch (InvocationTargetException e) {
        e.printStackTrace();
    } catch (NoSuchFieldException e) {
        e.printStackTrace();
    } 
    return null;
}
于 2013-03-14T11:31:49.080 に答える
2

@ personne3000 によって提案された解決策は素晴らしいですが、1 つの問題があります: アクティビティがバックグラウンドに移動し、(空きメモリを取得するために) システムによって強制終了されてから復元さfragmentReferencesれると、 は空にgetItemなります。呼ばれた。

以下のクラスは、このような状況を処理します。

public abstract class AbstractHolderFragmentPagerAdapter<F extends Fragment> extends FragmentPagerAdapter {

    public static final String FRAGMENT_SAVE_PREFIX = "holder";
    private final FragmentManager fragmentManager; // we need to store fragment manager ourselves, because parent's field is private and has no getters.

    public AbstractHolderFragmentPagerAdapter(FragmentManager fm) {
        super(fm);
        fragmentManager = fm;
    }

    private SparseArray<WeakReference<F>> holder = new SparseArray<WeakReference<F>>();

    protected void holdFragment(F fragment) {
        holdFragment(holder.size(), fragment);
    }

    protected void holdFragment(int position, F fragment) {
        if (fragment != null)
            holder.put(position, new WeakReference<F>(fragment));
    }

    public F getHoldedItem(int position) {
        WeakReference<F> ref = holder.get(position);
        return ref == null ? null : ref.get();
    }

    public int getHolderCount() {
        return holder.size();
    }

    @Override
    public void restoreState(Parcelable state, ClassLoader loader) { // code inspired by Google's FragmentStatePagerAdapter implementation
        super.restoreState(state, loader);
        Bundle bundle = (Bundle) state;
        for (String key : bundle.keySet()) {
            if (key.startsWith(FRAGMENT_SAVE_PREFIX)) {
                int index = Integer.parseInt(key.substring(FRAGMENT_SAVE_PREFIX.length()));
                Fragment f = fragmentManager.getFragment(bundle, key);
                holdFragment(index, (F) f);
            }
        }
    }

    @Override
    public Parcelable saveState() {
        Bundle state = (Bundle) super.saveState();
        if (state == null)
            state = new Bundle();

        for (int i = 0; i < holder.size(); i++) {
            int id = holder.keyAt(i);
            final F f = getHoldedItem(i);
            String key = FRAGMENT_SAVE_PREFIX + id;
            fragmentManager.putFragment(state, key, f);
        }
        return state;
    }
}
于 2014-08-27T08:34:30.670 に答える
2

フラグメントへのハンドルを取得する際の主な障害は、getItem() に依存できないことです。方向の変更後、フラグメントへの参照は null になり、getItem() は再度呼び出されません。

タグを取得するために FragmentPagerAdapter の実装に依存しないアプローチを次に示します。getItem() から作成されたフラグメント、またはフラグメント マネージャーから見つかったフラグメントを返す instantiateItem() をオーバーライドします。

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

    if (position == 0) {
        someFragment = (SomeFragment) value;
    } else if (position == 1) {
        anotherFragment = (AnotherFragment) value;
    }

    return value;
}
于 2015-06-22T20:16:21.040 に答える
0

FragmentPagerAdapter からフラグメントを返す方法については、この投稿を参照してください。フラグメントのインデックスを知っていることに依存しますが、これは getItem() で設定されます (インスタンス化時のみ)

于 2013-01-22T20:22:34.110 に答える
0

これが最善のアプローチかどうかはわかりませんが、他に何もうまくいきませんでした。getActiveFragment を含む他のすべてのオプションは、null を返すか、アプリをクラッシュさせました。

画面の回転でフラグメントがアタッチされていることに気付いたので、それを使用してフラグメントをアクティビティに送り返しました。

フラグメント内:

@Override
public void onAttach(Activity activity) {
    super.onAttach(activity);
    try {
        mListener = (OnListInteractionListener) activity;
        mListener.setListFrag(this);
    } catch (ClassCastException e) {
        throw new ClassCastException(activity.toString()
                + " must implement OnFragmentInteractionListener");
    }
}

次に、アクティビティで:

@Override
public void setListFrag(MyListFragment lf) {
    if (mListFragment == null) {
        mListFragment = lf;
    }
}

そして最後にアクティビティ onCreate() で:

if (savedInstanceState != null) {
    if (mListFragment != null)
        mListFragment.setListItems(items);
}

このアプローチでは、新しいフラグメントを作成せずに、実際に表示されるフラグメントをアクティビティにアタッチします。

于 2017-02-17T16:54:37.257 に答える
-6

このコードを試してみてください。

public class MYFragmentPAdp extends FragmentPagerAdapter {

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

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

     @Override
     public Fragment getItem(int position) {
         if (position == 0)
             Fragment fragment = new Fragment1();
         else (position == 1)
             Fragment fragment = new Fragment2();
         return fragment;
     }
}
于 2012-12-26T11:03:21.963 に答える