[Android] ViewPagerでフラグメントが表示されるタイミングを判断する方法


Answers

ViewPagerでフラグメントが表示されるタイミングを判断する方法

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

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

問題:フラグメントが実際に見えるようになる前に、 ViewPager onResume()ViewPagerます。

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

ViewPagerViewPagerは、第2のフラグメントをキャッシュするために第1のフラグメントが見えるときに第2のフラグメントを作成し、ユーザがスワイプを開始したときにそれを見えるようにする。

したがって、 onResume()イベントは、表示される前に2番目のフラグメントで起動されます。 だからこそ、私は、第2の断片が見えるようになったときに、適切な瞬間にダイアログを表示するイベントを見つけることを試みています。

これはどうすればできますか?




ViewPager Fragmentを検出するには、 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 call when viewpager create fragment and when we go to this fragment from
     * background or another activity, fragment
     * NOT call 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 call 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

DEMOプロジェクトはこちら

希望する




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

setUserVisibleHint()で十分ではないため、解決する必要がある3つの異なるケースが検出されました( onAttach()onAttach()され、発表者がいつ利用可能かを知ることができます)。

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

    setUserVisibleHint() // before fragment's lifecycle calls, so presenter is null
    onAttach()
    ...
    onResume()
    
  2. フラグメントが作成され、ホームボタンが押されました。 アプリをフォアグラウンドに戻すときは、次のように呼ばれます:

    onResume()
    
  3. オリエンテーションの変更:

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

私たちは、可視性のヒントがプレゼンターに一度しか届かないようにしたいので、これは私たちがそれを行う方法です:

@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);
}



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);
    }
}



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

public void onHiddenChanged(boolean hidden)

フラグメントの非表示状態( isHidden()によって返される)が変更されたときに呼び出されます。 断片は隠されずに始まります。 これは、フラグメントが状態をその状態から変更するたびに呼び出されます。

パラメーター
hidden - boolean :フラグメントが現在非表示になっている場合はtrue、表示されていない場合はfalseです。




私は関連付けられた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ページのみを表示する必要があります。

これは最後のページにはうまくいくが、最初や途中のものは本当に助けにならない(これにはたくさんの方法があるが)。




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.
    }

}



ビューアのフラグメントが画面上に表示されてユーザが見ることができるときにタイマーを起動しようとしたときにこの問題が発生しました。

タイマーは、フラグメントがユーザーに見える直前に常に開始されました。 これは、フラグメント内の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などと互換性がありません。




onPageChangeListenerを使用する別の方法は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

  }
});



フラグメントでは、GestureDetectorを使用します。




断片の中に次のコードを追加する

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

     }
}