如果是简单的一个ViewPager里面放置若干个Fragment,然后判断当前处于用户可见视野范围内,这个问题容易解决,方案也很多,在此不再具体解释。最麻烦的是ViewPager与Fragment发生多重嵌套,并且嵌套的层级很深时候,判断一个特定的Fragment是否可见变的是否棘手。比如ViewPagerA里面放几个Fragment(Fa,Fb,Fc),而Fa里面有放一个ViewPagerB,而ViewPagerB里面又盛放若干个Fragment(F甲,F乙,F丙),而 F甲 里面又再放一个ViewPagerC,ViewPagerC里面又放几个Fragment(F1,,F2,F3)…… 就像这样嵌套的越来越深时候,判断某一个特定的Fragment是否可见变的是否困难,但是这个需求和技术点又非常需要明确。典型的假如这些Fragment实时性都很强,都需要周期性的从服务器读取数据更新UI,如果让所有Fragment都同时实时更新,开销比较大,节俭的做法是只更新当前用户切换到的、处于用户视野可见范围内的Fragment数据才更新,而那些已经被用户切换走、处于不可见的Fragment暂停更新,但是当用户把那些原先不可见的Fragment切换到可见视野范围内又重新回来后,又有必要重新启动更新了。
我写一个代码例子:
package zhangphil.test;
import android.graphics.Color;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.design.widget.TabLayout;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentPagerAdapter;
import android.support.v4.view.ViewPager;
import android.support.v7.app.AppCompatActivity;
import android.util.TypedValue;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import org.greenrobot.eventbus.EventBus;
import org.greenrobot.eventbus.Subscribe;
import org.greenrobot.eventbus.ThreadMode;
import java.util.ArrayList;
import java.util.concurrent.TimeUnit;
public class ViewPagerActivity extends AppCompatActivity {
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.view_pager);
ArrayList mFragments = new ArrayList<>();
mFragments.add(TestFragmetA.newInstance("A0"));
mFragments.add(TestFragmetA.newInstance("A1"));
mFragments.add(TestFragmetA.newInstance("A2"));
mFragments.add(TestFragmetA.newInstance("A3"));
ViewPager mViewPager = findViewById(R.id.view_pager);
MyPagerAdapter mPagerAdapter = new MyPagerAdapter(getSupportFragmentManager(), mFragments);
mViewPager.setAdapter(mPagerAdapter);
TabLayout mTabLayout = findViewById(R.id.tab_layout);
for (int i = 0; i < mPagerAdapter.getCount(); i++) {
TabLayout.Tab tab = mTabLayout.newTab();
tab.setText("A" + i);
mTabLayout.addTab(tab);
}
mViewPager.addOnPageChangeListener(new TabLayout.TabLayoutOnPageChangeListener(mTabLayout));
mTabLayout.addOnTabSelectedListener(new TabLayout.ViewPagerOnTabSelectedListener(mViewPager) {
@Override
public void onTabSelected(TabLayout.Tab tab) {
mViewPager.setCurrentItem(tab.getPosition());
mFragments.get(tab.getPosition()).setUserVisibleHint(true);
}
@Override
public void onTabUnselected(TabLayout.Tab tab) {
mFragments.get(tab.getPosition()).setUserVisibleHint(false);
}
});
/**
* 全局的周期更新信号发射源,每隔3秒发一个信号,这个信号触发一次更新操作。
*/
new Thread(new Runnable() {
int count = 0;
@Override
public void run() {
while (true) {
String string = count + "";
EventBus.getDefault().post(string);
try {
TimeUnit.SECONDS.sleep(3);
} catch (InterruptedException e) {
e.printStackTrace();
}
count++;
}
}
}).start();
}
public static class MyPagerAdapter extends FragmentPagerAdapter {
private ArrayList fragments;
public MyPagerAdapter(FragmentManager fm, ArrayList fragments) {
super(fm);
this.fragments = fragments;
}
@Override
public Fragment getItem(int position) {
return fragments.get(position);
}
@Override
public int getCount() {
return fragments.size();
}
}
public static class TestFragmetA extends Fragment {
private static String TAG = "A";
public static TestFragmetA newInstance(String tag) {
Bundle args = new Bundle();
args.putString(TAG, tag);
TestFragmetA fragment = new TestFragmetA();
fragment.setArguments(args);
return fragment;
}
@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.view_pager, container, false);
return v;
}
@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
ArrayList mFragments = new ArrayList<>();
mFragments.add(TestFragmetB.newInstance(getArguments().getString(TAG) + "-甲"));
mFragments.add(TestFragmetB.newInstance(getArguments().getString(TAG) + "-乙"));
mFragments.add(TestFragmetB.newInstance(getArguments().getString(TAG) + "-丙"));
mFragments.add(TestFragmetB.newInstance(getArguments().getString(TAG) + "-丁"));
ViewPager mViewPager = view.findViewById(R.id.view_pager);
MyPagerAdapter mPagerAdapter = new MyPagerAdapter(getChildFragmentManager(), mFragments);
mViewPager.setAdapter(mPagerAdapter);
TabLayout mTabLayout = view.findViewById(R.id.tab_layout);
for (int i = 0; i < mPagerAdapter.getCount(); i++) {
TabLayout.Tab tab = mTabLayout.newTab();
tab.setText("B" + i);
mTabLayout.addTab(tab);
}
mViewPager.addOnPageChangeListener(new TabLayout.TabLayoutOnPageChangeListener(mTabLayout));
mTabLayout.addOnTabSelectedListener(new TabLayout.ViewPagerOnTabSelectedListener(mViewPager) {
@Override
public void onTabSelected(TabLayout.Tab tab) {
mViewPager.setCurrentItem(tab.getPosition());
mFragments.get(tab.getPosition()).setUserVisibleHint(true);
}
@Override
public void onTabUnselected(TabLayout.Tab tab) {
mFragments.get(tab.getPosition()).setUserVisibleHint(false);
}
});
}
@Override
public void setUserVisibleHint(boolean isVisibleToUser) {
super.setUserVisibleHint(isVisibleToUser);
//这里的 isVisibleToUser 已经被上一层级切换TabLayout中设置过。
}
}
public static class TestFragmetB extends Fragment {
private static String TAG = "B";
private TextView mRootView;
//根据生命周期控制是否更新。
private boolean CAN_REFRESH = false;
public static TestFragmetB newInstance(String tag) {
Bundle args = new Bundle();
args.putString(TAG, tag);
TestFragmetB fragment = new TestFragmetB();
fragment.setArguments(args);
return fragment;
}
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
EventBus.getDefault().register(this);
}
@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
if (mRootView == null) {
mRootView = new TextView(container.getContext());
mRootView.setGravity(Gravity.LEFT | Gravity.TOP);
mRootView.setText(getArguments().getString(TAG));
mRootView.setTextColor(Color.RED);
mRootView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 15);
}
return mRootView;
}
@Subscribe(threadMode = ThreadMode.MAIN)
public void onMsgEvent(String msg) {
/**
* 刷新判断条件是否成立。
*/
if (getUserVisibleHint() && getParentFragment().getUserVisibleHint() && CAN_REFRESH) {
update(msg);
}
}
/**
* 这里可以进行UI刷新。
*
* @param msg
*/
private void update(String msg) {
if (mRootView != null) {
String s = mRootView.getText() + "";
mRootView.setText(s + "," + msg);
}
}
@Override
public void onResume() {
super.onResume();
CAN_REFRESH = true;
}
@Override
public void onPause() {
super.onPause();
CAN_REFRESH = false;
}
@Override
public void onDestroy() {
super.onDestroy();
CAN_REFRESH = false;
EventBus.getDefault().unregister(this);
}
}
}
布局文件:
在ViewPager与Fragment多重嵌套且层级深时候,我的这个方案采用主动通过setUserVisibleHint设置isVisibleToUser的值,在TabLayout(本例是TabLayout,可以是其他形式触发ViewPager切换)切换时候,TabLayout中调度ViewPager加载哪一个Fragment是明确的,那么就给当前TabLayout选中的那个Fragment(也即是ViewPager切换到的Fragment)的isVisibleToUser设置为true,其他未被选中的Fragment,isVisibleToUser设置为false。因为ViewPager中的某一个Fragment又嵌套了一个ViewPager,所以需要把上一级的TabLayout选中Fragment可见状态传递下去,所以在Fragment中,如果Fragment又包含一重ViewPager,则和上一步一样,把isVisibleToUser的值在本级TabLayout切换中传递下去(通过setUserVisibleHint)。
做了这么多isVisibleToUser的传递,目的最终是打算通过getUserVisibleHint()来判断当前Fragment是否可见。
本例通过EventBus没隔3秒发送一个更新触发信号,在所有Fragment中都可以接收这个更新信号,但是只有处于用户可见的Fragment才可以更新,而判断当前Fragment是否处于可见,就通过getUserVisibleHint()返回的布尔值,注意到本例还有一个getParentFragment().getUserVisibleHint()作为当前Fragment是否处于可见的联合判断条件之一,是因为Fragment嵌套的太深了,当前Fragment的上一层级Fragment也必须在多重嵌套中处于可见时候,才满足最终的可见条件判断。
同时增加了一个CAN_REFRESH布尔值作为更新判断条件之一,CAN_REFRESH目的是为了标记当前Fragment的生命周期情况,假设一种场景,当前Fragment的确处于可见,但是用户按了手机的home键,导致整个App处于不可见,那么getUserVisibleHint()方法捕捉不到这种变化,需要联合生命周期的onResume和onPause共同判断当前Fragment是否处于手机屏幕的可见状态。
代码运行后,每一个Fragment只有出于可见状态时候,接收到EventBus的更新消息并把数据显示到屏幕上,如果处于不可见,不会显示EventBus发射的更新信号。来回切换Fragment和ViewPager时候,只有出于当前可见的Fragment才会更新到最新的EventBus发射的消息编号。其他不可见的Fragment不会更新,但是切换到时候,就会追加更新到最新EventBus更新消息编号。