1. ホーム
  2. android

[解決済み] FragmentPagerAdapterを使用した場合の既存のフラグメントの取得方法

2022-10-23 04:45:18

質問

フラグメント同士が通信できるようにするために Activity を使用しています。 FragmentPagerAdapter を接続する際のタブの管理およびすべての詳細を実装するヘルパークラスとして使用されます。 ViewPager と関連付けられた TabHost . 私が実装したのは FragmentPagerAdapter は、Android のサンプルプロジェクトで提供されているのと同じように サポート4Demo .

主な疑問は、特定のフラグメントを FragmentManager から特定のフラグメントを取得することができますか? FragmentPagerAdapter はフラグメントを作成し、IdとTagを自動生成します。

どのように解決するのですか?

問題の概要

注意: この回答では、参照する FragmentPagerAdapter とそのソースコードを参照するつもりです。しかし、一般的な解決策は FragmentStatePagerAdapter .

これを読んでいる人は、おそらくすでに知っていると思いますが FragmentPagerAdapter / FragmentStatePagerAdapter を作成するためのものです。 Fragments を作成するためのものです。 ViewPager が、アクティビティ再作成時(デバイスの回転や、システムがアプリを終了させてメモリを回復させるなど)には、これらの Fragments は再び作成されることはなく、代わりにその から取得されたインスタンスです。 FragmentManager . では、あなたの Activity への参照を取得する必要があるとします。 Fragments への参照を得る必要があります。あなたは id または tag これらの作成された Fragments というのも FragmentPagerAdapter 内部で設定する . そこで問題は、その情報なしにどうやってそれらへの参照を得るかです...。

現在の解決策の問題点:内部コードに依存すること

この質問と似たような質問で私が見た多くの解決策は、既存の Fragment を呼び出すことによって FragmentManager.findFragmentByTag() を模倣し という内部で作成されたタグを模倣しています。 "android:switcher:" + viewId + ":" + id . この問題は、内部のソースコードに依存していることです。ご存知のように、ソースコードは永遠に同じであることが保証されているわけではありません。Google の Android エンジニアは、このソース コードを変更することを簡単に決定できます。 tag 構造を変更することを決定し、その結果、既存の Fragments .

内部に依存しない代替案 tag

ここでは、簡単な例として、参照先が Fragments が返す FragmentPagerAdapter に依存しない、内部の tags に設定されている Fragments . 重要なのは instantiateItem() で、そこにリファレンスを保存します。 の代わりに の代わりに 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 を取得することもできます。 tags で設定された FragmentPagerAdapter を同じように設定します。 注意: これは FragmentStatePagerAdapter を設定しないので、これは tags を設定しないからです。 Fragments .

@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...
}