ViewPager+Fragment+自定义Tab的使用

我们知道,Google在Android Design Supprot Library增加了很多Material Design控件,使我们能够更加轻松地开发出很多美观又灵活的UI。

其中新增的Tabs就是一个很实用的控件,我们可以使用Tabs+ViewPager+Fragment写出很多实用的控件,如图一所示,关于Tabs控件的更多信息,大家可以阅读官方文档:点击这里

但是,有些时候我们的需求可能不是正好会和官方提供的控件相吻合,受限于这些官方Material Design控件都是遵循固定的设计标准,有些时候我们不得不自己重写控件,或者寻求其它实现方式,比如如图二所示的UI:

因为官方的Tabs控件每个Tab是有最小宽度的(看源码好像是56dp),也就是说无论你字体设置多小,每个Tab的最小宽度都不会变,而且图二在Tab的右边还要添加一些单个的控件,这个时候显然我们自己定义Tab布局,然后结合ViewPager和Fragment使用起来更方便,图二中的每个Tab项的宽度我们可以随便调,在右边可以随意添加单独控件

下面讲一下图二的实现过程,其实主要就是在上方加入一个Tab布局,然后通过给ViewPager设置OnPageChangeListener监听器来动态改变Tab下面的Indicator Line的leftMargin来实现滑动效果,全部源码链接在文章末尾,下面列出主要实现代码:

  • 首先是主界面上方的Tab布局
    这个布局中主要就是上方的三个tab和最右边的一个imageView已经tab下方的indicatorLine,布局很简单,源代码就不再列出,感兴趣的可以参考文章最后的源码链接,不再赘述

  • FragmentPagerAdapter

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    public class BeautifyFragmentAdapter extends FragmentPagerAdapter {

    private final List mFragments = new ArrayList<>();

    public BeautifyFragmentAdapter(FragmentManager fm) {
    super(fm);
    }

    public void addFragment(Fragment fragment) {
    mFragments.add(fragment);
    }

    @Override
    public Fragment getItem(int position) {
    return mFragments.get(position);
    }

    @Override
    public int getCount() {
    return mFragments.size();
    }
    }
  • 定义Fragment用来展示数据

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    public class BeautifyMainFragment extends Fragment {

    public static final String PAGES_TYPE = "pages_type";
    public static final int TYPE_WALLPAPER = 0;
    public static final int TYPE_THEME = 1;
    public static final int TYPE_FONT = 2;

    private int mPageType;

    @Override
    public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    mPageType = getArguments().getInt(PAGES_TYPE);
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
    Bundle savedInstanceState) {
    View view = inflater.inflate(R.layout.layout_beautify_fragment_list,
    container, false);
    textView.setText("第" + mPageType + "页");
    return view;
    }
    }
  • 为ViewPager设置Adapter,关键代码如下

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    private void setupViews() {
    mAdapter = new BeautifyFragmentAdapter(getSupportFragmentManager());

    Bundle wallpaperData = new Bundle();
    wallpaperData.putInt(BeautifyMainFragment.PAGES_TYPE,
    BeautifyMainFragment.TYPE_WALLPAPER);
    BeautifyMainFragment wallpaper = new BeautifyMainFragment();
    wallpaper.setArguments(wallpaperData);
    mAdapter.addFragment(wallpaper);

    Bundle themeData = new Bundle();
    themeData.putInt(BeautifyMainFragment.PAGES_TYPE,
    BeautifyMainFragment.TYPE_THEME);
    BeautifyMainFragment theme = new BeautifyMainFragment();
    theme.setArguments(themeData);
    mAdapter.addFragment(theme);

    Bundle fontData = new Bundle();
    fontData.putInt(BeautifyMainFragment.PAGES_TYPE,
    BeautifyMainFragment.TYPE_FONT);
    BeautifyMainFragment font = new BeautifyMainFragment();
    font.setArguments(fontData);
    mAdapter.addFragment(font);

    viewPager.setAdapter(mAdapter);
    viewPager.setCurrentItem(0);
    viewPager.addOnPageChangeListener(onPageChangeListener);
    }

这里只使用了一个Fragment文件,然后通过添加不同的参数来区分属于不同的page,当然也可以使用三个不同的Fragment文件针对不同的Tab,看个人习惯了

  • 重写onPageScrolled方法实现indicatorLine的滑动
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    ViewPager.OnPageChangeListener onPageChangeListener = new ViewPager.OnPageChangeListener() {

    /**
    * position:当前页面,以及你点击滑动的页面
    * offset:当前页面偏移的百分比
    * offsetPixels:当前页面偏移的像素位置
    */
    @Override
    public void onPageScrolled(int position, float offset, int offsetPixels) {
    FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams)
    indicatorLine.getLayoutParams();
    /**
    * 利用currentIndex(当前所在页面)和position以及offset来
    * 设置indicatorLine的左边距,这里有3个页面 滑动场景:
    * 从左到右分别为0,1,2
    * 0->1; 1->2; 2->1; 1->0
    */

    if (currentIndex == 0 &amp;&amp; position == 0) { // 0->1
    lp.leftMargin = (int) (offset * tabWidthPx
    + currentIndex * tabWidthPx);
    } else if (currentIndex == 1 &amp;&amp; position == 0) { // 1->0
    lp.leftMargin = (int) (-(1 - offset) * tabWidthPx
    + currentIndex * tabWidthPx);
    } else if (currentIndex == 1 &amp;&amp; position == 1) { // 1->2
    lp.leftMargin = (int) (offset * tabWidthPx
    + currentIndex * tabWidthPx);
    } else if (currentIndex == 2 &amp;&amp; position == 1) { // 2->1
    lp.leftMargin = (int) (-(1 - offset) * tabWidthPx
    + currentIndex * tabWidthPx);
    }
    indicatorLine.setLayoutParams(lp);
    }

    @Override
    public void onPageSelected(int position) {
    resetTextView();
    switch (position) {
    case 0:
    wallpaperTitle.setTextColor(ContextCompat.getColor(
    BeautifyMainActivity.this, R.color.bm_tab_text_selected));
    break;
    case 1:
    themeTitle.setTextColor(ContextCompat.getColor(
    BeautifyMainActivity.this, R.color.bm_tab_text_selected));
    break;
    case 2:
    fontTitle.setTextColor(ContextCompat.getColor(
    BeautifyMainActivity.this, R.color.bm_tab_text_selected));
    break;
    }
    currentIndex = position;
    }

    /**
    * state:滑动中的状态 有三种状态(0,1,2) 0:什么都没做 1:正在滑动 2:滑动完毕
    */
    @Override
    public void onPageScrollStateChanged(int state) {

    }
    };

    /**
    * 重置颜色
    */
    private void resetTextView() {
    wallpaperTitle.setTextColor(ContextCompat.getColor(
    this, R.color.bm_tab_text));
    themeTitle.setTextColor(ContextCompat.getColor(
    this, R.color.bm_tab_text));
    fontTitle.setTextColor(ContextCompat.getColor(
    this, R.color.bm_tab_text));
    }

Ok,到这里自定义的Tab+ViewPager+Fragment基本就实现了
源码地址:https://github.com/picksomething/ViewPagerWithTab