StackOverflowには似たような質問がたくさんありますが、私の質問は少し違います。
私は以下の構造のようなフラグメントの階層をネストしています:
_ Activity
|
|
AFragment
|
(ViewPager)
| |
| |
BFragment BFragment .....
|
(ViewPager)
| |
| |
CFragment CFragment ...
|
(ViewPager)
| |
| |
DFragment DFragment ...
_
今、私はDFragment
がユーザーに表示されているかどうかを知りたいですか?
StackOverflowから多くのソリューションを試しましたが、成功しませんでした。
私が試したのは:
setUserVisibleHint()
を試しましたが、true
の原因である上記の階層の複数のDFragment
に対してViewPager
を返します
私はこれらのリンクからも試しました: link1 、 link2 、 link など...しかし、実際の解決策は得られませんでした。
助けを待っています。ありがとうございました。
[〜#〜] update [〜#〜]
アダプタクラス
_class ViewPagerAdapter extends FragmentPagerAdapter {
private final List<Fragment> mFragmentList = new ArrayList<>();
private final List<String> mFragmentTitleList = new ArrayList<>();
public ViewPagerAdapter(FragmentManager manager) {
super(manager);
}
@Override
public Fragment getItem(int position) {
return mFragmentList.get(position);
}
@Override
public int getCount() {
return mFragmentList.size();
}
public void addFragment(Fragment fragment, String title) {
mFragmentList.add(fragment);
mFragmentTitleList.add(title);
}
@Override
public CharSequence getPageTitle(int position) {
return mFragmentTitleList.get(position);
}
}
_
フラグメントが膨らんだView
がデバイスの画面に表示されるかどうかを確認できます。
_
Fragment fragment = ... // retrieve from `ViewPager`'s adapter.
View fragmentRootView = fragment.getView();
if (fragmentRootView != null && fragmentRootView.getGlobalVisibleRect(new Rect())) {
// fragment is visible
} else {
// fragment is not visible
}
_
getGlobalVisibleRect()
は、ビューの一部がルートレベルで表示されている場合、true
を返します。
フラグが表示されているかどうかを確認する各フラグメントで、追加フラグを使用してoverride
setUserVisibleHint()
できます。例として
boolean isVisited = false;
@Override
public void setUserVisibleHint(boolean isVisibleToUser)
{
super.setUserVisibleHint(isVisibleToUser);
if (isVisibleToUser && !isVisited )
{
isVisited = true;
}
else if(isVisited)
{
// this fragment is already in front of user
}
}
私は同様の問題を抱えていました。サーバーのデータをfragment
に読み込む必要があったのは、ユーザーの目の前にあったときだけでした。上記のように解決しました。
それがあなたにも役立つことを願っています。
これを試して
@Override
public void setMenuVisibility(final boolean visible) {
super.setMenuVisibility(visible);
if (visible) {
}
else
{
}
}
これを試してみてください。
isVisible()
は、可視性チェックの追加レイヤーを追加します。
ViewPager viewPager=(ViewPager)findViewById(R.id.view_pager);
final Fragment[] fragments={new DFragment(),new MyFragment()}; //add all the other fragment objects present in the view pager......
viewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
@Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
}
@Override
public void onPageSelected(int position) {
if(fragments[position].isVisible()){
//Cool stuff to do.
}
}
@Override
public void onPageScrollStateChanged(int state) {
}
});
mViewPager.getCurrentItem()
メソッドを使用して、どのフラグメントが表示されるかを簡単にマッピングできます。このメソッドは、現在のフラグメントを見つけることができるint
値の番号を提供します。
-このメソッドの結果にアクセスして取得するには、2つのオプションがあります。
Static
viewPagerインスタンスを作成し、好きな場所で使用しますviewPager
インスタンスを作成します(findViewById()
)上記の2つのメソッドgetCurrentItem()
のいずれかをfragmentで使用して、現在表示されているフラグメントを見つけます。
例えば、
_public static ViewPager mViewPager;
_
MainActivity.mViewPager.getCurrentItem()
で現在のフラグメントを取得しますまた、どのフラグメントが表示されるかを示すメソッド:
_public void whichIsVisible() {
String msg = "";
int curFragNo = MainActivity.mViewPager.getCurrentItem();
switch (curFragNo) {
case 0:
msg = "AFragment is visible.";
break;
case 1:
msg = "BFragment is visible.";
break;
case 2:
msg = "CFragment is visible.";
break;
case 3:
msg = "DFragment is visible.";
break;
}
Toast.makeText(context, msg, Toast.LENGTH_SHORT).show();
}
_
(2)非静的使用の場合、
viewPager
インスタンスを取得し、viewPager=(ViewPager) view.getRootView().findViewById(R.id.container);
その後
viewPager.getCurrentItem()
を使用して、インデックスを取得し、使用状況に応じて現在表示されているフラグメントを見つけます。
私が試した限りでは、これはうまくいくと思います。試して、私に知らせてください。アクティビティでこのようなことをしてください。
public boolean isFragmentVisible(SwipeAdapter swipeAdapter) {
SwipeAdapter swipeAdapterA = fragmentA.getViewPagerAdapter();
BFragment bFragment = (BFragment) swipeAdapterA.getFragment(0);
if (bFragment != null) {
SwipeAdapter swipeAdapterB = bFragment.getViewPagerAdapter();
CFragment cFragment = (CFragment) swipeAdapterA.getFragment(0);
if (cFragment != null) {
SwipeAdapter swipeAdapterC = cFragment.getViewPagerAdapter();
DFragment dFragment = (DFragment) swipeAdapterA.getFragment(0);
if (dFragment != null) {
return dFragment.isFragmentVisible();
}
}
}
return false;
}
ビューページャーアダプターとしてこのクラスを使用します
class SwipeAdapter extends FragmentPagerAdapter {
private Map<Integer, String> mFragmentTags;
private FragmentManager mFragmentManager;
private String mPagetile[];
public SwipeAdapter(FragmentManager fm, Context context) {
super(fm);
mPagetile = context.getResources().getStringArray(R.array.pageTitle);
mFragmentManager = fm;
mFragmentTags = new HashMap<>();
}
@Override
public Fragment getItem(int position) {
switch (position) {
case 0:
return new "Your_fragment";
default:
break;
}
return null;
}
@Override
public Object instantiateItem(ViewGroup container, int position) {
Object obj = super.instantiateItem(container, position);
if (obj instanceof Fragment) {
Fragment f = (Fragment) obj;
String tag = f.getTag();
mFragmentTags.put(position, tag);
}
return obj;
}
public Fragment getFragment(int position) {
String tag = mFragmentTags.get(position);
if (tag == null)
return null;
return mFragmentManager.findFragmentByTag(tag);
}
@Override
public CharSequence getPageTitle(int position) {
return mPagetile[position];
}
@Override
public int getCount() {
return "Number of fragments";
}
}
DFragmentでこれを行います
boolean isVisible=false;
@Override
public void setMenuVisibility(boolean menuVisible) {
super.setMenuVisibility(menuVisible);
isVisible=menuVisible;
}
public boolean isFragmentVisible(){
return isVisible;
}
理由がわからなければ、うまくいったかどうか教えてください。
最も単純なスラウションは次のとおりです。 DF fragment
のonCreate
にboolean
にpreferences
を保存します。すなわちKey = is_fragment_visible_to_user
。そして、それをtrue
に変更します。 onStop
またはonDestroy
(要件を満たす方)の変更では、値はfalse
になります。これにより、設定値にアクセスして簡単に確認できます。複数のインスタンスの場合、タグを別のキーで保存できます。
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();
}
私が同様の問題を抱えていたとき、私はこのハッキーな方法を使用して解決しました
FragmentPagerAdapter
(s)内にこの関数を追加します。
public Fragment getActiveFragment(ViewPager container, int position) {
String name = makeFragmentName(container.getId(), position);
return fm.findFragmentByTag(name);
}
private static String makeFragmentName(int viewId, int index) {
return "Android:switcher:" + viewId + ":" + index;
}
それは最も優雅な解決策ではありませんが、機能する限り
[〜#〜] caution [〜#〜]
これはViewPager内部のプライベートメソッドであり、いつでも、またはOSバージョンごとに変更できます。
長い間過去ですが、私はまだ私のプログラムを提供したいのですが、フラグメントの目に見える状態を取得することは確かに簡単ではないことがわかったからです。リクエストに基づいて、次の3つの質問を解決する必要があります。
最初に、ViewPagerでフラグメントを取得するのに役立つアダプタを定義しました。ViewPagerでフラグメントの位置とタイプを変更できる場合は、public int getItemPosition(Object object) method
およびpublic Fragment getItem(int position)
メソッドを使用して、正しい位置。
/**
* Created by Kilnn on 2017/7/12.
* A abstract FragmentStatePagerAdapter which hold the fragment reference.
*/
public abstract class SmartFragmentStatePagerAdapter extends FragmentStatePagerAdapter {
private SparseArray<WeakReference<Fragment>> registeredFragments = new SparseArray<>();
public SmartFragmentStatePagerAdapter(FragmentManager fm) {
super(fm);
}
@Override
public Object instantiateItem(ViewGroup container, int position) {
Fragment fragment = (Fragment) super.instantiateItem(container, position);
registeredFragments.put(position, new WeakReference<>(fragment));
return fragment;
}
@Override
public void destroyItem(ViewGroup container, int position, Object object) {
registeredFragments.remove(position);
super.destroyItem(container, position, object);
}
public Fragment getRegisteredFragment(int position) {
WeakReference<Fragment> reference = registeredFragments.get(position);
return reference != null ? reference.get() : null;
}
}
そのとき、私は目に見える状態を処理するためにベースフラグメントを定義します。しかし、小さな欠陥があるのは、フラグメントの1つのセットに対して同じスイッチタイプを指定する必要があるということです。
import Android.support.annotation.IntDef;
import Android.support.v4.app.Fragment;
import Java.lang.annotation.Retention;
import Java.lang.annotation.RetentionPolicy;
/**
* Created by Kilnn on 2017/7/12.
* A smart fragment know itself's visible state.
*/
public abstract class SmartFragment extends Fragment {
private boolean isFragmentVisible;
@Override
public void onResume() {
super.onResume();
int switchType = getSwitchType();
if (switchType == ATTACH_DETACH) {
notifyOnFragmentVisible();
} else if (switchType == SHOW_HIDE) {
if (!isHidden()) {
notifyOnFragmentVisible();
}
} else if (switchType == VIEW_PAGER) {
//If the parent fragment exist and hidden when activity destroy,
//when the activity restore, The parent Fragment will be restore to hidden state.
//And the sub Fragment which in ViewPager is also be restored, and the onResumed() method will callback.
//And The sub Fragment's getUserVisibleHint() method will return true if it is in active position.
//So we need to judge the parent Fragment visible state.
if (getUserVisibleHint() && isParentFragmentVisible()) {
notifyOnFragmentVisible();
}
}
}
@Override
public void setUserVisibleHint(boolean isVisibleToUser) {
super.setUserVisibleHint(isVisibleToUser);
int switchType = getSwitchType();
if (switchType == VIEW_PAGER) {
if (isVisibleToUser) {
//If ViewPager in ViewPager , the sub ViewPager will call setUserVisibleHint before onResume
if (isResumed()) {
notifyOnFragmentVisible();
}
} else {
notifyOnFragmentInvisible();
}
}
}
@Override
public void onHiddenChanged(boolean hidden) {
super.onHiddenChanged(hidden);
int switchType = getSwitchType();
if (switchType == SHOW_HIDE) {
if (hidden) {
notifyOnFragmentInvisible();
} else {
//Just judge for safe
if (isResumed()) {
notifyOnFragmentVisible();
}
}
}
}
@Override
public void onPause() {
super.onPause();
notifyOnFragmentInvisible();
}
private boolean isParentFragmentVisible() {
Fragment parent = getParentFragment();
if (parent == null) return true;
if (parent instanceof SmartFragment) {
return ((SmartFragment) parent).isFragmentVisible();
} else {
//TODO May be can't get the correct visible state if parent Fragment is not SmartFragment
return parent.isVisible();
}
}
public boolean isFragmentVisible() {
// Don't judge the state of the parent fragment,
// because if the parent fragment visible state changes,
// you must take the initiative to change the state of the sub fragment
// return isFragmentVisible && isParentFragmentVisible();
return isFragmentVisible;
}
public void notifyOnFragmentVisible() {
if (!isFragmentVisible) {
onFragmentVisible();
isFragmentVisible = true;
}
}
public void notifyOnFragmentInvisible() {
if (isFragmentVisible) {
onFragmentInvisible();
isFragmentVisible = false;
}
}
/**
* If this method callback, the Fragment must be resumed.
*/
public void onFragmentVisible() {
}
/**
* If this method callback, the Fragment maybe is resumed or in onPause().
*/
public void onFragmentInvisible() {
}
/**
* Fragments switch with attach/detach(replace)
*/
public static final int ATTACH_DETACH = 0;
/**
* Fragments switch with show/hide
*/
public static final int SHOW_HIDE = 1;
/**
* Fragments manage by view pager
*/
public static final int VIEW_PAGER = 2;
@Retention(RetentionPolicy.SOURCE)
@IntDef({ATTACH_DETACH, SHOW_HIDE, VIEW_PAGER})
public @interface SwitchType {
}
@SwitchType
public abstract int getSwitchType();
}
最後に、ネストされた使用の場合、親FragmentでサブFragment表示状態を渡します。
public class ParentFragment extends SmartFragment{
....
@Override
public void onFragmentVisible() {
super.onFragmentVisible();
SmartFragment fragment = (SmartFragment) mAdapter.getRegisteredFragment(mViewPager.getCurrentItem());
if (fragment != null) {
fragment.notifyOnFragmentVisible();
}
}
@Override
public void onFragmentInvisible() {
super.onFragmentInvisible();
SmartFragment fragment = (SmartFragment) mAdapter.getRegisteredFragment(mViewPager.getCurrentItem());
if (fragment != null) {
fragment.notifyOnFragmentInvisible();
}
}
...
}
アタッチ/デタッチ、表示/非表示、およびViewPagerの3つのレイヤーをネストしたテストがあります。それはうまく動作します。おそらくもっとテストを行うべきだったかもしれませんが、私にとってはそれで十分でした。