私は3つの異なるフラグメントをホストするためにViewPager
をFragmentStatePagerAdapter
と共に使用しています。
ViewPager
のFragmentActivity
からFragment1
を取得したいとき。
問題は何ですか、どうすれば修正できますか?
主な答えは、フレームワークによって生成されている名前に依存しています。これが変わっても、それはもはや機能しません。
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を返します。しかし、これを使って現在のFragment
をViewPager
:adapater.getRegisteredFragment(viewPager.getCurrentItem())
から取得していますが、これはnull
を返しません。
私はこの解決策の他のどんな欠点も知らない。もしあれば、私は知りたいのですが。
ViewPagerからフラグメントを取得する場合、ここおよび他の関連SOスレッド/ブログに関する多くの回答があります。私が見たすべての人は壊れており、一般的に以下にリストされている2つのタイプのいずれかに分類されるようです。 this このスレッドの他の答えのように、現在のフラグメントのみを取得したい場合、他の有効なソリューションがいくつかあります。
FragmentPagerAdapter
を使用する場合は、以下を参照してください。 FragmentStatePagerAdapter
を使用する場合、 this を見る価値があります。 FragmentStateAdapterの現在のインデックスではないインデックスを取得することは、その性質上、これらがビューの外に出たり、offScreenLimitの境界から外れたりするため、完全に破棄されるほど有用ではありません。
間違った:独自のフラグメントの内部リストを維持し、FragmentPagerAdapter.getItem()
が呼び出されたときに追加されます
SparseArray
またはMap
を使用しますgetItem
は、ViewPager
で最初にページがスクロールされる(またはViewPager.setOffscreenPageLimit(x)
> 0の場合に取得される)ときにのみ呼び出されるため、ホストのActivity
/Fragment
が削除または再起動されると、カスタムFragmentPagerActivityが再作成されたときに内部SpaseArray
が消去されます、しかし背後でViewPagers内部フラグメントが再作成され、getItem
はではなくのいずれのインデックスに対しても呼び出されるため、インデックスからフラグメントを取得する機能は失われます永遠に。 FragmentManager.getFragment()
およびputFragment
を介してこれらのフラグメント参照を保存および復元することにより、これを説明できますが、これは面倒なIMHOになり始めます。間違った:FragmentPagerAdapter
の内部で使用されているものと一致する独自のタグIDを構築し、これを使用してFragmentManager
からページフラグメントを取得します。
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つのソリューションの問題を解決します
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 。何かが失敗するなら、あなたは良いクラッシュログを見るでしょう。
もう一つの簡単な解決策:
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);
}
}
これにはいくつかの答えがありますが、おそらくこれは誰かを助けるでしょう。 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
の多くの実装は、Fragment
でgetItem
sを作成しますnew。上記の方法を使用すると、Fragment
のすべての項目を処理するときにgetItem
が呼び出されるたびに、新しいFragmentPagerAdapter
sが作成されます。このため、代わりに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;
}
私の場合、上記の解決策はどれもうまくいきませんでした。
ただし、フラグメント内でChild Fragment Managerを使用しているため、次のものが使用されました。
Fragment f = getChildFragmentManager().getFragments().get(viewPager.getCurrentItem());
これは、マネージャ内のあなたのフラグメントがviewpagerアイテムに対応している場合にのみ機能します。
ViewPagerからの現在のVisibleフラグメントを取得するため。私はこの単純な文を使用していて、それはうまく機能しています。
public Fragment getFragmentFromViewpager()
{
return ((Fragment) (mAdapter.instantiateItem(mViewPager, mViewPager.getCurrentItem())));
}
これは上記の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
}
}
}
}
ちょっと私はこの質問に答えました ここ 。基本的には、上書きする必要があります
public Object instantiateItem(ViewGroupコンテナ、int位置)
fragmentStatePagerAdapterのメソッド。
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);
}
最初に使用しようとしているすべてのフラグメントのリスト(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();
}
しかし、それは私のハックとスラッシュのアプローチにすぎませんが、それは私にとってはうまくいきました。戻るボタンが押されたときに、現在表示されているフラグメントに関連する変更を行うのです。
これを行うための単純でクリーンな方法を見つけることができませんでした。ただし、ViewPagerウィジェットは、フラグメントをホストする別のViewGroupです。 ViewPagerはこれらのフラグメントを直接の子として持っています。つまり、(.getChildCount()と.getChildAt()を使用して)それらを反復処理して、探しているフラグメントインスタンスが現在ViewPagerにロードされているかどうかを確認し、それを参照することができます。例えば。フラグメントを区別するために静的な一意のIDフィールドを使用できます。
ViewPagerは、ListViewのような仮想化コンテナであるため、探しているフラグメントをロードしていない可能性があります。
FragmentPagerAdapter
をViewPagerアダプタクラスに拡張する必要があります。FragmentStatePagerAdapter
を使用すると、そのFragment
でID
を見つけることができなくなります。
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;
FragmentPagerAdapterはフラグメントのファクトリです。まだメモリ内にある場合は、その位置に基づいてフラグメントを見つけるには、これを使用します。
public Fragment findFragmentByPosition(int position) {
FragmentPagerAdapter fragmentPagerAdapter = getFragmentPagerAdapter();
return getSupportFragmentManager().findFragmentByTag(
"Android:switcher:" + getViewPager().getId() + ":"
+ fragmentPagerAdapter.getItemId(position));
}
v4サポートAPIのサンプルコード
最善の解決策は、 CodePathSmartFragmentStatePagerAdapter で作成した拡張子を使用することです。そのガイドに従って、これはViewPagerからフラグメントと現在選択されているフラグメントを検索することをかなり容易にします。また、アダプタ内に埋め込まれているフラグメントのメモリを管理するという点でも優れています。
私はこれを少し異なるアプローチで簡単に実装しました。
私のカスタムFragmentAdapter.getItemメソッドは、新しいMyFragment()ではなく、FragmentAdapterコンストラクタで作成されたMyFragmentのインスタンスを返しました。
私のアクティビティでは、アダプタからフラグメントを取得し、それがinstanceOf必要なフラグメントであるかどうかを確認してから、必要なメソッドをキャストして使用します。
tabLayoutには、フラグメント用の複数のタブがあります。フラグメントのインデックスを使用してTagでフラグメントを見つけることができます。
例えば。 Fragment1のインデックスは0なので、findFragmentByTag()
メソッドで、追加可能なfragmentTransactionを使用してViewpager.afterのタグを渡し、フラグメントを置き換えます。
String tag = "Android:switcher:" + R.id.viewPager + ":" + 0; Fragment1 f = (Fragment1) getSupportFragmentManager().findFragmentByTag(tag);
最も簡単で最も簡潔な方法です。 ViewPager
内のすべてのフラグメントが異なるクラスのものである場合は、それらを取得して次のように区別できます。
public class MyActivity extends Activity
{
@Override
public void onAttachFragment(Fragment fragment) {
super.onAttachFragment(fragment);
if (fragment.getClass() == MyFragment.class) {
mMyFragment = (MyFragment) fragment;
}
}
}
フラグメントで
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();
}
}
/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;
}
フラグメントマネージャでフラグメントを反復処理する簡単な方法。 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;
}