ViewPagerはコンテンツを再描画せず、空白のまま/空白にします


86

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

私たちが観察しているのは、ページがビューツリーを更新しない、つまり空白のままである、またはページング時にページが消えることさえあるということです。数回ページングを行うと、コンテンツが突然再表示されます。Androidにはここでビューの更新がないようです。また、階層ビューアを使用してデバッグする場合、ビューを選択すると常に再表示されることに気付きました。これは、階層ビューアが選択したビューを強制的に再描画するためと思われます。

ただし、プログラムでこの作業を行うことはできませんでした。リストビュー、またはビューページャー全体を無効にしても効果はありませんでした。

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

他の誰かもこれらの問題に遭遇していますか、それともこれを引き起こしている可能性があるものについての考えがありますか?

回答:


44

私たちはついに解決策を見つけることができました。どうやら私たちの実装は2つの問題に苦しんでいました:

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

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

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

そして:

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

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

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


2
同じ問題が発生していますが、onDestroyを処理するFragmentPagerAdapterに問題があります。ここにある他のソリューションも機能しません。
グレッグエニス

14
また、問題になる可能性があるのは、誰かがGetChildFragmentManagerの代わりにgetFragmentMangerを使用していることです
Boy

@Boy GetChildFragmentManagerとは何ですか?
穴に火を放ちます

1
@Boy Wooooow ....更新するものが何も得られなかった理由を理解しようとして、2日間ずっと髪を抜いていた。ありがとうございました!(彼らは本当に、本当に、childfragmentmanagerを使用するためにGoogleのドキュメントに通知を置くべきです、それはあなたが別のマネージャーを必要とするということではありません)。
user0721090601 2017年

@futtetennista私はこの問題に直面して。..同じやってる..あなたは確認することができます。.. stackoverflow.com/questions/61727835/...
AskQ

55

ViewPagerフラグメント内に設定されている場合は、パラメータとしての代わりにをFragmentPagerAdapter使用してを初期化します。getChildFragmentManager()getSupportFragmentManager()FragmentPagerAdapter

mAdapter = new MyFragmentPagerAdapter(getChildFragmentManager());

の代わりに

mAdapter = new MyFragmentPagerAdapter(getSupportFragmentManager());

2
良いキャッチです
。FragmentPagerAdapter

1
ありがとう-これはうまくいきました。再作成されたフラグメントにネストされたを強制しようとgetItem()してFragmentPagerAdapterいました。
kosiara-Bartosz Kosarzycki 2016

この作品は魅力のようです。この問題は、ビューページャーが断片的に膨らんでいることが原因だと思います
Thecarisma 2017年

このようなときは、ミディアムのような拍手があればいいのにと思います。そうすれば、1000回以上拍手できます。ニースの仕事、これが受け入れ答えなければなりません
Codelicious

21

私はまったく同じ問題を抱えていましたが、実際には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);
}

ヒントをありがとう。かなり長い間問題を調査してきました。
Moritz

2
これは私にとってはうまくいきましたが、最初のものが問題である理由を知っていますか?
HannahMitt 2014

@HannahMitt removeViewAtは、ViewPagerの現在の子リスト内の特定の位置にあるビューをすべて削除し、ページは任意の順序でViewPagerに追加できます(したがって、ページ0は実際にはインデックス1などのViewPagerにある可能性があります)。removeViewは子のリストを反復処理し、指定されたオブジェクトを正確に削除します。

2
動作しません:ビューをフラグメントにキャストできません。ここのオブジェクトはフラグメントです。
FRK 2017

viewpagerは静的でなければなりませんか?
カナガリンガム

10

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

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

基本的に、すべてが変更されたことをViewPagerに通知します(そして、すべてを強制的に再インスタンス化します)。それは私が頭のてっぺんから考えることができる唯一のことです。


1
はい、私はこのスレッドでこのオプションについて読みました:stackoverflow.com/questions/7263291/…-しかし、これは大ハンマーのアプローチのようです。確かにもっとエレガントな方法があるに違いありませんか?リストをポケットベルページとして使用することに関連しているのでしょうか。
マティアス

これはちょっとした大ハンマーのアプローチですが、そこから戻ることができます。つまり、メソッドから正しい値を返します。
Chris Banes 2012

@ChrisBanesあなたが呼んreturn POSITION_NONE;でいるように、forces it to re-instantiate everythingしかし私の場合、私はすべて再インスタンス化したくないのでview、既存のものをクリアせずに削除することは可能でしょうか?私に知らせてください
Ritesh Adulkar 2018

あなたは味わう生活している
mask8

2

あまりにも多くのソリューションを試しましたが、予期せずviewPager.post()機能しました

 mAdapter = new NewsVPAdapter(getContext(), articles);
    viewPager.post(new Runnable() {
        @Override
        public void run() {
            viewPager.setAdapter(mAdapter);
        }
    });

1
我が神よ。なぜ誰もこれに賛成しなかったのですか?フラグメントを再入力すると、内部のビューページャーがレンダリングされないという奇妙な動作が発生しました。このように少し遅らせると、実際に問題が解決しました。
ふごぐご

0

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ます。幸運を!


ありがとう-私はそれを見ていきます!おそらく、私たちが間違っていることに気付くでしょう。
マティアス

0

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

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

したがって、含まれているビューを確認してください。それらがViewからサブクラス化され、requestLayoutなどを盲目的にオーバーライドしないことを確認してください。


0

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


0

ViewPagerとFragmentStatePagerAdapterを使用しているときに、これと同じ問題が発生しました。3秒の遅延のあるハンドラーを使用してinvalidate()とrequestLayout()を呼び出そうとしましたが、機能しませんでした。動作したのは、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();
     }

0

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

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


-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];

-1

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

呼び出し viewPager.setCurrentItem(position, true);

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


-1

Kotlinユーザーの場合:

あなたの断片で; のchildFragmentManager代わりに使用viewPagerAdapter

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.