虽然过去这么久了,但我还是想给我的程序,因为我发现得到Fragment的可见状态确实不容易。根据您的要求,您需要解决以下三个问题:
- 如何使用 FragmentPagerAdapter 或 FragmentStatePagerAdapter
- 如何判断 Fragment 的可见状态
- Fragment嵌套时如何处理
一开始,我定义了一个base Adapter,帮助我在ViewPager中获取Fragment。如果你在ViewPager中Fragment的位置和类型是可变的,你必须使用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;
}
}
那时,我定义了一个基本的 Fragment 来处理可见状态。但是有一个小缺陷是您必须为一组 Fragment 指定相同的开关类型。
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 嵌套。它工作正常。也许我应该做更多的测试,但对我来说已经足够了。