90

ここでは、ViewPager で非常に奇妙な問題が発生しています。各 ViewPager ページにリストを埋め込み、リスト データの更新時にリスト アダプターとビュー ページャー アダプターの両方で notifyDataSetChanged をトリガーします。

ページがビュー ツリーを更新しないことがあります。つまり、空白のままになったり、ページング時に表示されなくなったりすることがあります。ページを数回前後に移動すると、コンテンツが突然再表示されます。ここで Android にビューの更新が欠けているようです。また、階層ビューアーでデバッグするときに、ビューを選択すると常に再表示されることに気付きました。これは、階層ビューアーが選択したビューを強制的に再描画するためと思われます。

ただし、プログラムでこれを機能させることはできませんでした。リストビュー、またはビューページャー全体を無効にしても効果はありませんでした。

これは、compatibility-v4_r7 ライブラリを使用しています。また、ページャーの表示に関連する多くの問題を修正すると主張しているため、最新のリビジョンを使用しようとしましたが、事態はさらに悪化しました (たとえば、ジェスチャが壊れていて、すべてのページをページングできなくなったことがあります)。

他の誰かもこれらの問題に遭遇していますか、それとも何が原因なのか考えていますか?

4

13 に答える 13

46

最終的に解決策を見つけることができました。どうやら、私たちの実装には 2 つの問題がありました。

  1. 私たちのアダプターは のビューを削除しませんでしたdestroyItem()
  2. レイアウトを 1 回だけインフレートする必要があるようにビューをキャッシュしていました。また、 でビューを削除していないため、ビューをdestroyItem()追加するのではなくinstantiateItem()、現在の位置に対応するキャッシュされたビューを返すだけでした。

私はのソースコードをあまり深く調べていませんViewPager-そして、あなたがそれをしなければならないことを正確に明示しているわけではありません-しかし、ドキュメントには次のように書かれています:

destroyItem()
指定された位置のページを削除します。アダプターはコンテナーからビューを削除する責任がありますが、finishUpdate(ViewGroup) から返されるまでに確実に削除する必要があるだけです。

と:

非常に単純な PagerAdapter は、ページ ビュー自体をキー オブジェクトとして使用することを選択し、作成後に instanceiateItem(ViewGroup, int) からそれらを返し、それらを親 ViewGroup に追加します。一致する destroyItem(ViewGroup, int, Object) 実装は、親 ViewGroup からビューを削除し、isViewFromObject(View, Object) は return view == object; として実装できます。

したがって、私の結論は、/ViewPagerで子を明示的に追加/削除するために、その基になるアダプターに依存しているということです。つまり、アダプタが のサブクラスである場合、サブクラスはこのロジックを実装する必要があります。instantiateItem()destroyItem()PagerAdapter

補足:内でリストを使用する場合は、このViewPager点に注意してください。

于 2012-08-23T11:14:25.890 に答える
23

私はまったく同じ問題を抱えていましたが、実際にはdestroyItemのビューを破棄しました(私は思った)。しかし問題は、私がviewPager.removeViewAt(index);instedを使用してそれを破壊したことでしたviewPager.removeView((View) object);

間違い:

@Override
public void destroyItem(ViewGroup viewPager, int position, Object object) {
    viewPager.removeViewAt(position);
}

右:

@Override
public void destroyItem(ViewGroup viewPager, int position, Object object) {
    viewPager.removeView((View) object);
}
于 2012-10-24T08:01:41.647 に答える
11

ViewPager は、アイテムの再利用に関して巧妙なことを試みますが、状況が変化したときに新しいアイテムの位置を返す必要があります。これを PagerAdapter に追加してみてください。

public int getItemPosition (Object object) { return POSITION_NONE; }

これは基本的に、すべてが変更されたことを ViewPager に伝えます (そして、強制的にすべてを再インスタンス化します)。頭のてっぺんから思いつくのはそれだけです。

于 2012-07-31T09:31:35.723 に答える
1

Androidサポートライブラリには、すべてのページにListViewを備えたViewPagerを含むデモアクティビティがあります。あなたはおそらくそれが何をするのか見てみる必要があります。

Eclipseの場合(Android Dev Tools r20を使用):

  1. 選択するNew > Android Sample Project
  2. ターゲットAPIレベルを選択します(利用可能な最新のものをお勧めします)
  3. 選択するSupport4Demos
  4. プロジェクトを右クリックして、Android Tools > Add Support Library
  5. アプリを実行し、を選択FragmentしてからPager

このためのコードはにありsrc/com.example.android.supportv4.app/FragmentPagerSupport.javaます。幸運を!

于 2012-07-31T09:44:19.457 に答える
0

同じ問題がありましたが、これは関係がありますListView(リストが空の場合、空のビューが正常に表示されるため)。問題requestLayout()のあるListView. これでうまく描けるようになりました!

于 2012-12-19T22:42:38.540 に答える
0

私はこれに遭遇し、非常によく似た問題を抱えていました。スタックオーバーフローについても尋ねました。

私にとって、私のビューの親の親で、誰かがを呼び出さずにサブクラス化LinearLayoutし、オーバーライドしました。これにより、ViewPager で呼び出されなくなりました (ただし、hierarchyviewer はこれらを手動で呼び出します)。測定されないと、ViewPager では空白として表示されます。requestLayout()super.requestLayout()onMeasureonLayout

したがって、含まれているビューを確認してください。View からサブクラス化し、requestLayout などをやみくもにオーバーライドしないようにしてください。

于 2012-07-31T17:31:08.130 に答える
0

ViewPager と FragmentStatePagerAdapter を使用しているときに、この同じ問題に遭遇しました。invalidate() と requestLayout() を呼び出すために 3 秒の遅延を伴うハンドラーを使用しようとしましたが、機能しませんでした。うまくいったのは、次のようにviewPagerの背景色をリセットすることでした:

MyFragment.java

    private Handler mHandler;
    private Runnable mBugUpdater;

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
            Bundle savedInstanceState) {
        View rootView = new ViewPager(getActivity());
        //...Create your adapter and set it here...

        mHandler = new Handler();
        mBugUpdater = new Runnable(){
            @Override
            public void run() {
                mVp.setBackgroundColor(mItem.getBackgroundColor());
                mHandler = null;
                mBugUpdater = null;
            }           
        };
        mHandler.postDelayed(mBugUpdater,50);

        return rootView;
    }

    @Override
    public void onPause() {
        if(mHandler != null){
            //Remove the callback if it hasn't triggered yet
            mHandler.removeCallbacks(mBugUpdater);
            mHandler = null;
            mBugUpdater = null;
        }
        super.onPause();
     }
于 2015-08-30T16:57:24.960 に答える
0

同じ症状で問題がありましたが、別の原因があり、私のばかげた間違いであることが判明しました. 誰かに役立つ場合に備えて、ここに追加すると思いました。

以前は FragmentStatePagerAdapter を使用した ViewPager がありましたが、これには 2 つのフラグメントがありましたが、後で 3 つ目を追加しました。ただし、デフォルトのオフスクリーン ページ制限が 1 であることを忘れていました。そのため、新しい 3 番目のフラグメントに切り替えると、最初のフラグメントが破棄され、元に戻した後に再作成されます。問題は、私のアクティビティがこれらのフラグメントに UI 状態を初期化するよう通知することを担当していたことです。これは、アクティビティとフラグメントのライフサイクルが同じ場合にたまたま機能しましたが、これを修正するには、起動ライフサイクル中に独自の UI を初期化するようにフラグメントを変更する必要がありました。最後に、setOffscreenPageLimit を 2 に変更して、3 つのフラグメントすべてが常に有効になるようにしました (この場合、メモリをあまり消費しないため安全です)。

于 2017-02-02T03:40:09.097 に答える
-1

同様の問題がありました。で必要なビューは 3 つだけなので、ビューをキャッシュしますViewPager。前方にスライドするとすべて問題ありませんが、後方にスライドし始めるとエラーが発生し、「私のビューには既に親があります」と表示されます。解決策は、不要なアイテムを手動で削除することです。

@Override
    public Object instantiateItem(ViewGroup container, int position) {
        int localPos = position % SIZE;
        TouchImageView view;
        if (touchImageViews[localPos] != null) {
            view = touchImageViews[localPos];
        } else {
            view = new TouchImageView(container.getContext());
            view.setLayoutParams(new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT));
            touchImageViews[localPos] = view;
        }
        view.setImageDrawable(mDataModel.getPhoto(position));
        Log.i(IRViewPagerAdpt.class.toString(), "Add view " + view.toString() + " at pos: " + position + " " + localPos);
        if (view.getParent() == null) {
        ((ViewPager) container).addView(view);
    }
        return view;
    }

    @Override
    public void destroyItem(ViewGroup container, int position, Object view) {
        //      ((ViewPager) container).removeView((View) view);
        Log.i(IRViewPagerAdpt.class.toString(), "remove view " + view.toString() + " at pos: " + position);
    }

..................

private static final int SIZE = 3;
private TouchImageView[] touchImageViews = new TouchImageView[SIZE];
于 2013-05-24T14:38:40.377 に答える
-1

私にとって、アプリのプロセスが強制終了された後、問題はアクティビティに戻ってきました。Android ソースから変更されたカスタム ビュー ページャー アダプターを使用しています。ビュー ページャーはアクティビティに直接埋め込まれています。

通話中viewPager.setCurrentItem(position, true);

(アニメーション付き)データを設定した後、notifyDataSetChanged()は機能しているように見えますが、パラメーターがfalseに設定されている場合は機能せず、フラグメントは空白です。これは、誰かに役立つかもしれないエッジケースです。

于 2017-01-03T18:19:46.503 に答える