1. ホーム
  2. アンドロイド

[解決済み】ViewPagerを動的に更新しますか?

2022-03-26 05:05:43

質問

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)との関係はどうなっているのかがわかりません。

を読みました。 参照 :

  • public abstract Fragment getItem (int position)

    指定された位置に関連するFragmentを返します。

  • public Object instantiateItem (ViewGroup container, int position)

    指定された位置のページを作成します。アダプタは、ここで指定されたコンテナにビューを追加する責任を負いますが、 finishUpdate(ViewGroup) から戻るまでに、これが完了していることを確認する必要があります。 パラメータ

    container ページが表示される View を含みます。 position インスタンス化されるページ位置。

    戻り値

    新しいページを表すオブジェクトを返します。これは View である必要はなく、ページの他のコンテナであっても構いません。

が、やはり理解できない。

以下は私のコードです。私はサポートパッケージ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 を使用する場合、以下のように、FragmentStatePagerAdapter のみを扱うのがベストです。 getItem() に触れず instantiateItem() を全く使用しない。その instantiateItem() - destroyItem() - isViewFromObject() インターフェースは、FragmentPagerAdapter が、より単純な getItem() インターフェイスを使用します。

この話に入る前に、以下のことを明らかにしておきます。

実際に表示されているフラグメントを切り替えたい場合は、FragmentPagerAdapter を避けて、FragmentPagerAdapter を使用する必要があります。 FragmentStatePagerAdapterです。

この回答の以前のバージョンでは、例として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() このように、更新を自ら管理することができます。すべての更新コードを getItem() ListView や他の AdapterView タイプのアダプタのように。

最後に、FragmentPagerAdapter がフラグメントを破棄しないからといって、getItemPosition が FragmentPagerAdapter の中で全く役に立たないというわけではありません。このコールバックを使用して、ViewPager 内のフラグメントの順序を変更することができます。しかし、フラグメントを FragmentManager から完全に削除することはできません。