Android Design Support Library全解

作者: 登高且赋 | 来源:发表于2017-08-27 16:16 被阅读317次

    Android Design Support Library系列第4弹,TabLayout 实现滑动选项卡

    智能管家Tablayout.gif

    如上图的滑动选项卡的效果在移动端的应用中很是常见,之前要是该效果需要利用动态加载布局技术和控制滑动技术(比如利用HorizontalScrollView),较为繁琐。不过现在就完全不同了,Design Support Library提供了功能强大的TabLayout来帮助我们实现动态滑动选项卡。它可以和ViewPager很好地配合,让我们来看看它们如何工作的吧。

    TabLayout与ViewPager的布局

    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout
        xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        xmlns:tools="http://schemas.android.com/tools"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical"
        tools:context="com.android.srx.github.designsupportlibrarydemo.TabLayoutActivity">
    
        <!--AppBarLayout 顶部栏布局,其本质是垂直方向的LinearLayout-->
        <android.support.design.widget.AppBarLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content">
            <!--ToolBar和TabLayout作为顶部栏的一部分,ToolBar在本章的内容并不是必须的可以忽略-->
            <android.support.v7.widget.Toolbar
                android:id="@+id/toolbar"
                android:layout_width="match_parent"
                android:layout_height="?attr/actionBarSize"
                app:layout_scrollFlags="scroll|enterAlways"
                android:popupTheme="@style/ThemeOverlay.AppCompat.ActionBar"/>
            <!--TabLayout被设置为可滑动的,可以设置为固定的fixed-->
            <android.support.design.widget.TabLayout
                android:id="@+id/tabs"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                app:tabIndicatorColor="@color/colorAccent"
                app:tabMode="scrollable"/>
        </android.support.design.widget.AppBarLayout>
    
        <!--协作布局,本质等同于FrameLayout,用于和SnackBar效果配合-->
        <android.support.design.widget.CoordinatorLayout
            android:layout_width="match_parent"
            android:layout_height="0dp"
            android:layout_weight="1">
    
            <!--用于加载Fragment-->
            <android.support.v4.view.ViewPager
                android:id="@+id/viewpager"
                android:layout_width="match_parent"
                android:layout_height="match_parent"/>
    
            <!--浮动按钮,用来体现ViewPager的滑动效果-->
            <android.support.design.widget.FloatingActionButton
                android:id="@+id/btnFloatingAction"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_gravity="bottom|right"
                android:src="@drawable/ic_plus"
                android:visibility="gone"
                app:fabSize="normal"
                app:borderWidth="2dp"
                app:rippleColor="
    
    
    @color/colorPrimaryDark"
                app:elevation="12dp"
                android:layout_marginBottom="@dimen/activity_vertical_margin"
                android:layout_marginEnd="@dimen/activity_horizontal_margin"/>
    
        </android.support.design.widget.CoordinatorLayout>
    
    </LinearLayout>
    

    以上是页面布局文件,其中的重点看本次的主角TabLayoutViewPager,其他的控件都是陪衬。

    AppbarLayout继承自LinearLayout,上文介绍NavigationView时候已经出场了,它就是一个垂直方向的LinearLayout,并支持滑动手势,它可以让你定制在某个可滑动的View滑动手势发生改变时,内部的子View也做出相应的变化,下一集中它和CoordinatorLayout才是主角,这里让它提前登场混个脸熟(没错,这里是下一篇文章的伏笔)。

    请注意TabLayout中的app:tabMode="scrollable"属性,它设定TabLayout是的标签是可以滑动的:当标签过多而设备屏幕无法将其全部展示时,可以滑动切换Tab。如果被设定app:tabMode="fixed",则代表Tab都是固定的,不能滑动切换。对比效果如下:

    滑动模式 固定模式
    此外还可以通过app:tabIndicatorColor属性设置Tab提示字体的颜色,app:tabIndicatorHeight设置Tab提示器的高度。

    ViewPager在布局文件还不需要更多的设置,我们需要在代码中为ViewPager和TabLayout绑定关系。

    ViewPager与TabLayout绑定

    数据填充

    在实现ViewPager与TabLayout协同工作之前,我们需要做点准备工作,就是为TabLayout和ViewPager中的Fragment生成要显示的数据,这里为了便于研究就是显示一些字符串。

    //标签文字
    private List<String> mTitles;
    //Fragment中显示的提示词
    private List<String> mWords;
    //ViewPager中加载的Fragment
    private List<Fragment> mFragments;
    
    //数据长度
    private final int mSize = 8;
    
    //初始化待显示的数据
    private void initData() {
        //初始化Tab标签
        mTitles = new StringGenerator("title").generateList(mSize);
        //初始化Fragment中显示提示词
        mWords = new StringGenerator("Fragment").generateList(mSize);
    
        //初始化待放入View中的Fragment
        mFragments = new ArrayList<>();
        for (int i = 0; i < mSize; i++) {
            TabFragment fragment = new TabFragment();
            fragment.setWords(mWords.get(i));
            mFragments.add(fragment);
        }
    }
    

    这里调用自定义的生成器StringGenerator来生成数据,填充List类型的mTitles和mWords,这是一种常见的为了测试而生成数据并填充容器的方法,参考了《Thinking in Java》第17章。其代码如下

    public class StringGenerator implements Generator<String> {
    
        private String preString;
        private int index = 0;
    
        public StringGenerator(String preString){
            this.preString = preString;
        }
        @Override
        public String next() {
            return preString+(index++);
        }
    
        public List<String> generateList(int size){
            if(size<1){
                return null;
            }
            List<String> list = new ArrayList<>();
            for (int i=0;i<size;i++){
                list.add(next());
            }
            return list;
        }
    }
    
    public interface Generator<T> {
        T next();
    }
    

    众所周知,ViewPager只是一中要容器,需要在其中记载Fragment才能显示内容。TabFragment是自定义的Fragment,其内容很简单,布局里只有一个TextView来显示字符串。代码如下:

    public class TabFragment extends android.support.v4.app.Fragment {
    
    
        private String words;
        private TextView mTextView;
    
        public TabFragment() {
            // Required empty public constructor
        }
    
    
        @Override
        public View onCreateView(LayoutInflater inflater, ViewGroup container,
                                 Bundle savedInstanceState) {
            View view = inflater.inflate(R.layout.fragment_tab,null);
            findView(view);
            return view;
        }
    
        private void findView(View view) {
            mTextView = (TextView) view.findViewById(R.id.tv_in_tab_fragment);
            mTextView.setText(words);
        }
    
        public String getWords() {
            return words;
        }
    
        public void setWords(String words) {
            this.words = words;
        }
    
    }
    

    逻辑绑定

    现在准备工作已经完成,进入重点内容:设置适配器让TabLayout和ViewPager协同工作。其流程如下:

    1. 设置适配器FragmentPagerAdapter,它的主要任务两个:
      • 根据不同的位置(position)确定ViewPager加载不同Fragment;
      • 根据不同的为位置,让TabLayout切换到不同title;
    2. 为ViewPager设置适配器;
    3. 为TabLayout设置ViewPager,确定绑定关系;

    由此可见其核心就是在于构建适配器FragmentPagerAdapter

    实现代码如下:

        private ViewPager mViewPager;
        private TabLayout mTabLayout;
        private FloatingActionButton mActionButton;//浮动按钮;
    //初始化ViewPager
        private void initViewPager() {
            initData();
    
            //预加载
            mViewPager.setOffscreenPageLimit(mTitles.size()/2);
    
            //ViewPager滑动监听
            mViewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
                @Override
                public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
    
                }
                //只有位置为0的Fragment才会显示浮动按钮;
                @Override
                public void onPageSelected(int position) {
                    if(position==0){
                        mActionButton.setVisibility(View.GONE);
                    } else {
                        mActionButton.setVisibility(View.VISIBLE);
                    }
                }
    
                @Override
                public void onPageScrollStateChanged(int state) {
    
                }
            });
    
            //为ViewPager设置适配器,设定Fragment和title的关系
            //设置不同Fragment对应不同的Title
            mViewPager.setAdapter(new FragmentPagerAdapter(getSupportFragmentManager()) {
                @Override
                public Fragment getItem(int position) {
                    return mFragments.get(position);
                }
    
                @Override
                public int getCount() {
                    return mFragments.size();
                }
    
                @Override
                public CharSequence getPageTitle(int position) {
                    return mTitles.get(position);
                }
            });
    
            //将ViewPage绑定到TabLayout
            mTabLayout.setupWithViewPager(mViewPager);
    
            //去掉ActionBar的渐进阴影
        getSupportActionBar().setElevation(0);
        }
    

    其核心代码就是

    mViewPager.setAdapter(new FragmentPagerAdapter(getSupportFragmentManager()) {
                @Override
                public Fragment getItem(int position) {
                    return mFragments.get(position);
                }
    
                @Override
                public int getCount() {
                    return mFragments.size();
                }
    
                @Override
                public CharSequence getPageTitle(int position) {
                    return mTitles.get(position);
                }
            });
    
            //将ViewPage绑定到TabLayout
            mTabLayout.setupWithViewPager(mViewPager);
    
    1. 首先构建匿名类的FragmentPagerAdapter
    2. 然后调用mViewPager.setAdapter()方法,将上一步的匿名类传入;
    3. 最后mTabLayout.setupWithViewPager(mViewPager);将二者绑定;

    最后补上其余实现代码:

    public class TabLayoutActivity extends AppCompatActivity {
    
        private ViewPager mViewPager;
        private TabLayout mTabLayout;
        private FloatingActionButton mActionButton;
        private final int mSize = 8;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_tab_layout);
            Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
            setSupportActionBar(toolbar);
            initView();
        }
    
        //初始Activity中的各种View
        private void initView() {
            mViewPager = (ViewPager) findViewById(R.id.viewpager);
            mTabLayout = (TabLayout) findViewById(R.id.tabs);
            mActionButton = (FloatingActionButton) findViewById(R.id.btnFloatingAction);
            mActionButton.setOnClickListener(new View.OnClickListener() {
    
                @Override
                public void onClick(View v) {
    
                    Snackbar.make(v, "Hello SnackBar!", Snackbar.LENGTH_SHORT)
                            .setAction("Undo", new View.OnClickListener() {
                                @Override
                                public void onClick(View v) {
                                    // Perform anything for the action selected
                                }
                            })
                            .show();
                }
            });
            initViewPager();//具体代码见前文
        }
    

    效果如下:


    Tablayout.gif

    更多关于Design Support Library中控件的讲解将会在持续更新,欢迎关注。
    最后给出Github源码

    相关文章

      网友评论

        本文标题:Android Design Support Library全解

        本文链接:https://www.haomeiwen.com/subject/mozbdxtx.html