web-dev-qa-db-ja.com

ViewPagerからフラグメントを取得する

私は3つの異なるフラグメントをホストするためにViewPagerFragmentStatePagerAdapterと共に使用しています。

  • [フラグメント1]
  • [フラグメント2]
  • [フラグメント3]

ViewPagerFragmentActivityからFragment1を取得したいとき。

問題は何ですか、どうすれば修正できますか?

235
Qun Qin

主な答えは、フレームワークによって生成されている名前に依存しています。これが変わっても、それはもはや機能しません。

instantiateItem()destroyItem()Fragment(State)PagerAdapterをオーバーライドして、この解決策はどうでしょうか。

public class MyPagerAdapter extends FragmentStatePagerAdapter {
    SparseArray<Fragment> registeredFragments = new SparseArray<Fragment>();

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

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

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

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

    @Override
    public void destroyItem(ViewGroup container, int position, Object object) {
        registeredFragments.remove(position);
        super.destroyItem(container, position, object);
    }

    public Fragment getRegisteredFragment(int position) {
        return registeredFragments.get(position);
    }
}

利用可能なフラグメントを扱うとき、これは私のために働くようです。まだインスタンス化されていないフラグメントは、getRegisteredFragmentを呼び出すときにnullを返します。しかし、これを使って現在のFragmentViewPageradapater.getRegisteredFragment(viewPager.getCurrentItem())から取得していますが、これはnullを返しません。

私はこの解決策の他のどんな欠点も知らない。もしあれば、私は知りたいのですが。

496

ViewPagerからフラグメントを取得する場合、ここおよび他の関連SOスレッド/ブログに関する多くの回答があります。私が見たすべての人は壊れており、一般的に以下にリストされている2つのタイプのいずれかに分類されるようです。 this このスレッドの他の答えのように、現在のフラグメントのみを取得したい場合、他の有効なソリューションがいくつかあります。

FragmentPagerAdapterを使用する場合は、以下を参照してください。 FragmentStatePagerAdapterを使用する場合、 this を見る価値があります。 FragmentStateAdapterの現在のインデックスではないインデックスを取得することは、その性質上、これらがビューの外に出たり、offScreenLimitの境界から外れたりするため、完全に破棄されるほど有用ではありません。

不幸な道

間違った:独自のフラグメントの内部リストを維持し、FragmentPagerAdapter.getItem()が呼び出されたときに追加されます

  • 通常、SparseArrayまたはMapを使用します
  • ライフサイクルイベントの説明で見た多くの例の1つではないため、このソリューションは脆弱です。 getItemは、ViewPagerで最初にページがスクロールされる(またはViewPager.setOffscreenPageLimit(x)> 0の場合に取得される)ときにのみ呼び出されるため、ホストのActivity/Fragmentが削除または再起動されると、カスタムFragmentPagerActivityが再作成されたときに内部SpaseArrayが消去されます、しかし背後でViewPagers内部フラグメントが再作成され、getItemではなくのいずれのインデックスに対しても呼び出されるため、インデックスからフラグメントを取得する機能は失われます永遠に。 FragmentManager.getFragment()およびputFragmentを介してこれらのフラグメント参照を保存および復元することにより、これを説明できますが、これは面倒なIMHOになり始めます。

間違った:FragmentPagerAdapterの内部で使用されているものと一致する独自のタグIDを構築し、これを使用してFragmentManagerからページフラグメントを取得します。

  • これは、最初の内部配列ソリューションの損失フラグメント参照問題に対処するので、より不安全ですが、上記の答えやネット上の他の場所で正しく指摘されているように、 private ViewPagerの内部メソッドで、いつでも、またはどのOSバージョンでも変更できます。

このソリューション用に再作成されたメソッドは

private static String makeFragmentName(int viewId, long id) {
    return "Android:switcher:" + viewId + ":" + id;
}

幸せなパス:ViewPager.instantiateItem()

上記のgetItem()と同様のアプローチですが、ライフサイクルを中断しないは、これがinstantiateItem()の代わりにgetItem()にフックすることです。 この回答 を参照してください

幸せなパス:独自のFragmentViewPager

最新のサポートライブラリのソース から独自のFragmentViewPagerクラスを構築し、フラグメントタグを生成するために内部的に使用されるメソッドを変更します。以下に置き換えることができます。これには、タグの作成が変更されることはなく、常に危険なプライベートAPI /メソッドに依存しないという利点があります。

/**
 * @param containerViewId the ViewPager this adapter is being supplied to
 * @param id pass in getItemId(position) as this is whats used internally in this class
 * @return the tag used for this pages fragment
 */
public static String makeFragmentName(int containerViewId, long id) {
    return "Android:switcher:" + containerViewId + ":" + id;
}

次に、ドキュメントにあるように、インデックスに使用されるフラグメントを取得する場合は、このメソッドのようなもの(カスタムFragmentPagerAdapterまたはサブクラスに配置できます)を呼び出すだけで、結果がnull getItemがそのページに対してまだ呼び出されていない場合、つまりまだ作成されていない場合。

/**
 * @return may return null if the fragment has not been instantiated yet for that position - this depends on if the fragment has been viewed
 * yet OR is a sibling covered by {@link Android.support.v4.view.ViewPager#setOffscreenPageLimit(int)}. Can use this to call methods on
 * the current positions fragment.
 */
public @Nullable Fragment getFragmentForPosition(int position)
{
    String tag = makeFragmentName(mViewPager.getId(), getItemId(position));
    Fragment fragment = getSupportFragmentManager().findFragmentByTag(tag);
    return fragment;
}

これはシンプルなソリューションであり、ウェブ上のあらゆる場所にある他の2つのソリューションの問題を解決します

85
Dori

FragmentPagerAdapterに次のメソッドを追加します。

public Fragment getActiveFragment(ViewPager container, int position) {
String name = makeFragmentName(container.getId(), position);
return  mFragmentManager.findFragmentByTag(name);
}

private static String makeFragmentName(int viewId, int index) {
    return "Android:switcher:" + viewId + ":" + index;
}

getActiveFragment(0)が機能しなければなりません。

これがViewPagerに実装されたソリューションです https://Gist.github.com/jacek-marchwicki/d6320ba9a910c514424d 。何かが失敗するなら、あなたは良いクラッシュログを見るでしょう。

70
kzotin

もう一つの簡単な解決策:

    public class MyPagerAdapter extends FragmentPagerAdapter {
        private Fragment mCurrentFragment;

        public Fragment getCurrentFragment() {
            return mCurrentFragment;
        }
//...    
        @Override
        public void setPrimaryItem(ViewGroup container, int position, Object object) {
            if (getCurrentFragment() != object) {
                mCurrentFragment = ((Fragment) object);
            }
            super.setPrimaryItem(container, position, object);
        }
    }
38
Mindphaser

これにはいくつかの答えがありますが、おそらくこれは誰かを助けるでしょう。 FragmentからViewPagerを取得する必要がある場合、比較的簡単なソリューションを使用しました。 Activityを保持しているFragmentまたはViewPagerで、このコードを使用して、保持しているすべてのFragmentを循環できます。

FragmentPagerAdapter fragmentPagerAdapter = (FragmentPagerAdapter) mViewPager.getAdapter();
for(int i = 0; i < fragmentPagerAdapter.getCount(); i++) {
    Fragment viewPagerFragment = fragmentPagerAdapter.getItem(i);
    if(viewPagerFragment != null) {
        // Do something with your Fragment
        // Check viewPagerFragment.isResumed() if you intend on interacting with any views.
    }
}
  • Fragment内のViewPagerの位置がわかっている場合は、getItem(knownPosition)を呼び出すだけです。

  • FragmentでのViewPagerの位置がわからない場合は、getUniqueId()などのメソッドを使用して、子供にFragmentsにインターフェースを実装させ、それを使用してそれらを区別できます。または、すべてのFragmentsを循環して、if(viewPagerFragment instanceof FragmentClassYouWant)などのクラスタイプを確認できます。

!!!編集!!!

getItemは、各FragmentPagerAdapter初回に作成する必要がある場合にのみFragmentによって呼び出されることを発見しました。その後、FragmentsがリサイクルされるようですFragmentManagerを使用します。このように、FragmentPagerAdapterの多くの実装は、FragmentgetItemsを作成しますnew。上記の方法を使用すると、Fragmentのすべての項目を処理するときにgetItemが呼び出されるたびに、新しいFragmentPagerAdaptersが作成されます。このため、代わりにFragmentManagerを使用して、それぞれのFragmentを取得する(受け入れられた答えを使用して)より良いアプローチを見つけました。これはより完全なソリューションであり、私にとってはうまく機能しています。

FragmentPagerAdapter fragmentPagerAdapter = (FragmentPagerAdapter) mViewPager.getAdapter();
for(int i = 0; i < fragmentPagerAdapter.getCount(); i++) {
    String name = makeFragmentName(mViewPager.getId(), i);
    Fragment viewPagerFragment = getChildFragmentManager().findFragmentByTag(name);
    // OR Fragment viewPagerFragment = getFragmentManager().findFragmentByTag(name);
    if(viewPagerFragment != null) {

        // Do something with your Fragment
        if (viewPagerFragment.isResumed()) {
            // Interact with any views/data that must be alive
        }
        else {
            // Flag something for update later, when this viewPagerFragment
            // returns to onResume
        }
    }
}

そして、このメソッドが必要になります。

private static String makeFragmentName(int viewId, int position) {
    return "Android:switcher:" + viewId + ":" + position;
}
21
Steven Byle

私の場合、上記の解決策はどれもうまくいきませんでした。

ただし、フラグメント内でChild Fragment Managerを使用しているため、次のものが使用されました。

Fragment f = getChildFragmentManager().getFragments().get(viewPager.getCurrentItem());

これは、マネージャ内のあなたのフラグメントがviewpagerアイテムに対応している場合にのみ機能します。

10
Veedka

ViewPagerからの現在のVisibleフラグメントを取得するため。私はこの単純な文を使用していて、それはうまく機能しています。

      public Fragment getFragmentFromViewpager()  
      {
         return ((Fragment) (mAdapter.instantiateItem(mViewPager, mViewPager.getCurrentItem())));
      }
6
Xar E Ahmer

これは上記のStevenの回答に基づいています。これは、親アクティビティにすでにアタッチされているフラグメントの実際のインスタンスを返します。

FragmentPagerAdapter fragmentPagerAdapter = (FragmentPagerAdapter) mViewPager.getAdapter();
    for(int i = 0; i < fragmentPagerAdapter.getCount(); i++) {

        Fragment viewPagerFragment = (Fragment) mViewPager.getAdapter().instantiateItem(mViewPager, i);
        if(viewPagerFragment != null && viewPagerFragment.isAdded()) {

            if (viewPagerFragment instanceof FragmentOne){
                FragmentOne oneFragment = (FragmentOne) viewPagerFragment;
                if (oneFragment != null){
                    oneFragment.update(); // your custom method
                }
            } else if (viewPagerFragment instanceof FragmentTwo){
                FragmentTwo twoFragment = (FragmentTwo) viewPagerFragment;

                if (twoFragment != null){
                    twoFragment.update(); // your custom method
                }
            }
        }
    }
3
OrangeTree

ちょっと私はこの質問に答えました ここ 。基本的には、上書きする必要があります

public Object instantiateItem(ViewGroupコンテナ、int位置)

fragmentStatePagerAdapterのメソッド。

2
Utkarsh Saxena

Fragment内にホストされているViewPagerの参照を取得するには、後の段階でdon'tgetItem()またはその他のメソッドを呼び出す必要があります。 Fragment内のデータを更新したい場合は、次の方法を使用します。 ViewPagerを動的に更新しますか?

重要なのは、Adaper内に新しいデータを設定してnotifyDataSetChanged()を呼び出すことです。これにより、getItemPosition()が呼び出され、Fragmentの参照が渡され、それを更新することができます。他のすべての方法では、自分自身または他の適切な解決策ではないハックを参照する必要があります。

@Override
public int getItemPosition(Object object) {
    if (object instanceof UpdateableFragment) {
        ((UpdateableFragment) object).update(xyzData);
    }
    //don't return POSITION_NONE, avoid fragment recreation. 
    return super.getItemPosition(object);
}
2
M-WaJeEh

最初に使用しようとしているすべてのフラグメントのリスト(List<Fragment> fragments;)を作成し、次にそれらをページャーに追加して、現在表示されているフラグメントを処理しやすくしました。

そう:

@Override
onCreate(){
    //initialise the list of fragments
    fragments = new Vector<Fragment>();

    //fill up the list with out fragments
    fragments.add(Fragment.instantiate(this, MainFragment.class.getName()));
    fragments.add(Fragment.instantiate(this, MenuFragment.class.getName()));
    fragments.add(Fragment.instantiate(this, StoresFragment.class.getName()));
    fragments.add(Fragment.instantiate(this, AboutFragment.class.getName()));
    fragments.add(Fragment.instantiate(this, ContactFragment.class.getName()));


    //Set up the pager
    pager = (ViewPager)findViewById(R.id.pager);
    pager.setAdapter(new MyFragmentPagerAdapter(getSupportFragmentManager(), fragments));
    pager.setOffscreenPageLimit(4);
}

だから、これはと呼ぶことができます:

public Fragment getFragment(ViewPager pager){   
    Fragment theFragment = fragments.get(pager.getCurrentItem());
    return theFragment;
}

だから私はそれが正しいフラグメント上にあった場合にのみ実行されるifステートメントでそれをチャックすることができます

Fragment tempFragment = getFragment();
if(tempFragment == MyFragmentNo2.class){
    MyFragmentNo2 theFrag = (MyFragmentNo2) tempFragment;
    //then you can do whatever with the fragment
    theFrag.costomFunction();
}

しかし、それは私のハックとスラッシュのアプローチにすぎませんが、それは私にとってはうまくいきました。戻るボタンが押されたときに、現在表示されているフラグメントに関連する変更を行うのです。

2
Russell Cargill

これを行うための単純でクリーンな方法を見つけることができませんでした。ただし、ViewPagerウィジェットは、フラグメントをホストする別のViewGroupです。 ViewPagerはこれらのフラグメントを直接の子として持っています。つまり、(.getChildCount()と.getChildAt()を使用して)それらを反復処理して、探しているフラグメントインスタンスが現在ViewPagerにロードされているかどうかを確認し、それを参照することができます。例えば。フラグメントを区別するために静的な一意のIDフィールドを使用できます。

ViewPagerは、ListViewのような仮想化コンテナであるため、探しているフラグメントをロードしていない可能性があります。

FragmentPagerAdapterをViewPagerアダプタクラスに拡張する必要があります。
FragmentStatePagerAdapterを使用すると、そのFragmentIDを見つけることができなくなります。

public static String makeFragmentName(int viewPagerId, int index) {
  return "Android:switcher:" + viewPagerId + ":" + index;
}

このメソッドの使い方: -

Fragment mFragment = ((FragmentActivity) getContext()).getSupportFragmentManager().findFragmentByTag(
       AppMethodUtils.makeFragmentName(mViewPager.getId(), i)
);
InterestViewFragment newFragment = (InterestViewFragment) mFragment;
2
i_m_mahii

FragmentPagerAdapterはフラグメントのファクトリです。まだメモリ内にある場合は、その位置に基づいてフラグメントを見つけるには、これを使用します。

public Fragment findFragmentByPosition(int position) {
    FragmentPagerAdapter fragmentPagerAdapter = getFragmentPagerAdapter();
    return getSupportFragmentManager().findFragmentByTag(
            "Android:switcher:" + getViewPager().getId() + ":"
                    + fragmentPagerAdapter.getItemId(position));
}

v4サポートAPIのサンプルコード

2
Daniel De León

最善の解決策は、 CodePathSmartFragmentStatePagerAdapter で作成した拡張子を使用することです。そのガイドに従って、これはViewPagerからフラグメントと現在選択されているフラグメントを検索することをかなり容易にします。また、アダプタ内に埋め込まれているフラグメントのメモリを管理するという点でも優れています。

1
Nathan

私はこれを少し異なるアプローチで簡単に実装しました。

私のカスタムFragmentAdapter.getItemメソッドは、新しいMyFragment()ではなく、FragmentAdapterコンストラクタで作成されたMyFragmentのインスタンスを返しました。

私のアクティビティでは、アダプタからフラグメントを取得し、それがinstanceOf必要なフラグメントであるかどうかを確認してから、必要なメソッドをキャストして使用します。

0
lxknvlk

tabLayoutには、フラグメント用の複数のタブがあります。フラグメントのインデックスを使用してTagでフラグメントを見つけることができます。

例えば。 Fragment1のインデックスは0なので、findFragmentByTag()メソッドで、追加可能なfragmentTransactionを使用してViewpager.afterのタグを渡し、フラグメントを置き換えます。

String tag = "Android:switcher:" + R.id.viewPager + ":" + 0; Fragment1 f = (Fragment1) getSupportFragmentManager().findFragmentByTag(tag);

0
SUMIT MONAPARA

最も簡単で最も簡潔な方法です。 ViewPager内のすべてのフラグメントが異なるクラスのものである場合は、それらを取得して次のように区別できます。

public class MyActivity extends Activity
{

    @Override
    public void onAttachFragment(Fragment fragment) {
        super.onAttachFragment(fragment);
        if (fragment.getClass() == MyFragment.class) {
            mMyFragment = (MyFragment) fragment;
        }
    }

}
0
riwnodennyk

フラグメントで

public int getArgument(){
   return mPage;
{
public void update(){

}

FragmentActivityで

List<Fragment> fragments = getSupportFragmentManager().getFragments();
for(Fragment f:fragments){
    if((f instanceof PageFragment)&&(!f.isDetached())){
          PageFragment pf = (PageFragment)f;   
          if(pf.getArgument()==pager.getCurrentItem())pf.update();
    }
}
0
Mark Zhuravlyov

/values/integers.xmlに整数のリソースIDを作成します。

<integer name="page1">1</integer>
<integer name="page2">2</integer>
<integer name="page3">3</integer>

その後、PagerAdapter getItem関数で次のようにします。

public Fragment getItem(int position) {

        Fragment fragment = null;

        if (position == 0) {
            fragment = FragmentOne.newInstance();
            mViewPager.setTag(R.integer.page1,fragment);

        }
        else if (position == 1) {

            fragment = FragmentTwo.newInstance();
            mViewPager.setTag(R.integer.page2,fragment);

        } else if (position == 2) {

            fragment = FragmentThree.newInstance();
            mViewPager.setTag(R.integer.page3,fragment);

        }

        return fragment;
        }

次に、実際にこの関数を書いてフラグメント参照を取得します。

private Fragment getFragmentByPosition(int position) {
    Fragment fragment = null;

    switch (position) {
        case 0:
            fragment = (Fragment) mViewPager.getTag(R.integer.page1);
            break;

        case 1:

            fragment = (Fragment) mViewPager.getTag(R.integer.page2);
            break;

        case 2:
            fragment = (Fragment) mViewPager.getTag(R.integer.page3);
            break;
            }

            return fragment;
    }

上記の関数を呼び出してフラグメント参照を取得し、それをカスタムフラグメントにキャストします。

Fragment fragment = getFragmentByPosition(position);

        if (fragment != null) {
                    FragmentOne fragmentOne = (FragmentOne) fragment;
                    }
0
user2574090

フラグメントマネージャでフラグメントを反復処理する簡単な方法。 public static PlaceholderFragment newInstance(int sectionNumber)に配置されたセクション位置引数を持つviewpagerを探します。

public PlaceholderFragment getFragmentByPosition(Integer pos){
    for(Fragment f:getChildFragmentManager().getFragments()){
        if(f.getId()==R.id.viewpager && f.getArguments().getInt("SECTNUM") - 1 == pos) {
            return (PlaceholderFragment) f;
        }
    }
    return null;
}
0
Evgeny