- 目前使用的方式
- 原来使用的方式
- 如何懒加载
- 如何禁止viewpager滑动
- 如何刷新
笔者为何会记录这个实现,盖因对业务的需要,导致笔者在两个不同的项目中采用了完全不一样的实现方式,一个至繁,一个至简。
一、目前的方式
此处先附上两份代码:
Fragment.java
/**
* 各个View
*/
@BindView(R2.id.tab_layout)
TabLayout tabLayout;
@BindView(R2.id.view_pager)
ViewPager viewPager;
/**
* Fragment集合
*/
private List<Fragment> mFragmentList;
/**
* tab项标题
*/
@StringRes
private static final int[] TAB_TITLES = new int[]{
R.string.study,
R.string.gift
};
private Context mContext;
public static NewsFragment newInstance() {
return new NewsFragment();
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
return inflater.inflate(R.layout.fragment, container, false);
}
@Override
public void onAttach(Context context) {
super.onAttach(context);
mContext = context;
}
@Override
public void initVariables() {
mFragmentList = new ArrayList<>();
mFragmentList.add(ItemFragment.newInstance(Constants.STUDY));
mFragmentList.add(ItemFragment.newInstance(Constants.GIFT));
}
@Override
public void doBeforeStart() {
// 绑定tabLayout和viewPager
tabLayout.setupWithViewPager(viewPager);
// 为ViewPager设置Adapter
viewPager.setAdapter(new SectionsViewPagerAdapter(mContext, getChildFragmentManager(),
mFragmentList, TAB_TITLES));
}
从上述代码可以看到,实现代码非常的简单:
- 初始化各子Fragment,添加到List中
- 绑定TabLayout和ViewPager
- 为ViewPager设置Adapter,实现子Fragment、Tab标题与ViewPager的绑定
而Adapter的内容也非常简单,只需继承FragmentPagerAdapter并重写其中的方法即可。
SectionsViewPagerAdapter.java
public class SectionsViewPagerAdapter extends FragmentPagerAdapter {
private final Context mContext;
// tab项对应的fragment
private final List<Fragment> mFragmentList;
// tab项对应的标题
private final int[] mTitles;
public SectionsViewPagerAdapter(Context context, FragmentManager fm, List<Fragment> fragments, int[] titles) {
super(fm);
mContext = context;
mFragmentList = fragments;
mTitles = titles;
}
@Nullable
@Override
public CharSequence getPageTitle(int position) {
return mContext.getResources().getString(mTitles[position]);
}
@Override
public Fragment getItem(int i) {
return mFragmentList.get(i);
}
@Override
public int getCount() {
return mFragmentList.size();
}
}
简单的两份代码,就实现了一个页面切换的效果。
页面刷新
如果我们对Tab的样式没有特殊要求,且没有实现页面下拉刷新,那么我们可以用如下方式实现页面刷新——即判断当前页面是否可见。
@Override
public void setUserVisibleHint(boolean isVisibleToUser) {
// 使用getUserVisibleHint()能获取更准确的状态
if (getUserVisibleHint()) {
if (isReFresh) {
// 刷新页面
} else {
// 将刷新标志设为true
isReFresh = true;
}
}
super.setUserVisibleHint(isVisibleToUser);
}
在这段代码中,为何使用 getUserVisibleHint() 获取当前 Fragment 的显示状态,而不是使用 isVisibleToUser 参数。经过调试发现,isVisibleToUser 的值与 getUserVisibleHint() 获取到的值是相反的,另外, setUserVisibleHint() 是一个set方法,其中的 isVisibleToUser 参数是可以被人为设置的,所以应使用 getUserVisibleHint() 获取最真实的状态。
二、以前的实现方式
private CustomViewPager viewPager;
private TabLayout tabs;
private List<Fragment> fragmentList;
private List<String> titleList;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_devices_inspect);
initView();
initData();
initTab();
initEvent();
initViewPager();
}
/**
* 初始化view
*/
private void initView() {
viewPager = findViewById(R.id.viewpager);
tabs = findViewById(R.id.tabs);
}
/**
* 初始化数据
*/
private void initData() {
fragmentList= new ArrayList<Fragment>();
fragmentList.add(new FirstItemFragment(viewPager, 0, this));
fragmentList.add(new SecondItemFragment(viewPager, 1, this));
fragmentList.add(new ThirdItemFragment(viewPager, 2, this));
titleList= new ArrayList<String>();
titleList.add("第一");
titleList.add("第二");
titleList.add("第三");
}
/**
* 初始化tab
*/
private void initTab() {
int size = titleList.size();
for (int i = 0; i < size; i++) {
tabs.addTab(tabs.newTab().setText(titleList.get(i)));
}
}
/**
* 设置监听
*/
private void initEvent() {
// 点选Tab时切换页面
tabs.addOnTabSelectedListener(new TabLayout.OnTabSelectedListener() {
@Override
public void onTabSelected(TabLayout.Tab tab) {
int currentItem = tab.getPosition();
viewPager.setCurrentItem(currentItem);
}
@Override
public void onTabUnselected(TabLayout.Tab tab) {
}
@Override
public void onTabReselected(TabLayout.Tab tab) {
}
});
// 切换页面时重置页面高度
viewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
@Override
public void onPageScrolled(int i, float v, int i1) {
}
@Override
public void onPageSelected(int i) {
viewPager.resetHeight(i);
}
@Override
public void onPageScrollStateChanged(int i) {
}
});
// 绑定viewPager与TabLayout
viewPager.addOnPageChangeListener(new TabLayout.TabLayoutOnPageChangeListener(tabs));
}
/**
* 初始化ViewPager
*/
private void initViewPager(){
FragmentManager manager = getSupportFragmentManager();
viewPager.setAdapter(new FragmentPagerAdapter(manager, fragmentList, titleList));
viewPager.setOffscreenPageLimit(3);
viewPager.resetHeight(0);
}
而在ItemFragment中,需要在onCreateView()中将Fragment绑定到viewPager中:
现在看看Adapter的内容
public class FragmentPagerAdapter extends FragmentStatePagerAdapter {
private List<Fragment> fragmentList;
private List<String> titleList;
public FragmentPagerAdapter(FragmentManager fm, List<Fragment> fragmentList, List<String> titleList) {
super(fm);
this.fragmentList= fragmentList;
this.titleList= titleList;
}
@Nullable
@Override
public CharSequence getPageTitle(int position) {
return titleList.get(position);
}
@Override
public void setPrimaryItem(@NonNull ViewGroup container, int position, @NonNull Object object) {
this.currentFragment = (Fragment) object;
super.setPrimaryItem(container, position, object);
}
@Override
public Fragment getItem(int i) {
return fragmentList.get(i);
}
@Override
public int getCount() {
return fragmentList.size();
}
}
为什么两种实现方式千差万别呢??
- 没有真正理解TabLayout的使用,除非对Tab有特殊要求,否则不需要自己实现tab的布局和文字内容,FragmentPagerAdapter会自动将标题设置到对应的tab项中,所以不需要initTab()方法。
- 而为什么会造成需要重设ViewPager的高度(且高度设置并不完美),私以为是因为思想和布局的问题。在后一种实现方式中,使用了scrollView包裹viewPager。实际上,ViewPager继承自ViewGroup,是一个容器,我们只需要向其中添加内容即可,所以对于需要上滑的页面,ScrollView控件应在具体页面添加,而不应该使用ScrollView包裹ViewPager。另外,ViewPager的高度应该铺满全屏,不管内容多少,营造出一种全屏的感觉给用户。所以不应该重设ViewPager的高度以满足是否需要滑动的问题,重设的高度等于所有显示内容的总高度,并没有铺满全屏,会导致例如页面加载提示在页面中部等情况。