ViewPagerでコンテンツを更新できません。
FragmentPagerAdapterクラスのinstantiateItem()およびgetItem()メソッドの正しい使い方は何ですか?
フラグメントをインスタンス化して返すためにgetItem()のみを使用していました。
@Override
public Fragment getItem(int position) {
return new MyFragment(context, paramters);
}
これはうまくいった。内容を変更することはできません。
だから私はこれが見つかりました: ViewPager PagerAdapterがビューを更新していない
「私のアプローチは、instantiateItem()メソッド内のインスタンス化されたビューに対してsetTag()メソッドを使用することです」
そのためにinstantiateItem()を実装したいと思います。しかし、何を返す必要があるのか(タイプはObject)、getItem(int position)との関係は何ですか?
参照 を読みます。
パブリック抽象フラグメントgetItem(int position)
指定された位置に関連付けられているフラグメントを返します。
public Object instantiateItem(ViewGroupコンテナ、int位置)
指定された位置にページを作成します。ここで指定したコンテナーにビューを追加するのはアダプターの役割です。ただし、これはfinishUpdate(ViewGroup)から戻るまでに完了するようにする必要があります。パラメーター
コンテナページが表示されるコンテナビュー。インスタンス化するページ位置。
返品
新しいページを表すオブジェクトを返します。これはビューである必要はありませんが、ページの他のコンテナにすることができます。
しかし、私はまだそれを得られません。
これが私のコードです。サポートパッケージv4を使用しています。
ViewPagerTest
public class ViewPagerTest extends FragmentActivity {
private ViewPager pager;
private MyFragmentAdapter adapter;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.pager1);
pager = (ViewPager)findViewById(R.id.slider);
String[] data = {"page1", "page2", "page3", "page4", "page5", "page6"};
adapter = new MyFragmentAdapter(getSupportFragmentManager(), 6, this, data);
pager.setAdapter(adapter);
((Button)findViewById(R.id.button)).setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
reload();
}
});
}
private void reload() {
String[] data = {"changed1", "changed2", "changed3", "changed4", "changed5", "changed6"};
//adapter = new MyFragmentAdapter(getSupportFragmentManager(), 6, this, data);
adapter.setData(data);
adapter.notifyDataSetChanged();
pager.invalidate();
//pager.setCurrentItem(0);
}
}
MyFragmentAdapter
class MyFragmentAdapter extends FragmentPagerAdapter {
private int slideCount;
private Context context;
private String[] data;
public MyFragmentAdapter(FragmentManager fm, int slideCount, Context context, String[] data) {
super(fm);
this.slideCount = slideCount;
this.context = context;
this.data = data;
}
@Override
public Fragment getItem(int position) {
return new MyFragment(data[position], context);
}
@Override
public int getCount() {
return slideCount;
}
public void setData(String[] data) {
this.data = data;
}
@Override
public int getItemPosition(Object object) {
return POSITION_NONE;
}
}
MyFragment
public final class MyFragment extends Fragment {
private String text;
public MyFragment(String text, Context context) {
this.text = text;
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.slide, null);
((TextView)view.findViewById(R.id.text)).setText(text);
return view;
}
}
ここに同様の問題を抱えている人もいます、答えはありません http://www.mail-archive.com/[email protected]/msg200477.html
FragmentPagerAdapterまたはFragmentStatePagerAdapterを使用するときは、getItem()
のみを扱い、instantiateItem()
には触れないでください。 PagerAdapterのinstantiateItem()
-destroyItem()
-isViewFromObject()
インターフェースは、FragmentPagerAdapterがはるかに単純なgetItem()
インターフェースを実装するために使用する低レベルインターフェースです。
これに入る前に、私はそれを明確にする必要があります
表示されている実際のフラグメントを切り替えたい場合は、FragmentPagerAdapterを避けてFragmentStatePagerAdapterを使用する必要があります。
この回答の初期のバージョンでは、例としてFragmentPagerAdapterを使用していましたが、FragmentPagerAdapterが最初に表示された後にFragmentPagerAdapterがフラグメントを破棄することはないため、機能しません。
私があなたがリンクした記事で提供されているsetTag()
とfindViewWithTag()
回避策をお勧めしません。 setTag()
とfindViewWithTag()
の使用はフラグメントでは機能しないので、これは良い一致ではありません。
正しい解決策はgetItemPosition()
をオーバーライドすることです。 notifyDataSetChanged()
が呼び出されると、ViewPagerはアダプタ内のすべての項目に対してgetItemPosition()
を呼び出して、それらを別の位置に移動する必要があるのか、削除する必要があるのかを確認します。
デフォルトでは、getItemPosition()
はPOSITION_UNCHANGED
を返します。これは、「このオブジェクトは正しい場所にあるため、破壊したり削除したりしないでください」という意味です。 POSITION_NONE
を返すことで、代わりに「このオブジェクトは表示されているアイテムではなくなったので削除してください」と言って問題を解決します。そのため、アダプタ内のすべてのアイテムを削除して再作成する効果があります。
これは完全に正当な修正です。この修正により、notifyDataSetChangedはビューのリサイクルなしで通常のアダプタのように動作します。この修正を実装してもパフォーマンスが満足のいくものであれば、競合はやめています。仕事は終わった。
より良いパフォーマンスが必要な場合は、よりgetItemPosition()
実装を使うことができます。これは、ページャが文字列のリストからフラグメントを作成する例です。
ViewPager pager = /* get my ViewPager */;
// assume this actually has stuff in it
final ArrayList<String> titles = new ArrayList<String>();
FragmentManager fm = getSupportFragmentManager();
pager.setAdapter(new FragmentStatePagerAdapter(fm) {
public int getCount() {
return titles.size();
}
public Fragment getItem(int position) {
MyFragment fragment = new MyFragment();
fragment.setTitle(titles.get(position));
return fragment;
}
public int getItemPosition(Object item) {
MyFragment fragment = (MyFragment)item;
String title = fragment.getTitle();
int position = titles.indexOf(title);
if (position >= 0) {
return position;
} else {
return POSITION_NONE;
}
}
});
この実装では、新しいタイトルを表示しているフラグメントだけが表示されます。リストにまだあるタイトルを表示しているフラグメントは、代わりにリスト内の新しい位置に移動され、リストに含まれなくなったタイトルを持つフラグメントは破棄されます。
フラグメントが再作成されていないが、とにかく更新する必要がある場合はどうなりますか?生きているフラグメントへの更新はフラグメント自身によって最もよく処理されます。それがフラグメントを持つことの利点です、結局のところ - それはそれ自身のコントローラです。フラグメントは、onCreate()
内の別のオブジェクトにリスナーまたはオブザーバーを追加してから、onDestroy()
内でそれを削除して、更新自体を管理することができます。 ListViewや他のタイプのAdapterViewのアダプタの場合のように、すべての更新コードをgetItem()
内に入れる必要はありません。
最後の1つ - FragmentPagerAdapterがフラグメントを破壊しないという理由だけで、getItemPositionがFragmentPagerAdapterでまったく役に立たないという意味ではありません。 ViewPagerでフラグメントを並べ替えるために、このコールバックをまだ使用できます。ただし、FragmentManagerからそれらが完全に削除されることはありません。
getItemPosition()
からPOSITION_NONE
を返して全画面を再現するのではなく、次のようにします。
//call this method to update fragments in ViewPager dynamically
public void update(UpdateData xyzData) {
this.updateData = xyzData;
notifyDataSetChanged();
}
@Override
public int getItemPosition(Object object) {
if (object instanceof UpdateableFragment) {
((UpdateableFragment) object).update(updateData);
}
//don't return POSITION_NONE, avoid fragment recreation.
return super.getItemPosition(object);
}
あなたのフラグメントはUpdateableFragment
インターフェースを実装するべきです:
public class SomeFragment extends Fragment implements
UpdateableFragment{
@Override
public void update(UpdateData xyzData) {
// this method will be called for every fragment in viewpager
// so check if update is for this fragment
if(forMe(xyzData)) {
// do whatever you want to update your UI
}
}
}
そしてインターフェース:
public interface UpdateableFragment {
public void update(UpdateData xyzData);
}
あなたのデータクラス:
public class UpdateData {
//whatever you want here
}
私は7つのフラグメントを持つViewPagerを持っているときに私が前に直面したのと同じ問題にまだ直面している人のために。これらのフラグメントのデフォルトはapiサービスから英語のコンテンツをロードするが、ここで私は設定アクティビティから言語を変更したい、そして設定アクティビティを終えた後私はユーザーからの言語選択と一致するユーザーがここからアラビア語を選択した場合は、アラビア語のコンテンツを読み込む
1 - 上記のようにFragmentStatePagerAdapterを使用する必要があります。
2 on mainActivity私はonResumeをオーバーライドし、次のことを行いました
if (!(mPagerAdapter == null)) {
mPagerAdapter.notifyDataSetChanged();
}
3-i mPagerAdapterのgetItemPosition()をオーバーライドしてPOSITION_NONEを返すようにしました。
@Override
public int getItemPosition(Object object) {
return POSITION_NONE;
}
魅力のように働く
私はこの問題に遭遇し、ついに今日それを解決したので、私はこれまでに学んだことを書き留め、それがAndroidのViewPager
に慣れていない人にとって役立つことを願っています。私はAPIレベル17でFragmentStatePagerAdapter
を使用していて、現在2つのフラグメントしかありません。正しくないものがあるはずです。訂正してください、ありがとう。
シリアル化されたデータはメモリにロードする必要があります。これはCursorLoader
/AsyncTask
/Thread
を使って行うことができます。自動的にロードされるかどうかは、コードによって異なります。 CursorLoader
を使用している場合は、登録済みのデータオブザーバがあるため自動ロードされます。
viewpager.setAdapter(pageradapter)
を呼び出した後、アダプタのgetCount()
はフラグメントを構築するために常に呼び出されます。そのため、データがロードされている場合、getCount()
は0を返す可能性があるため、データが表示されていない場合にダミーのフラグメントを作成する必要はありません。
データがロードされた後、getCount()
はまだ0なので、アダプタは自動的にフラグメントを構築しません。そのため、実際にロードされたデータ番号をgetCount()
によって返されるように設定し、アダプタのnotifyDataSetChanged()
を呼び出します。 ViewPager
は、メモリ内のデータによってフラグメント(最初の2つのフラグメントのみ)を作成し始めます。 notifyDataSetChanged()
が返される前に行われます。それからViewPager
はあなたが必要とする正しいフラグメントを持っています。
データベースとメモリー内のデータが両方とも更新される(ライトスルー)か、メモリー内のデータだけが更新される(ライトバック)か、データベース内のデータのみが更新される場合。最後の2つのケースでは、データがデータベースからメモリーに自動的にロードされない場合(上記のとおり)。 ViewPager
とpagerアダプタはメモリ内のデータを扱うだけです。
そのため、メモリ内のデータが更新されたら、アダプタのnotifyDataSetChanged()
を呼び出すだけです。フラグメントはすでに作成されているので、notifyDataSetChanged()
が返される前にアダプタのonItemPosition()
が呼び出されます。 getItemPosition()
では何もする必要はありません。その後、データが更新されます。
コードのdestroyDrawingCache()
の後にViewPagerでnotifyDataSetChanged()
を試してください。
上記のすべての解決策を試してこの問題を解決し、また this 、 this 、および this この問題を解決し、ViewPager
を作成して古いFragment
を破棄し、_pager
を新しいFragment
sで埋めるために失敗しました。私は次のように問題を解決しました:
1)ViewPager
クラスを次のようにFragmentPagerAdapter
に拡張します。
public class myPagerAdapter extends FragmentPagerAdapter {
2)ViewPager
およびtitle
を保存するfragment
のアイテムを次のように作成します。
public class PagerItem {
private String mTitle;
private Fragment mFragment;
public PagerItem(String mTitle, Fragment mFragment) {
this.mTitle = mTitle;
this.mFragment = mFragment;
}
public String getTitle() {
return mTitle;
}
public Fragment getFragment() {
return mFragment;
}
public void setTitle(String mTitle) {
this.mTitle = mTitle;
}
public void setFragment(Fragment mFragment) {
this.mFragment = mFragment;
}
}
3)ViewPager
のコンストラクターにFragmentManager
インスタンスを取得させ、次のようにclass
に格納します。 :
private FragmentManager mFragmentManager;
private ArrayList<PagerItem> mPagerItems;
public MyPagerAdapter(FragmentManager fragmentManager, ArrayList<PagerItem> pagerItems) {
super(fragmentManager);
mFragmentManager = fragmentManager;
mPagerItems = pagerItems;
}
4)以前のadapter
をすべて削除して、fragment
データを新しいデータで再設定するメソッドを作成します。 fragmentManager
自体を直接作成してadapter
を作成し、次のように新しいリストから新しいfragment
を再度設定します。
public void setPagerItems(ArrayList<PagerItem> pagerItems) {
if (mPagerItems != null)
for (int i = 0; i < mPagerItems.size(); i++) {
mFragmentManager.beginTransaction().remove(mPagerItems.get(i).getFragment()).commit();
}
mPagerItems = pagerItems;
}
5)コンテナからActivity
またはFragment
から、新しいデータでアダプタを再初期化しないでください。メソッドsetPagerItems
を使用して、新しいデータを次のように設定して、新しいデータを設定します。
ArrayList<PagerItem> pagerItems = new ArrayList<PagerItem>();
pagerItems.add(new PagerItem("Fragment1", new MyFragment1()));
pagerItems.add(new PagerItem("Fragment2", new MyFragment2()));
mPagerAdapter.setPagerItems(pagerItems);
mPagerAdapter.notifyDataSetChanged();
役に立てば幸いです。
どういうわけか私のために答えのどれも私のために働かなかった従って私は私のfragmentStatePagerAdapterのsuperを呼び出さないでrestoreStateメソッドをオーバーライドしなければならなかった。コード:
private class MyAdapter extends FragmentStatePagerAdapter {
// [Rest of implementation]
@Override
public void restoreState(Parcelable state, ClassLoader loader) {}
}
私は自分のニーズに合わせてBill Phillipsが提供するソリューションを少し修正しました。
private class PagerAdapter extends FragmentStatePagerAdapter{
Bundle oBundle;
FragmentManager oFragmentManager;
ArrayList<Fragment> oPooledFragments;
public PagerAdapter(FragmentManager fm) {
super(fm);
oFragmentManager=fm;
}
@Override
public int getItemPosition(Object object) {
Fragment oFragment=(Fragment)object;
oPooledFragments=new ArrayList<>(oFragmentManager.getFragments());
if(oPooledFragments.contains(oFragment))
return POSITION_NONE;
else
return POSITION_UNCHANGED;
}
}
そのため、getItemPosition()
は、FragmentManager
が呼び出されたときに現在getItemPosition
内にあるフラグメントに対してのみPOSITION_NONE
を返すようになります。 (このFragmentStatePager
とそれに関連するViewPager
はActivityではなくFragmentに含まれています)
私は似たような問題を抱えていましたが、既存の解決策(ハードコードされたタグ名など)を信頼したくないし、M-WaJeEhの解決策を私のために働かせることができませんでした。これが私の解決策です:
GetItemで作成されたフラグメントへの参照を配列内に保持します。 configurationChangeやメモリ不足などの理由でアクティビティが破壊されない限り、これは問題なく動作します( - >アクティビティに戻ったときに、フラグメントは再度呼び出されずに配列を更新せずに最後の状態に戻ります。 ).
この問題を回避するために、instantiateItem(ViewGroup、int)を実装し、次のように配列を更新しました。
@Override
public Object instantiateItem(ViewGroup container, int position) {
Object o = super.instantiateItem(container, position);
if(o instanceof FragmentX){
myFragments[0] = (FragmentX)o;
}else if(o instanceof FragmentY){
myFragments[1] = (FragmentY)o;
}else if(o instanceof FragmentZ){
myFragments[2] = (FragmentZ)o;
}
return o;
}
それで、一方で私にはうまくいく解決策を見つけてあなたとそれを共有したかったのはうれしいですが、他の誰かが似たようなことをしたのかそれは好きですか?これまでのところ、それは私のために非常にうまく機能します...
私は同じ問題を抱えていて、検索し過ぎたことがあります。 stackoverflowでまたはグーグルを介して与えられた任意の答えは私の問題の解決策ではありませんでした。私の問題は簡単でした。私はリストを持っています、私はこのリストをviewpagerで見せます。リストの先頭に新しい要素を追加してviewpagerの表示を更新すると、変更が反映されます。私の最終的な解決策は誰でも簡単に使える方法でした。新しい要素がリストに追加され、リストを更新したいとき。最初にviewpagerアダプタをnullに設定してからアダプタを再作成し、それにiをviewpagerに設定します。
myVPager.setAdapter(null);
myFragmentAdapter = new MyFragmentAdapter(getSupportFragmentManager(),newList);
myVPager.setAdapter(myFragmentAdapter);
アダプタがFragmentStatePagerAdapterを拡張する必要があることを確認してください
FragmentStatePagerAdapterを使用する場合は、 https://code.google.com/p/Android/issues/detail?can=2&start=0&num=100&q=&colspec=ID%20Type%20Status%20Ownerをご覧ください。 %20合計%20Stars&groupby =&sort =&id = 37990 。 FragmentStatePagerAdapterには問題があるかもしれませんが、あなたのユースケースを悩ますかもしれません。
また、リンクにも解決策がほとんどありません。
Fragment
のViewPager
の内容を更新するために EventBus libraryを使用します。ロジックは単純です、 EventBusのドキュメントのやり方と同じです 。 FragmentPagerAdapter
インスタンスを制御する必要はありません。コードはこちらです。
1:イベントを定義する
どのメッセージを更新する必要があるかを定義します。
public class UpdateCountEvent {
public final int count;
public UpdateCountEvent(int count) {
this.count = count;
}
}
2.登録者を作成する
更新が必要なフラグメントの以下のコードを書きます。
@Override
public void onStart() {
super.onStart();
EventBus.getDefault().register(this);
}
@Override
public void onStop() {
EventBus.getDefault().unregister(this);
super.onStop();
}
public void onEvent(UpdateCountEvent event) {//get update message
Toast.makeText(getActivity(), event.count, Toast.LENGTH_SHORT).show();
}
3.ポストイベント
パラメータを更新する必要がある他のActivity
または他のFragment
に、以下のコードを記述します。
//input update message
EventBus.getDefault().post(new UpdateCountEvent(count));
私は党に遅れているのを知っています。 TabLayout#setupWithViewPager(myViewPager);
の直後にFragmentPagerAdapter#notifyDataSetChanged();
を呼び出すことで問題を解決しました
私の場合、新しいページを挿入するときにビューページャが既存のフラグメントの位置を2回要求していましたが、新しいアイテムの位置を要求していなかったため、正しくない動作とデータが表示されません。
FragmentStatePagerAdapterのソースをコピーします(年齢の更新はされていないようです)。
NotifyDataSetChanged()をオーバーライドします。
@Override
public void notifyDataSetChanged() {
mFragments.clear();
super.notifyDataSetChanged();
}
クラッシュを防ぐためにdestroyItem()に健全性チェックを追加します。
if (position < mFragments.size()) {
mFragments.set(position, null);
}
これは@Bill Phillips Oneからの情報を組み込んだ私の実装です。データが変更された場合を除いて、ほとんどの場合フラグメントキャッシュを取得します。シンプルで、うまく機能しているようです。
MyFragmentStatePagerAdapter.Java
private boolean mIsUpdating = false;
public void setIsUpdating(boolean mIsUpdating) {
this.mIsUpdating = mIsUpdating;
}
@Override
public int getItemPosition(@NonNull Object object) {
if (mIsUpdating) {
return POSITION_NONE;
}
else {
return super.getItemPosition(object);
}
}
MyActivity.Java
mAdapter.setIsUpdating(true);
mAdapter.notifyDataSetChanged();
mAdapter.setIsUpdating(false);
私は上のすべての答えと他の多くの投稿を見てきましたが、それでも私のために動くものを見つけることができませんでした(動的にタブを追加したり削除しながらさまざまなフラグメントタイプで)。 FWIWに従うアプローチは私のために働いたものです(他の誰かが同じ問題を抱えている場合)。
public class MyFragmentStatePageAdapter extends FragmentStatePagerAdapter {
private static final String TAB1_TITLE = "Tab 1";
private static final String TAB2_TITLE = "Tab 2";
private static final String TAB3_TITLE = "Tab 3";
private ArrayList<String> titles = new ArrayList<>();
private Map<Fragment, Integer> fragmentPositions = new HashMap<>();
public MyFragmentStatePageAdapter(FragmentManager fm) {
super(fm);
}
public void update(boolean showTab1, boolean showTab2, boolean showTab3) {
titles.clear();
if (showTab1) {
titles.add(TAB1_TITLE);
}
if (showTab2) {
titles.add(TAB2_TITLE);
}
if (showTab3) {
titles.add(TAB3_TITLE);
}
notifyDataSetChanged();
}
@Override
public int getCount() {
return titles.size();
}
@Override
public Fragment getItem(int position) {
Fragment fragment = null;
String tabName = titles.get(position);
if (tabName.equals(TAB1_TITLE)) {
fragment = Tab1Fragment.newInstance();
} else if (tabName.equals(TAB2_TITLE)) {
fragment = Tab2Fragment.newInstance();
} else if (tabName.equals(TAB3_TITLE)) {
fragment = Tab3Fragmen.newInstance();
}
((BaseFragment)fragment).setTitle(tabName);
fragmentPositions.put(fragment, position);
return fragment;
}
@Override
public CharSequence getPageTitle(int position) {
return titles.get(position);
}
@Override
public int getItemPosition(Object item) {
BaseFragment fragment = (BaseFragment)item;
String title = fragment.getTitle();
int position = titles.indexOf(title);
Integer fragmentPosition = fragmentPositions.get(item);
if (fragmentPosition != null && position == fragmentPosition) {
return POSITION_UNCHANGED;
} else {
return POSITION_NONE;
}
}
@Override
public void destroyItem(ViewGroup container, int position, Object object) {
super.destroyItem(container, position, object);
fragmentPositions.remove(object);
}
}
インデックスベースでフラグメントを再作成または再ロードする場合は、FragmentPagerAdapterの代わりにFragmentStatePagerAdapterを使用します。たとえば、FirstFragment以外のフラグメントを再ロードする場合は、インスタンスを確認して位置を返すことができます。
public int getItemPosition(Object item) {
if(item instanceof FirstFragment){
return 0;
}
return POSITION_NONE;
}
このソリューションはすべての人にはうまくいきませんが、私の場合、ViewPagerのすべてのFragmentは異なるクラスであり、一度に1つしか存在しません。
この制約により、このソリューションは安全であり、実稼働環境で使用するのに安全であるはずです。
private void updateFragment(Item item) {
List<Fragment> fragments = getSupportFragmentManager().getFragments();
for (Fragment fragment : fragments) {
if (fragment instanceof MyItemFragment && fragment.isVisible()) {
((MyItemFragment) fragment).update(item);
}
}
}
同じフラグメントの複数のバージョンがある場合は、この同じ方法を使用してそれらのフラグメントのメソッドを呼び出して、それが更新するフラグメントかどうかを判断できます。
私は非常に多くの異なったアプローチを試みていました、どれも本当に私の問題を解決しませんでした。以下は、皆さんが提供したソリューションを組み合わせて解決する方法です。みんな、ありがとう。
class PagerAdapter extends FragmentPagerAdapter {
public boolean flag_refresh=false;
public PagerAdapter(FragmentManager fm) {
super(fm);
}
@Override
public Fragment getItem(int page) {
FragmentsMain f;
f=new FragmentsMain();
f.page=page;
return f;
}
@Override
public int getCount() {
return 4;
}
@Override
public int getItemPosition(Object item) {
int page= ((FragmentsMain)item).page;
if (page == 0 && flag_refresh) {
flag_refresh=false;
return POSITION_NONE;
} else {
return super.getItemPosition(item);
}
}
@Override
public void destroyItem(View container, int position, Object object) {
((ViewPager) container).removeView((View) object);
}
}
OnResume()の後にページ0をリフレッシュしたいだけです。
adapter=new PagerAdapter(getSupportFragmentManager());
pager.setAdapter(adapter);
@Override
protected void onResume() {
super.onResume();
if (adapter!=null) {
adapter.flag_refresh=true;
adapter.notifyDataSetChanged();
}
}
私のFragmentsMainには、公開整数 "page"があります。それは、それが更新したいページかどうかを教えてくれます。
public class FragmentsMain extends Fragment {
private Cursor cursor;
private static Context context;
public int page=-1;