フラグメントがViewPagerに表示される時期を判断する方法


754

問題:フラグメントonResume()にはViewPagerフラグメントが実際に目に見えるようになる前に発射されます。

たとえば、との2つのフラグメントがViewPagerありFragmentPagerAdapterます。2番目のフラグメントは許可されたユーザーのみが使用でき、フラグメントが表示されたときに(アラートダイアログを使用して)ログインするようユーザーに要求する必要があります。

しかし、ViewPagerは、2番目のフラグメントをキャッシュするために最初のフラグメントが表示されるときに2番目のフラグメントを作成し、ユーザーがスワイプを開始するとそれを表示します。

したがって、onResume()イベントは、2番目のフラグメントで表示されるずっと前に発生します。そのため、2番目のフラグメントが表示されて適切なタイミングでダイアログが表示されたときに発生するイベントを見つけようとしています。

これはどのように行うことができますか?


18
「ViewPagerとFragmentPagerAdapterを含む2つのフラグメントがあります。2番目のフラグメントは、許可されたユーザーのみが使用でき、フラグメントが表示されたときにログインを要求する必要があります(アラートダイアログ)。」-私見、それはひどいUXです。ユーザーが水平にスワイプしたためにダイアログをポップアップすると、Playストアで1つ星の評価が表示されます。
CommonsWare

「ログイン」ボタンでTextViewに情報を表示するだけの方が良いですか?その場合の解決策は何ですか?
4ntoine

5
「表示されない場合、データをロードする必要はありません。」-では、それをに入れるべきではありませんViewPager。2ページのページャでは、好きかどうかにかかわらず、両方のページがすぐに読み込まれます。のユーザーエクスペリエンスでViewPagerは、スワイプするとすぐにコンテンツが表示され、しばらくすると表示されないことが想定されています。そのViewPagerため、ユーザーエクスペリエンスを確保するために、表示されているものよりも先にページを初期化します。
CommonsWare 2012

2
:ViewPagerは柔軟性は十分ではありません、最小setOffscreenPageLimitが1であるので、それはキャッシュをオフにすることはできませんようだstackoverflow.com/questions/10073214/...。この理由はありません。予想される動作(強制的なキャッシュの場合)はフラグメントを作成することですが、フラグメントが表示されたときにフラグメントのonResume()を起動します。
4ntoine

1
少し遅れますが、同じ問題に直面している人 は、この問題を処理し、いくつかの追加機能を提供するFragmentViewPagerライブラリ(私は作成者です)を試すことができます。サンプルについては、プロジェクトのGitHubページまたはこのstackoverflowの回答を確認してください。
S. Brukhanda 2016年

回答:


582

フラグメントがViewPagerに表示される時期を判断する方法

をオーバーライドすることで、次のことができsetUserVisibleHintますFragment

public class MyFragment extends Fragment {
    @Override
    public void setUserVisibleHint(boolean isVisibleToUser) {
        super.setUserVisibleHint(isVisibleToUser);
        if (isVisibleToUser) {
        }
        else {
        }
    }
}

7
本日のAndroidサポートライブラリの更新(リビジョン11)のおかげで、ユーザーに表示されるヒントの問題が最終的に修正されました。ViewPagerでユーザーに表示されるヒントを使用しても安全です。
Oasis Feng

58
onCreateViewが呼び出される前にsetUserVisibleHintメソッドが呼び出されることがわかりました。これにより、初期化の追跡が困難になります。
AndroidDev 2015年

11
@AndroidDevヒントがtrueであるときにコードを実行したいが、ビューツリーが既に初期化されている必要がある場合はisResumed()、NPEを回避するためにコードのブロックをラップするだけです。私のためにうまく働いています。
Ravi Thapliyal 2015年

13
SDKがデフォルトで提供する必要のあるものに対して非常に多くの異なるハックが存在するのは、とんでもないことです。
Mike6679 2017年

15
setUserVisibleHintは非推奨になりました
SR

525

更新:Androidサポートライブラリ(リビジョン11)は、ユーザーに表示されるヒントの問題を最終的に修正しました。フラグメントにサポートライブラリを使用すると、安全に使用getUserVisibleHint()またはオーバーライドできます。setUserVisibleHint()、gornの回答で説明されているように、して変更をキャプチャ。

更新1ここに、の1つの小さな問題がありgetUserVisibleHint()ます。この値はデフォルトですtrue

// Hint provided by the app that this fragment is currently visible to the user.
boolean mUserVisibleHint = true;

したがって、setUserVisibleHint()が呼び出される前に使用しようとすると問題が発生する可能性があります。回避策として、onCreateこのような方法で値を設定する場合があります。

public void onCreate(@Nullable Bundle savedInstanceState) {
    setUserVisibleHint(false);

時代遅れの答え:

ほとんどのユースケースでは、ViewPager一度に1ページを表示していますが、使用している場合は、プリキャッシュされたフラグメントはまた、「目に見える」状態(実際には見えない)に入れているFragmentStatePagerAdapter中でAndroid Support Library pre-r11

私は上書きします:

public class MyFragment extends Fragment {
    @Override
    public void setMenuVisibility(final boolean visible) {
        super.setMenuVisibility(visible);
        if (visible) {
            // ...
        }
    }
   // ...
}

フラグメントのフォーカス状態をキャプチャするには、「可視性」の最も適切な状態だと思います。なぜなら、ViewPagerの1つのフラグメントだけが、実際にメニュー項目を親アクティビティの項目と一緒に配置できるからです。


73
getActivity()の使用には注意してください。最初の起動時にはnullになります。
vovkab

10
setUserVisibleHint()は常にFragmentPagerAdapterで適切に機能していることに注意してください。
louielouie 2013

8
この解決策(そしてまた解決策)は少し遅れています。ビューページャーがすばやくスワイプされて複数回繰り返される場合、このメソッドは遅延して呼び出されます(フラグメントが非表示/非表示になった場合)。
AsafK 2014年

3
が呼び出されたtrueときにgetUserVisibleHint()を取得していますが、呼び出されたときにメニューがまだ作成されていないようです。最終的に、表示されているフラグメントのメニューのみが必要なときに、メニューの2つのオプションが追加されます。これに関して何か提案はありますか?onCreateOptionsMenusetUserVisibleHint
cYrixmorten 2014

13
setUserVisibleHintは非推奨になりました
SR

143

これにより、通常のonResume()動作に戻ります。ホームキーを押してアプリを終了し、アプリを再起動することでうまく機能します。 onResume()続けて2回呼び出されることはありません。

@Override
public void setUserVisibleHint(boolean visible)
{
    super.setUserVisibleHint(visible);
    if (visible && isResumed())
    {
        //Only manually call onResume if fragment is already visible
        //Otherwise allow natural fragment lifecycle to call onResume
        onResume();
    }
}

@Override
public void onResume()
{
    super.onResume();
    if (!getUserVisibleHint())
    {
        return;
    }

    //INSERT CUSTOM CODE HERE
}

4
まさに私が探していたもの。解きに問題setUserVisibleHintの前に呼び出されるonCreateViewと、そのsetUserVisibleHintアプリがバックグラウンドになった場合と呼ばれ、前景ません。驚くばかり!ありがとうございました!
Alex

11
自分でonResumeを呼び出すのはかなり悪い考えだと思いますが、そうでなければ、この投稿の質問に答えるために必要なものはすべてsetUserVisibleHint関数にあります。
クエンティンG.

4
私はむしろ平野実装したいonVisibleToUser()メソッドをしてからのことを求めているonResume()からsetUserVisibleHint(boolean)呼び出しの代わりにonResume()自分自身をし、ライフサイクルコールバックと干渉します。そうでなければ、このアプローチはうまく機能すると思います、ありがとう!
ステファンヘニングセン2018年

1
はい、上記のコメントに同意します。一般に、クラスのパブリックメソッドからパブリックメソッドを呼び出さないようにしてください。プライベートメソッドを作成し、両方のパブリックメソッドから呼び出します。
JohanFranzén18年

4
setUserVisibleHintは非推奨です。
Hamid Reza

70

これは別の使用方法onPageChangeListenerです:

  ViewPager pager = (ViewPager) findByViewId(R.id.viewpager);
  FragmentPagerAdapter adapter = new FragmentPageAdapter(getFragmentManager);
  pager.setAdapter(adapter);
  pager.setOnPageChangeListener(new OnPageChangeListener() {

  public void onPageSelected(int pageNumber) {
    // Just define a callback method in your fragment and call it like this! 
    adapter.getItem(pageNumber).imVisible();

  }

  public void onPageScrolled(int arg0, float arg1, int arg2) {
    // TODO Auto-generated method stub

  }

  public void onPageScrollStateChanged(int arg0) {
    // TODO Auto-generated method stub

  }
});

1
おかげで、このソリューションはうまく機能します。これは、(v4)フラグメントサポートパッケージを使用する古いプラットフォーム向けのビルドに推奨されるソリューションである必要があります
Frank Yin

7
FragmentStatePagerAdapterを使用し、getItem()で新しいFragmentインスタンスをインスタンス化している場合、ビューページャーが取得したものではなく、新しいフラグメントでのみ状態を設定するため、これは期待した結果を与えられなかったことに注意する価値があります。
ベンピアソン

1
このソリューションは、フラグメントが表示されたときに何かを実行したい場合に適しています。フラグメントの表示状態は保持されません(つまり、フラグメントがいつ非表示になるかはわかりません)。
AsafK 2014年

私も同じタイプの問題です。問題の解決を手伝ってください。私のポスト:stackoverflow.com/questions/23115283/...
Jeeten Parmar

これは、フラグメントのiamVisibleメソッドで使用しているアダプターにnullポインター例外を与えています。フラグメントが表示されている場合にのみ、ネットワークから受信したデータを設定しようとしています。
zaphod100.10 2015年

58

setUserVisibleHint()時々前後 に呼ばれonCreateView()、トラブルの原因になります。

これを克服するには、メソッドisResumed()内もチェックする必要がありますsetUserVisibleHint()。しかし、この場合、フラグメントが再開されて表示さsetUserVisibleHint()れる場合にのみ呼び出されることに気付きました。作成時ではありません。

あなたはフラグメントがあるときに何かを更新したいのであればvisible、中に両方のあなたの更新機能を置くonCreate()setUserVisibleHint()

@Override
public View onCreateView(...){
    ...
    myUIUpdate();
    ...        
}
  ....
@Override
public void setUserVisibleHint(boolean visible){
    super.setUserVisibleHint(visible);
    if (visible && isResumed()){
        myUIUpdate();
    }
}

更新:それでも私myUIUpdate()は2回呼び出されることがあることに気づきましたが、その理由は、3つのタブがあり、このコードが2番目のタブにある場合、最初の1番目のタブを開いたときに、2番目のタブも表示されずmyUIUpdate()呼び出されます。次に、2番目のタブにスワイプすると、myUIUpdate()from if (visible && isResumed())が呼び出され、その結果、myUIUpdate()1秒間に2回呼び出されることがあります。

他の問題がある!visiblesetUserVisibleHint 1)フラグメント画面から出たときと、2)作成前に、最初にフラグメント画面に切り替えたときの両方で呼び出されます。

解決:

private boolean fragmentResume=false;
private boolean fragmentVisible=false;
private boolean fragmentOnCreated=false;
...

@Override
public View onCreateView(...){
    ...
    //Initialize variables
    if (!fragmentResume && fragmentVisible){   //only when first time fragment is created
        myUIUpdate();
    }
    ...        
}

@Override
public void setUserVisibleHint(boolean visible){
    super.setUserVisibleHint(visible);
    if (visible && isResumed()){   // only at fragment screen is resumed
        fragmentResume=true;
        fragmentVisible=false;
        fragmentOnCreated=true;
        myUIUpdate();
    }else  if (visible){        // only at fragment onCreated
        fragmentResume=false;
        fragmentVisible=true;
        fragmentOnCreated=true;
    }
    else if(!visible && fragmentOnCreated){// only when you go out of fragment screen
        fragmentVisible=false;
        fragmentResume=false;
    }
}

説明:

fragmentResumefragmentVisible:作り確かmyUIUpdate()ではonCreateView()断片がない履歴書には、作成された状態で表示された場合にのみ呼び出されます。また、1番目のタブにいるときの問題も解決します。2番目のタブが表示されていなくても作成されます。これはそれを解決し、フラグメント画面が表示されるかどうかを確認しますonCreate

fragmentOnCreated:フラグメントが表示されないようにし、フラグメントを最初に作成するときに呼び出されないようにします。したがって、このif句は、フラグメントからスワイプしたときにのみ呼び出されます。

更新 このすべてのBaseFragmentコードをこのようなコードに配置して、メソッドをオーバーライドできます。


1
ソリューションが完全に機能するのは、1つのシナリオを除いてです。フラグメントが正常に開かれ、ボタンをクリックして別のフラグメントに置き換えた後、クリックしてバックスタックから新しいフラグメントをポップすると、そのケースsetUserVisibleHintは呼び出されませんでした。 !そして中のonCreateView方法fragmentVisiblefalse!?そのため、フラグメントは空で表示されました。何かご意見は。?
Alaa AbuZarifa

28

目に見える状態で検出するにFragmentViewPager使用 するだけsetUserVisibleHintでは不十分だと私は確信しています。
これがフラグメントが表示されているか非表示であるかを確認するための私の解決策です。まず、viewpagerを起動するときに、ページを切り替えて、別のアクティビティ/フラグメント/背景/前景に移動します `

public class BaseFragmentHelpLoadDataWhenVisible extends Fragment {
    protected boolean mIsVisibleToUser; // you can see this variable may absolutely <=> getUserVisibleHint() but it not. Currently, after many test I find that

    /**
     * This method will be called when viewpager creates fragment and when we go to this fragment background or another activity or fragment
     * NOT called when we switch between each page in ViewPager
     */
    @Override
    public void onStart() {
        super.onStart();
        if (mIsVisibleToUser) {
            onVisible();
        }
    }

    @Override
    public void onStop() {
        super.onStop();
        if (mIsVisibleToUser) {
            onInVisible();
        }
    }

    /**
     * This method will called at first time viewpager created and when we switch between each page
     * NOT called when we go to background or another activity (fragment) when we go back
     */
    @Override
    public void setUserVisibleHint(boolean isVisibleToUser) {
        super.setUserVisibleHint(isVisibleToUser);
        mIsVisibleToUser = isVisibleToUser;
        if (isResumed()) { // fragment have created
            if (mIsVisibleToUser) {
                onVisible();
            } else {
                onInVisible();
            }
        }
    }

    public void onVisible() {
        Toast.makeText(getActivity(), TAG + "visible", Toast.LENGTH_SHORT).show();
    }

    public void onInVisible() {
        Toast.makeText(getActivity(), TAG + "invisible", Toast.LENGTH_SHORT).show();
    }
}

説明 以下のlogcatを注意深くチェックすると、このソリューションが機能する理由がわかると思います

最初の打ち上げ

Fragment1: setUserVisibleHint: isVisibleToUser=false isResumed=false
Fragment2: setUserVisibleHint: isVisibleToUser=false isResumed=false
Fragment3: setUserVisibleHint: isVisibleToUser=false isResumed=false
Fragment1: setUserVisibleHint: isVisibleToUser=true isResumed=false // AT THIS TIME isVisibleToUser=true but fragment still not created. If you do something with View here, you will receive exception
Fragment1: onCreateView
Fragment1: onStart mIsVisibleToUser=true
Fragment2: onCreateView
Fragment3: onCreateView
Fragment2: onStart mIsVisibleToUser=false
Fragment3: onStart mIsVisibleToUser=false

ページ2に移動

Fragment1: setUserVisibleHint: isVisibleToUser=false isResumed=true
Fragment2: setUserVisibleHint: isVisibleToUser=true isResumed=true

ページ3に移動

Fragment2: setUserVisibleHint: isVisibleToUser=false isResumed=true
Fragment3: setUserVisibleHint: isVisibleToUser=true isResumed=true

バックグラウンドに移動:

Fragment1: onStop mIsVisibleToUser=false
Fragment2: onStop mIsVisibleToUser=false
Fragment3: onStop mIsVisibleToUser=true

フォアグラウンドに移動

Fragment1: onStart mIsVisibleToUser=false
Fragment2: onStart mIsVisibleToUser=false
Fragment3: onStart mIsVisibleToUser=true

デモプロジェクトはこちら

それが役に立てば幸い


1
フラグメントがフラグメントを構成する別のビューページャーを持っている場合は機能しません。
Shihab Uddin 2018年

申し訳ありませんが、十分な時間がないため、現時点では回答を投稿できません。可能であれば、問題についてgithub.com/PhanVanLinh/AndroidViewPagerSkeleton/tree/masterで gitを参照してください。SubChildContainerFragment検出に使用されますa fragment has another view pager which also consists fragment。混合SubChildContainerFragmentChildContainerFragmentて1クラスにできます。お役に立てば幸いです。私は後で完全な回答を投稿します
ファンファンリン

26
package com.example.com.ui.fragment;


import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;

import com.example.com.R;

public class SubscribeFragment extends Fragment {

    @Override
    public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        View view = inflater.inflate(R.layout.fragment_subscribe, container, false);
        return view;
    }

    @Override
    public void setUserVisibleHint(boolean isVisibleToUser) {
        super.setUserVisibleHint(isVisibleToUser);

        if (isVisibleToUser) {
            // called here
        }
    }

    @Override
    public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);
    }
}

2
これは受け入れられる答えになるはずです。android.app.Fragmentでも動作します。これは大きなボーナスです。
RajV 2017年

setUserVisibleHint非推奨
ekashking

23

ViewPager2ViewPagerバージョンからandroidx.fragment:fragment:1.1.0あなただけ使用することができますonPauseし、onResumeユーザーのために、現在表示されているフラグメントを決定するためのコールバック。onResumeフラグメントが表示されたときと、表示されなくなったときにコールバックが呼び出されonPauseます。

ViewPager2の場合、これはデフォルトの動作ですが、同じ動作を古い製品に対してViewPager簡単に有効にすることができます。

最初のViewPagerでこの動作を有効にするにFragmentPagerAdapter.BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENTは、FragmentPagerAdapterコンストラクターの2番目の引数としてパラメーターを渡す必要があります。

FragmentPagerAdapter(fragmentManager, BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT)

注:setUserVisibleHint()メソッドとFragmentPagerAdapter 1つのパラメーターを持つコンストラクターは、android jetpackの新しいバージョンのFragmentでは非推奨になりました。


1
素敵な解決策をありがとう。昔からこれを探していました。素晴らしい仕事
アヌラーグスリバスタバ

1
ViewPager2の場合、オプションBEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENTはデフォルトの動作ではないため、手動で設定する必要があります。このオプションを設定すると、あなたの解決策が私にとってうまくいきました。ありがとう。
driAn

1
2020年4月現在、これは更新されたソリューションであり、魅力のように機能します。
Prakash

1
@ 4ntoineは、この回答を正解として受け入れることを検討してください。現時点ではこれが正しく、ほとんどの回答は非推奨のsetUserVisibleHintメソッドを使用しています
Jorn Rigter

16

サブクラスでオーバーライドsetPrimaryItem()FragmentPagerAdapterます。私はこの方法を使用していますが、うまく機能します。

@Override
public void setPrimaryItem(ViewGroup container, int position, Object object) {
    // This is what calls setMenuVisibility() on the fragments
    super.setPrimaryItem(container, position, object);

    if (object instanceof MyWhizBangFragment) {
        MyWhizBangFragment fragment = (MyWhizBangFragment) object;
        fragment.doTheThingYouNeedToDoOnBecomingVisible();
    }
}

1
これはほとんど機能しましたが、NPEに問題があり、何度も呼び出されていました。下に代替案を投稿しました!
Gober


@Goberが最初にObjectオブジェクトを保存し、FragmentStateAdapterがsetPrimaryItem()メソッドで行うのと同じように、同じObjectオブジェクトを使用したフォローアップコールをチェックして無視します
wanglugao

12

Fragment.onHiddenChanged()そのためにオーバーライドします。

public void onHiddenChanged(boolean hidden)

隠された状態のときに呼び出されます( isHidden()フラグメント)が変更されます。フラグメントは隠されていない状態から始まります。これは、フラグメントが状態を変更するたびに呼び出されます。

パラメータ
hidden- boolean:フラグメントが非表示になった場合はtrue、非表示の場合はfalse。


3
このメソッドは現在非推奨であり、もう使用できません
bluewhile

4
@bluewhile廃止予定はどこにありますか?developer.android.com/reference/android/app/...
チェル

1
私はこれを使用して成功しましたが、ドキュメントには非推奨であることは示されていません。
Trevor、

1
@ bluewhile、4.1(api 16)ではまだ非推奨ではありません。
dan

8
私はAPIレベル19を使用しており、これは非推奨ではありませんが、宣伝どおりに機能しないことを確認できます。たとえば、フラグメントが別のフラグメントによって隠されている場合、onHiddenChangedは呼び出されません。

4

私はそれを理解しonCreateOptionsMenuonPrepareOptionsMenu断片のみの場合に呼び出されるメソッド本当に見えます。私はこれらのように動作するメソッドを見つけることができませんでした。また試しましたOnPageChangeListenerが、たとえば、onCreateメソッドで初期化された変数が必要な状況では機能しませんでした。

そのため、これらの2つの方法は、この問題の回避策として、特に小さなジョブや短いジョブに対して使用できます。

私は、これはより良い解決策ですが、最善ではないと思います。私はこれを使用しますが、同時により良い解決策を待ちます。

よろしく。


2

kris larsonがpageradapterのsetPrimaryItemをオーバーライドする別の解決策がここに投稿され、ほとんど私にとってはうまくいきました。ただし、このメソッドはセットアップごとに複数回呼び出されます。また、フラグメント内のビューなどからNPEを取得しました。これは、このメソッドが呼び出された最初の数回は準備ができていないためです。次の変更により、これは私にとってうまくいきました:

private int mCurrentPosition = -1;

@Override
public void setPrimaryItem(ViewGroup container, int position, Object object) {
    super.setPrimaryItem(container, position, object);

    if (position == mCurrentPosition) {
        return;
    }

    if (object instanceof MyWhizBangFragment) {
        MyWhizBangFragment fragment = (MyWhizBangFragment) object;

        if (fragment.isResumed()) {
            mCurrentPosition = position;
            fragment.doTheThingYouNeedToDoOnBecomingVisible();
        }
    }
}

2

フラグメント内に次のコードを追加します

@Override
public void setMenuVisibility(final boolean visible) 
 {
    super.setMenuVisibility(visible);
    if (visible && isResumed()) 
     {

     }
}

これはViewPagerFragmentsでのみ機能します。通常の活動の断片のためではありません。
AndroidGuy 2017年

2

FragmentStatePagerAdaptersと3つのタブで作業中に同じ問題が発生しました。最初のタブがクリックされるたびにDilaogを表示し、他のタブをクリックすると非表示にする必要がありました。

オーバーライドsetUserVisibleHint()だけでは、現在表示されているフラグメントを見つけるのに役立ちませんでした。

3番目のタブからクリックした場合-----> 1番目のタブ。2番目のフラグメントと1番目のフラグメントで2回トリガーされました。isResumed()メソッドと組み合わせました。

    @Override
public void setUserVisibleHint(boolean isVisibleToUser) {
    super.setUserVisibleHint(isVisibleToUser);
    isVisible = isVisibleToUser;

    // Make sure that fragment is currently visible
    if (!isVisible && isResumed()) {
        // Call code when Fragment not visible
    } else if (isVisible && isResumed()) {
       // Call code when Fragment becomes visible.
    }

}

2

MVPの特別なケースでは、フラグメントがビューが表示されたことをプレゼンターに通知する必要があり、プレゼンターはのDaggerによって挿入されfragment.onAttach()ます。

setUserVisibleHint()十分ではありません。対処する必要がある3つの異なるケースが検出されました(onAttach()プレゼンターがいつ利用できるかを知るために記載されています):

  1. フラグメントが作成されました。システムは次の呼び出しを行います。

    setUserVisibleHint() // before fragment's lifecycle calls, so presenter is null
    onAttach()
    ...
    onResume()
  2. フラグメントはすでに作成されており、ホームボタンが押されています。アプリをフォアグラウンドに復元すると、次のように呼び出されます。

    onResume()
  3. 向きの変更:

    onAttach() // presenter available
    onResume()
    setUserVisibleHint()

可視性のヒントがプレゼンターに1回だけ到達するようにしたいので、次のようにします。

@Nullable
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
    View root = inflater.inflate(R.layout.fragment_list, container, false);
    setHasOptionsMenu(true);

    if (savedInstanceState != null) {
        lastOrientation = savedInstanceState.getInt(STATE_LAST_ORIENTATION,
              getResources().getConfiguration().orientation);
    } else {
        lastOrientation = getResources().getConfiguration().orientation;
    }

    return root;
}

@Override
public void onResume() {
    super.onResume();
    presenter.onResume();

    int orientation = getResources().getConfiguration().orientation;
    if (orientation == lastOrientation) {
        if (getUserVisibleHint()) {
            presenter.onViewBecomesVisible();
        }
    }
    lastOrientation = orientation;
}

@Override
public void setUserVisibleHint(boolean isVisibleToUser) {
    super.setUserVisibleHint(isVisibleToUser);
    if (presenter != null && isResumed() && isVisibleToUser) {
        presenter.onViewBecomesVisible();
    }
}

@Override public void onSaveInstanceState(Bundle outState) {
    super.onSaveInstanceState(outState);
    outState.putInt(STATE_LAST_ORIENTATION, lastOrientation);
}

2

で検出していfocused viewます!

これは私のために働く

public static boolean isFragmentVisible(Fragment fragment) {
    Activity activity = fragment.getActivity();
    View focusedView = fragment.getView().findFocus();
    return activity != null
            && focusedView != null
            && focusedView == activity.getWindow().getDecorView().findFocus();
}

1

この問題は、ビューページャーのフラグメントが画面に表示されているときにタイマーを起動させようとしたときに発生しました。

タイマーは常に、フラグメントがユーザーに表示される直前に開始されました。これはonResume()、フラグメントが表示される前にフラグメント内のメソッドが呼び出されるためです。

私の解決策は、onResume()メソッドでチェックを行うことでした。フラグメント8がビューページャーの現在のフラグメントであるときに、特定のメソッド 'foo()'を呼び出したかったのです。

@Override
public void onResume() {
    super.onResume();
    if(viewPager.getCurrentItem() == 8){
        foo();
        //Your code here. Executed when fragment is seen by user.
    }
}

お役に立てれば。私はこの問題が頻繁に現れるのを見てきました。これは私が見た中で最も簡単な解決策のようです。その他の多くは、下位のAPIなどと互換性がありません。


1

同じ問題がありました。ViewPager他のフラグメントライフサイクルイベントを実行し、その動作を変更できませんでした。フラグメントと使用可能なアニメーションを使用して、単純なページャーを作成しました。 SimplePager



1

私はSectionsPagerAdapterを子フラグメントでサポートしているので、多くの頭痛の種の後で、このトピックの解決策に基づいて最終的に機能するバージョンを取得しました。

public abstract class BaseFragment extends Fragment {

    private boolean visible;
    private boolean visibilityHintChanged;

    /**
     * Called when the visibility of the fragment changed
     */
    protected void onVisibilityChanged(View view, boolean visible) {

    }

    private void triggerVisibilityChangedIfNeeded(boolean visible) {
        if (this.visible == visible || getActivity() == null || getView() == null) {
            return;
        }
        this.visible = visible;
        onVisibilityChanged(getView(), visible);
    }

    @Override
    public void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        if (!visibilityHintChanged) {
            setUserVisibleHint(false);
        }
    }

    @Override
    public void onResume() {
        super.onResume();
        if (getUserVisibleHint() && !isHidden()) {
            triggerVisibilityChangedIfNeeded(true);
        }
    }

    @Override
    public void onHiddenChanged(boolean hidden) {
        super.onHiddenChanged(hidden);
        triggerVisibilityChangedIfNeeded(!hidden);
    }

    @Override
    public void setUserVisibleHint(boolean isVisibleToUser) {
        super.setUserVisibleHint(isVisibleToUser);
        visibilityHintChanged = true;
        if (isVisibleToUser && isResumed() && !isHidden()) {
            triggerVisibilityChangedIfNeeded(true);
        } else if (!isVisibleToUser) {
            triggerVisibilityChangedIfNeeded(false);
        }
    }

    @Override
    public void onPause() {
        super.onPause();
        triggerVisibilityChangedIfNeeded(false);
    }

    @Override
    public void onStop() {
        super.onStop();
        triggerVisibilityChangedIfNeeded(false);
    }

    protected boolean isReallyVisible() {
        return visible;
    }
}

言及するのを忘れて、親フラグメントセットに子フラグメントを追加したら、fragment.setUserVisibleHint(true);
Andoctorey、

また、子フラグメントを追加するには、getFragmentManager()の代わりにgetChildFragmentManager()を使用することを忘れないでください。
Andoctorey、

0

setUserVisibleHint(false)アクティビティ/フラグメントの停止時に呼び出されないことに注意してください。register/unregisterリスナーなどを適切に開始/停止することを確認する必要があります。

また、setUserVisibleHint(false)フラグメントが非表示の状態で始まる場合にも表示されます。unregisterその場合、以前に登録したことがないので、そこに行きたくないでしょう。

@Override
public void onStart() {
    super.onStart();

    if (getUserVisibleHint()) {
        // register
    }
}

@Override
public void onStop() {
    if (getUserVisibleHint()) {
        // unregister
    }

    super.onStop();
}

@Override
public void setUserVisibleHint(boolean isVisibleToUser) {
    super.setUserVisibleHint(isVisibleToUser);

    if (isVisibleToUser && isResumed()) {
        // register

        if (!mHasBeenVisible) {
            mHasBeenVisible = true;
        }
    } else if (mHasBeenVisible){
        // unregister
    }
}

0

これを実装する簡単な方法は、フラグメントに移動する前にユーザーがログインしているかどうかを確認することです。

MainActivityでは、onNavigationItemSelectedメソッド内で次のようなことを行うことができます。

 case R.id.nav_profile_side:


                if (User_is_logged_in) {

                    fragmentManager.beginTransaction()
                            .replace(R.id.content_frame
                                    , new FragmentProfile())
                            .commit();
                }else {

                    ShowLoginOrRegisterDialog(fragmentManager);

                }

                break;

ただし、ナビゲーションドロワーを使用している場合、ProfileFragmentには移動していませんが、ドロワーの選択はプロファイルに変更されます。

選択を現在の選択にリセットするには、以下のコードを実行します

        navigationView.getMenu().getItem(0).setChecked(true);

0

setUserVisibleHint(boolean visible)非推奨になりましたこれが正しい解決策です

FragmentPagerAdapter(fragmentManager, BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT)

ViewPager2ViewPagerバージョンからandroidx.fragment:fragment:1.1.0あなただけ使用することができますonPause()し、onResume()ユーザーのために、現在表示されているフラグメントを決定します。onResume()フラグメントが表示されたとき、および表示されなくなったときに呼び出されonPauseます。

最初のViewPagerでこの動作を有効にするにFragmentPagerAdapter.BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENTは、FragmentPagerAdapterコンストラクターの2番目の引数としてパラメーターを渡す必要があります。


-4

関連するFragmentStatePagerAdapterのCountメソッドをオーバーライドし、合計カウントから非表示にするページ数を引いた値を返します。

 public class MyAdapter : Android.Support.V13.App.FragmentStatePagerAdapter
 {   
     private List<Fragment> _fragments;

     public int TrimmedPages { get; set; }

     public MyAdapter(Android.App.FragmentManager fm) : base(fm) { }

     public MyAdapter(Android.App.FragmentManager fm, List<Android.App.Fragment> fragments) : base(fm)
     {
         _fragments = fragments;

         TrimmedPages = 0;
     }

     public override int Count
     {
         //get { return _fragments.Count; }
         get { return _fragments.Count - TrimmedPages; }
     }
 }

したがって、ViewPagerに最初に追加された3つのフラグメントがあり、何らかの条件が満たされるまで最初の2つだけが表示される場合は、TrimmedPagesを1に設定してページ数をオーバーライドし、最初の2ページのみを表示する必要があります。

これは最後のページには有効ですが、最初または中間のページには役立ちません(これを行う方法はたくさんありますが)。

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