TabLayout的用法:常规用法 + 自定义用法 + 多项用法 + 联动 CoordinatorLayout

先上最终效果:

效果

主要包括以下几方面的内容:

1)两项Tab的常规切换;

2)自定义两项Tab,仔细看下方的指示器里面的图片是可以动;

3)多项Tab,上方的共10个Tab项,涉及到基类的创建和继承;

4)最上面将Tab项放入CoordinatorLayout中,完成上下拉动时的联动;

一,前期基础知识储备

TabLayout 是 Design Support 库中的一个全新控件。TabLayout 允许开发者将标签指示器(Tab)与其对应的页面(一般为ViewPager)进行关联,达到切换标签指示器的同时,与它关联的页面也会跟着一起滑动的效果。

列出经常设置的属性:

  • app:tabIndicatorColor :指示线的颜色
  • app:tabIndicatorHeight : 指示线的高度,当为0dp时,下划线消失
  • app:tabIndicatorFullWidth="false" 指示线是否铺满宽度,false表示下划线刚好和字体同宽
  • app:tabSelectedTextColor : tab选中时的字体颜色
  • app:tabTextColor="@color/colorPrimary" :未选中字体颜色
  • app:tabBackground="color" : 整个tablayout颜色
  • app:tabMode="scrollable" : 默认是fixed,固定的即不可滑动,铺满屏幕;scrollable:可滚动的
  • app:tabTextAppearance="@style/TabLayoutTextStyle" 

自定义Style,为Tablayout的字体改变大小TextSize。

二,上代码,具体实现

1.Tablayout的常规用法

① 添加依赖;

ext {
    // Sdk and tools
    minSdkVersion = 17
    targetSdkVersion = 29
    compileSdkVersion = 29
    buildToolsVersion = '29.0.0'

    //Version
    versionCode = 1
    versionName = '1.0'    

    // App dependencies
    supportLibraryVersion = '29.0.0'
    butterknifeVersion = '10.1.0'
    utilcodeVersion = '1.25.9'
    gsonVersion = '2.6.2'
    circleimageviewVersion = '2.1.0'
    adapterHelperVersion = '2.9.40'
    zhyadapterVersion = '3.0.3'
    circularprogressbarVersion = '1.2.0'
    magicIndictorVersion = '1.5.0'
}

implementation "com.android.support:appcompat-v7:$supportLibraryVersion"
implementation "com.android.support:design:$supportLibraryVersion"

② 制作Viewpager需要的Fragment;

基类制作,代码如下:

public abstract class BaseFragment extends Fragment {

    @Nullable
    @Override
    public View onCreateView(LayoutInflater inflater, 
         @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        View root = inflater.inflate(getLayoutResId(), null);
        ButterKnife.bind(this, root);
        return root;
    }

    @Override
    public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);
        init();
    }

    protected void init(){}

    protected abstract int getLayoutResId();
}

首页Fragment制作,继承父类,重写抽象方法,代码如下:

public class HomeFragment extends BaseFragment { 

    public static HomeFragment newInstance() {
        return new HomeFragment();
    }

    @Override
    protected int getLayoutResId() {
        return R.layout.fragment_coor_home;
    }

    @Override
    protected void init() {
        loadData();
        initView();
        initMagicIndicator();
    }
}

我的Fragment制作,继承基类,重写抽象方法,代码和上面类似。

③ 为Activity写入布局,放入TabLayout和ViewPager,放入CardView是为了实现上边缘的阴影效果;




    

    

        
    

④ Activity内代码如下:

public class MainActivity extends AppCompatActivity {
    private static final String TAG = "MainActivity";
    //未选中的Tab图片
    private int[] unSelectTabRes = new int[]{R.drawable.ic_launcher, R.drawable.ic_launcher_round};
    //选中的Tab图片
    private int[] selectTabRes = new int[]{R.drawable.ic_launcher_round, R.drawable.ic_launcher};
    //Tab标题
    private String[] title = new String[]{"首页", "我的"};

    @BindView(R.id.tab_layout_view)
    TabLayout mTabLayout;
    @BindView(R.id.viewpager_content_view)
    ViewPager mViewPager;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        ButterKnife.bind(this);
        initView();
        initListener();

        getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, 
                             WindowManager.LayoutParams.FLAG_FULLSCREEN);
        getWindow().setBackgroundDrawable(null);
    }

    private void initView() {
        //使用适配器将ViewPager与Fragment绑定在一起
        mViewPager.setAdapter(new MyFragmentPagerAdapter(getSupportFragmentManager()));
        //将TabLayout与ViewPager绑定
        mTabLayout.setupWithViewPager(mViewPager);
        //获取底部的单个Tab
        for (int i = 0; i < title.length; i++) {
            if (i == 0) {
                mTabLayout.getTabAt(0).setIcon(selectTabRes[0]);
            } else {
                mTabLayout.getTabAt(i).setIcon(unSelectTabRes[i]);
            }
        }
    }

    private void initListener() {
        //TabLayout切换时导航栏图片处理
        mTabLayout.addOnTabSelectedListener(new TabLayout.OnTabSelectedListener() {
            @Override
            public void onTabSelected(TabLayout.Tab tab) {
                //选中图片操作
                for (int i = 0; i < title.length; i++) {
                    if (tab == mTabLayout.getTabAt(i)) {
                        mTabLayout.getTabAt(i).setIcon(selectTabRes[i]);
                        mViewPager.setCurrentItem(i);
                    }
                }
            }

            @Override
            public void onTabUnselected(TabLayout.Tab tab) {
                //未选中图片操作
                for (int i = 0; i < title.length; i++) {
                    if (tab == mTabLayout.getTabAt(i)) {
                        mTabLayout.getTabAt(i).setIcon(unSelectTabRes[i]);
                    }
                }
            }

            @Override
            public void onTabReselected(TabLayout.Tab tab) {

            }
        });
    }

    //自定义ViewPager适配器
    public class MyFragmentPagerAdapter extends FragmentPagerAdapter {

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

        @Override
        public Fragment getItem(int position) {
            if (position == 0) {
                return HomeFragment.newInstance();//首页
            } else if (position == 1) {
                return MineFragment.newInstance();//我的
            }
            return HomeFragment.newInstance();
        }

        @Override
        public int getCount() {
            return title.length;
        }

        @Override
        public CharSequence getPageTitle(int position) {
            return title[position];
        }
    }
}

推荐文章《Android开发 Tablayout的学习》

2. TabLayout的自定义用法

以下用以实现Tab指示器不是添加一张简单的图片,而是加入一张可以动的图片,即Gif图。

1) 切换时动画效果实现:借鉴开源项目GifImageView的思路。原项目是加载网络上的Gif图,放入实际项目中,是使用本地图片,使用时,只需要直接调用setBytes()方法,传入的参数是字节。那么我们可以将使用的Gif图放在assets文件夹或者raw文件夹,然后读取到这张图片,然后将其转为字节,传入即可。

2) 将GifImageView放入到Tablayout中,就需要用到自定义TabLayout了。TabLayout的自定义,主要是通过setCustomView方法来添加自定义布局实现。

① 创建自定义布局,用以填充TabLayout,该布局中放入GifImageView;




    

    

② Activity布局不变,依旧放入TabLayout,而后代码中做修改;

    private void initView() {
        //使用适配器将ViewPager与Fragment绑定在一起
        MyFragmentPagerAdapter fragmentPagerAdapter = new MyFragmentPagerAdapter(getSupportFragmentManager());
        mViewPager.setAdapter(fragmentPagerAdapter);
        //将TabLayout与ViewPager绑定
        mTabLayout.setupWithViewPager(mViewPager);

        //设置自定义tab
        for (int i = 0; i < title.length; i++) {
            TabLayout.Tab tab = mTabLayout.getTabAt(i);
            if (tab != null) {
                tab.setCustomView(fragmentPagerAdapter.getTabView(i));
            }
        }

        View view = mTabLayout.getTabAt(0).getCustomView();
        TextView textView = view.findViewById(R.id.tv);
        textView.setTextColor(getResources().getColor(R.color.choosed_text));
        GifImageView gifImageView = view.findViewById(R.id.gif_img);
        gifImageView.setBytes(AssetsUtils.getByte(MainActivity.this, selectTabRes[0]));
        gifImageView.startAnimation();
    }

    private void initListener() {
        //TabLayout切换时导航栏图片处理
        mTabLayout.addOnTabSelectedListener(new TabLayout.OnTabSelectedListener() {
            @Override
            public void onTabSelected(TabLayout.Tab tab) {
                //选中图片操作
                for (int i = 0; i < title.length; i++) {
                    if (tab == mTabLayout.getTabAt(i)) {
                        View view = tab.getCustomView();
                        if (view != null) {
                            TextView textView = view.findViewById(R.id.tv);
                            textView.setTextColor(getResources().getColor(R.color.choosed_text));
                            GifImageView gifImageView = view.findViewById(R.id.gif_img);
                            gifImageView.setBytes(AssetsUtils.getByte(MainActivity.this, selectTabRes[i]));
                            gifImageView.startAnimation();
                            mViewPager.setCurrentItem(i);
                        }
                    }
                }
            }

            @Override
            public void onTabUnselected(TabLayout.Tab tab) {
                //未选中图片操作
                for (int i = 0; i < title.length; i++) {
                    if (tab == mTabLayout.getTabAt(i)) {
                        View view = tab.getCustomView();
                        if(view != null) {
                            TextView textView = view.findViewById(R.id.tv);
                            textView.setTextColor(getResources().getColor(R.color.unchoosed_text));
                            GifImageView gifImageView = view.findViewById(R.id.gif_img);
                            gifImageView.setBytes(AssetsUtils.getByte(MainActivity.this, unSelectTabRes[i]));
                            gifImageView.startAnimation();
                        }
                    }
                }
            }

            @Override
            public void onTabReselected(TabLayout.Tab tab) {

            }
        });
    }

    //自定义ViewPager适配器
    public class MyFragmentPagerAdapter extends FragmentPagerAdapter {

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

        @Override
        public Fragment getItem(int position) {
            if (position == 0) {
                return HomeFragment.newInstance();//首页
            } else if (position == 1) {
                return MineFragment.newInstance();//我的
            }
            return HomeFragment.newInstance();
        }

        @Override
        public int getCount() {
            return title.length;
        }

        @Override
        public CharSequence getPageTitle(int position) {
            return title[position];
        }

        public View getTabView(int position) {
            View view = LayoutInflater.from(MainActivity.this).inflate(R.layout.custom_tablayout, null);
            TextView textView = view.findViewById(R.id.tv);
            GifImageView gifImageView = view.findViewById(R.id.gif_img);
            textView.setText(title[position]);
            if (position == 0) {
                textView.setTextColor(getResources().getColor(R.color.choosed_text));
                gifImageView.setBytes(AssetsUtils.getByte(MainActivity.this, selectTabRes[0]));
            } else {
                textView.setTextColor(getResources().getColor(R.color.unchoosed_text));
                gifImageView.setBytes(AssetsUtils.getByte(MainActivity.this, unSelectTabRes[1]));
                gifImageView.startAnimation();
            }
            return view;
        }
    }
}

1)修改设置自定义tab的方法;TabLayout.Tab tab = mTabLayout.getTabAt(i); 拿到每一个tab,然后调用setCustomView的方法,将每一个Tab都设置刚刚自定义好的布局。其中定义一个getTabView的方法,指定好初始状态时,各个Tab的状态。

2)Tab发生点击时,调用方法,tab == mTabLayout.getTabAt(i)View view = tab.getCustomView(); 拿到每一个tab的view,然后再做各个状态对应的view切换,比如字体颜色的改变,GifImageView图片的改变。

其他代码如同1中的常规用法,最后就可以实现前面图中的,切换Tab,tab指示图片做动态变化。

推荐文章:《TabLayout自定义tab,实现多样导航栏》《TabLayout的自定义》

3. Tablayout的多项用法

所谓多项用法,就是当TabLayout填充多项时,该如何优雅处理 —— Fragment父类定义。

① 定义10个Fragment;

比如首页的所有布局都是类似的,10个Fragment,每个Fragment都是用列表填充,那么这里的实现也可以使用父类来简化实现。为每个列表Fragment创建父类,父类继承自此前BaseFragment,然后使用同一列表。注意这里使用了洋神的万能适配器。

implementation "com.zhy:base-rvadapter:$zhyadapterVersion"

列表Fragment使用的统一布局如下:




    

RecyclerView使用的适配器布局,使用CardView是为了快速实现圆角:




    

新建的列表Fragment基类如下:

public abstract class BaseItemFragment extends BaseFragment {

    @BindView(R.id.rv)
    ChooseCenterRecyclerview recyclerView;
    public ArrayList datas = new ArrayList();
    public CommonAdapter adapter;

    public abstract void getDatas(ArrayList datas);

    @Override
    protected int getLayoutResId() {
        return R.layout.fragment_base_item_tab;
    }

    @Override
    protected void init() {
        recyclerView.setLayoutManager(new GridLayoutManager(getActivity(), 2));
        adapter = new CommonAdapter(getActivity(), R.layout.fragment_recy_item, datas) {
            @Override
            protected void convert(ViewHolder holder, Bean bean, int position) {
                AppCompatImageView imageView = holder.getView(R.id.iv_effectmore_cover);
                imageView.setImageResource(bean.getCover());
            }
        };
        recyclerView.setAdapter(adapter);
        adapter.setOnItemClickListener(new CommonAdapter.OnItemClickListener() {
            @Override
            public void onItemClick(View view, RecyclerView.ViewHolder holder, int position) {
                onRecyItemClick.onClickRecyItem(position);

            }

            @Override
            public boolean onItemLongClick(View view, RecyclerView.ViewHolder holder, int position) {
                return false;
            }
        });
    }

    public interface OnRecyItemClick {
        void onClickRecyItem(int pos);
    }

    public OnRecyItemClick onRecyItemClick;

    public void setOnRecyItemClick(OnRecyItemClick onRecyItemClick) {
        this.onRecyItemClick = onRecyItemClick;
    }
}

写入10个子Fragment继承自刚刚自定义好的基类,这样得到的子类十分清爽,代码简洁。

public class PaintlyFragment1 extends BaseItemFragment {

    public static PaintlyFragment1 newInstance(){
        return new PaintlyFragment1();
    }

    @Override
    public void getDatas(ArrayList list) {
        datas = list;
    }
}

然后正式项目中为了变得更加优雅,调用第三方库MagicIndicator,替换掉tabLayout,当然可以不替换,用法是一致的。

repositories {
    ...
    maven {
        url "https://jitpack.io"
    }
}

implementation "com.github.hackware1993:MagicIndicator:$magicIndictorVersion"

② Activity布局如下:




    

    

    

③ Viewpager的适配器和列表填充数据使用的Bean如下:

public class Bean implements Serializable {

    private String name;
    private int cover;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getCover() {
        return cover;
    }

    public void setCover(int cover) {
        this.cover = cover;
    }
}

public class FmPagerAdapter extends FragmentPagerAdapter {
    private List fragmentList;

    public FmPagerAdapter(List fragmentList, FragmentManager fm) {
        super(fm);
        this.fragmentList = fragmentList;
    }

    @Override
    public int getCount() {
        return fragmentList != null && !fragmentList.isEmpty() ? fragmentList.size() : 0;
    }

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

}

④ 主Fragment代码如下,主要是填充数据的操作:

public class HomeFragment extends BaseFragment {
    private ArrayList datas_item_1 = new ArrayList<>();
    ... ...
    private FmPagerAdapter pagerAdapter;
    private ArrayList fragments = new ArrayList<>();
    private String[] titles_12 = new String[]{"智能", "红润", "日系", "自然", "艺术黑白", 
                 "甜美", "甜美", "甜美", "甜美", "甜美"};
    private String[] tab_titles = new String[]{"智能", "红润", "日系", "自然", "艺术黑白", 
                 "甜美", "甜美", "甜美", "甜美", "甜美"};
    private List mDataList = Arrays.asList(tab_titles);

    @BindView(R.id.home_fragment_viewpager)
    ViewPager mViewPager;
    @BindView(R.id.home_fragment_tab_magic)
    MagicIndicator magicIndicator;

    public static HomeFragment newInstance() {
        return new HomeFragment();
    }

    @Override
    protected int getLayoutResId() {
        return R.layout.fragment_home;
    }

    @Override
    protected void init() {
        loadData();
        initView();
        initMagicIndicator();
    }

    private void initView() {
        PaintlyFragment1 tabFragment1 = PaintlyFragment1.newInstance();
        tabFragment1.getDatas(datas_item_1);
        tabFragment1.setOnRecyItemClick(pos -> {
            Bean bean = datas_item_1.get(pos);
            int res = bean.getCover();
        });
        ... ...

        fragments.add(tabFragment1);
        fragments.add(tabFragment2);
        fragments.add(tabFragment3);
        fragments.add(tabFragment4);
        fragments.add(tabFragment5);
        fragments.add(tabFragment6);
        fragments.add(tabFragment7);
        fragments.add(tabFragment8);
        fragments.add(tabFragment9);
        fragments.add(tabFragment10);

        pagerAdapter = new FmPagerAdapter(fragments, getActivity().getSupportFragmentManager(), mDataList);
        mViewPager.setAdapter(pagerAdapter);

    }

    private void initMagicIndicator() {
        magicIndicator.setBackgroundColor(Color.WHITE);
        CommonNavigator commonNavigator = new CommonNavigator(getActivity());
        commonNavigator.setScrollPivotX(0.35f);
        commonNavigator.setAdapter(new CommonNavigatorAdapter() {
            @Override
            public int getCount() {
                return mDataList == null ? 0 : mDataList.size();
            }

            @Override
            public IPagerTitleView getTitleView(Context context, final int index) {
                SimplePagerTitleView simplePagerTitleView = new SimplePagerTitleView(context);
                simplePagerTitleView.setText(mDataList.get(index));
                simplePagerTitleView.setNormalColor(Color.parseColor("#A0A0A0")); // 不被选中时文字颜色
                simplePagerTitleView.setSelectedColor(Color.parseColor("#ffffff")); // 选中时文字颜色
                simplePagerTitleView.setOnClickListener(v -> mViewPager.setCurrentItem(index));
                return simplePagerTitleView;
            }

            @Override
            public IPagerIndicator getIndicator(Context context) {
                WrapPagerIndicator indicator = new WrapPagerIndicator(context);
                indicator.setFillColor(Color.parseColor("#FE8D9A")); // 椭圆色块背景色
                return indicator;
            }
        });
        magicIndicator.setNavigator(commonNavigator);
        ViewPagerHelper.bind(magicIndicator, mViewPager);
    }

    private void loadData() {
        for (int i = 0; i < titles_12.length; i++) {
            Bean bean_item_1 = new Bean();
            bean_item_1.setCover(pics_item_1[i]);
            bean_item_1.setName(titles_12[i]);
            datas_item_1.add(bean_item_1);
        }
        ... ...

    }

    private static int[] pics_item_1 = new int[]{
            R.drawable.ic_a0110001_change, R.drawable.ic_a0110001_change,
            R.drawable.ic_a0110001_change, R.drawable.ic_a0110001_change,
            R.drawable.ic_a0110001_change, R.drawable.ic_a0110001_change,
            R.drawable.ic_a0110001_change, R.drawable.ic_a0110001_change,
            R.drawable.ic_a0110001_change, R.drawable.ic_a0110001_change,
            R.drawable.ic_a0110001_change, R.drawable.ic_a0110001_change};
    ... ...

}

4. 将Tablayout 放入Coordinatorlayout中实现列表上下拉联动




    

        

            

                
            

        
    

    

        

        
    


列表部分不变,AppCompatImageView图片控件放入CoordinatorLayout,然后列表所在的父容器LinearLayout加入layout_behavior实现联动。最后实现的效果如上面那幅图的上下拉效果。

推荐文章:《Android Material Design:常用控件学习笔记》

 

最后提一下此项目的总架构为:单Activity + 多Fragment。结构如下图:

TabLayout的用法:常规用法 + 自定义用法 + 多项用法 + 联动 CoordinatorLayout_第1张图片

 

你可能感兴趣的:(有趣的自定义view,高级技巧-自定义View)