天天看点

总结三:TabLayout+ViewPager实现页面切换一、目前的方式二、以前的实现方式

  • 目前使用的方式
  • 原来使用的方式
  • 如何懒加载
  • 如何禁止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));
}
           

从上述代码可以看到,实现代码非常的简单:

  1. 初始化各子Fragment,添加到List中
  2. 绑定TabLayout和ViewPager
  3. 为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();
    }
}
           

为什么两种实现方式千差万别呢??

  1. 没有真正理解TabLayout的使用,除非对Tab有特殊要求,否则不需要自己实现tab的布局和文字内容,FragmentPagerAdapter会自动将标题设置到对应的tab项中,所以不需要initTab()方法。
  2. 而为什么会造成需要重设ViewPager的高度(且高度设置并不完美),私以为是因为思想和布局的问题。在后一种实现方式中,使用了scrollView包裹viewPager。实际上,ViewPager继承自ViewGroup,是一个容器,我们只需要向其中添加内容即可,所以对于需要上滑的页面,ScrollView控件应在具体页面添加,而不应该使用ScrollView包裹ViewPager。另外,ViewPager的高度应该铺满全屏,不管内容多少,营造出一种全屏的感觉给用户。所以不应该重设ViewPager的高度以满足是否需要滑动的问题,重设的高度等于所有显示内容的总高度,并没有铺满全屏,会导致例如页面加载提示在页面中部等情况。

继续阅读