可自定义图片指示器并支持自定义Tab宽度的TabLayout
来源:互联网 发布:imap服务器端口 编辑:程序博客网 时间:2024/05/23 01:20
[置顶] 可自定义图片指示器并支持自定义Tab宽度的TabLayout
版权声明:转载请标明:http://blog.csdn.net/qq_27258799
目录(?)[+]
- 自定义图片指示器
- 自定义Tab宽度
- 方案1反射修改Tab宽度
- 方案2异步修改Tab宽度
- 最后一个问题
本篇文章已授权微信公众号 guolin_blog (郭霖)独家发布
Tab栏是APP中最常用的组件,如果我们没有太多个性化需求,直接用TabLayout就可以轻松实现。
但是如果我们的产品大大说不想用一条线作指示器了,想用个图片。或者像我们产品一样,想在主页展示4个半Tab(一共5个)时,原生TabLayout就不能满足了。
而前面我发过一篇,自定义三角下标的tablayout,来实现类似于今日头条tab效果,同时还有三角下标。
那时候我是继承HorizontalScrollView,但是滑动过程中有一些bug,因为要自己处理“什么时候该滑动”和“滑动多少”,后来在项目中逻辑太复杂,就想重新推翻重写一个。
当时也想过用TabLayout,但是TabLayout的滑动指示器是矩形,项目中要使用自定义图片,所以最开始就没继续思考,直接用了HorizontalScrollView。
现在回头想想,我真是傻,我直接继承TabLayout,把图片指示器画上去不就OK了,而且绘制图片指示器的逻辑跟自定义三角下标的tablayout一文里的逻辑一样!文末会贴上工程源码。
先看看这次的整体效果:
这里再简单说一下绘制三角下标的原理。
自定义图片指示器
- 初始化指示器坐标:
private void initTranslationParams(LinearLayout llTab, int screenWidth) { //mSlideIcon 是图片指示器 if (mSlideIcon == null) { return; } tabWidth = (int) (screenWidth / (mTabVisibleCount + mLastTabVisibleRatio)); View firstView = llTab.getChildAt(0); if (firstView != null) { //初始位置:第一个tab正下方 this.mInitTranslationX = (firstView.getLeft() + tabWidth / 2 - this.mSlideIcon.getWidth() / 2); this.mInitTranslationY = (getBottom() - getTop() - this.mSlideIcon.getHeight()); } }
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 监听ViewPager的滑动:
viewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() { @Override public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { //传入当前滑动位置,重绘指示器 topTabLayout.redrawIndicator(position, positionOffset); } @Override public void onPageSelected(int position) { } @Override public void onPageScrollStateChanged(int state) { } });
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 重新计算指示器位置:
public void redrawIndicator(int position, float positionOffset) { mTranslationX = (int) ((position + positionOffset) * tabWidth); invalidate(); }
- 1
- 2
- 3
- 4
- 绘制指示器
@Override protected void dispatchDraw(Canvas canvas) { if (mSlideIcon == null) { return; } canvas.save(); // 平移到正确的位置,修正tabs的平移量 canvas.translate(mInitTranslationX + mTranslationX, this.mInitTranslationY); canvas.drawBitmap(this.mSlideIcon, 0, 0, null); canvas.restore(); super.dispatchDraw(canvas); }
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
自定义Tab宽度
本文主要想记录一下自定义Tab宽度,因为我们项目中,顶部tab栏,有5个Tab,但是最后一个只露出半个(想打死产品有木有)。
我们看下TabLayout的源码,没有任何设置Tab宽度的接口,只能在XML中通过
app:tabMaxWidth="" app:tabMinWidth=""
- 1
- 2
来锁定Tab宽度,然后解析出来:
mRequestedTabMinWidth = a.getDimensionPixelSize(R.styleable.TabLayout_tabMinWidth, INVALID_WIDTH);mRequestedTabMaxWidth = a.getDimensionPixelSize(R.styleable.TabLayout_tabMaxWidth, INVALID_WIDTH);
- 1
- 2
- 3
- 4
但是我们这里需要结合手机屏幕宽度和其他业务因素动态的计算出Tab的宽度,在代码里设置。
怎么办?
我首先想到利用反射直接修改这两个值,而mRequestedTabMinWidth
和mRequestedTabMinWidth
又是final的,但是!
但是请注意,这两个变量都不是在定义时初始化的,所以,如果使用反射是可以在代码里设置这两个值的。这里多提一句:
如果final变量在定义时就已经初始化了,那么反射是无法修改值的
如果final变量在定义时没有初始化,是可以用反射修改值的
所以方案1来了。
方案1:反射修改Tab宽度
final Class<?> clz = TabLayout.class; try { final Field requestedTabMaxWidthField = clz.getDeclaredField("mRequestedTabMaxWidth"); final Field requestedTabMinWidthField = clz.getDeclaredField("mRequestedTabMinWidth"); requestedTabMaxWidthField.setAccessible(true); requestedTabMaxWidthField.set(this, 宽度); requestedTabMinWidthField.setAccessible(true); requestedTabMinWidthField.set(this, 宽度); } catch (final NoSuchFieldException e) { e.printStackTrace(); } catch (final SecurityException e) { e.printStackTrace(); } catch (final IllegalArgumentException e) { e.printStackTrace(); } catch (final IllegalAccessException e) { e.printStackTrace(); }
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
这样就可以自定义Tab的宽度了。
下面是方案2,不使用反射,使用异步方法。
方案2:异步修改Tab宽度
当Tab绘制完成以后,我们重新设置TabView的参数,重绘一次,也可以修改Tab的宽度。
public SlidingTabLayout(Context context, AttributeSet attrs) { super(context, attrs); post(new Runnable() { @Override public void run() { resetTabParams(); } });}private void resetTabParams() { LinearLayout tabStrip = getTabStrip(); if (tabStrip == null) { return; } for (int i = 0; i < tabStrip.getChildCount(); i++) { LinearLayout tabView = (LinearLayout) tabStrip.getChildAt(i); LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(宽度, LinearLayout.LayoutParams .WRAP_CONTENT); tabView.setLayoutParams(params); }}//反射拿到TabLayout里的mTabStrip(mTabStrip是TabView的父容器)@Nullablepublic LinearLayout getTabStrip() { Class<?> tabLayout = TabLayout.class; Field tabStrip = null; try { tabStrip = tabLayout.getDeclaredField("mTabStrip"); } catch (NoSuchFieldException e) { e.printStackTrace(); } tabStrip.setAccessible(true); LinearLayout llTab = null; try { llTab = (LinearLayout) tabStrip.get(this); } catch (IllegalAccessException e) { e.printStackTrace(); } llTab.setClipChildren(false); return llTab;}
- 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
如此我们便可以自定义Tab的宽度啦~
最后一个问题
最后我还遇到一个问题,我们原来的Tab栏上有飘出的气泡,多余部分不能被截断,因此需要设置:
setClipChildren(false);setClipToPadding(false);
- 1
- 2
那在TabLayout中要给谁设置呢?
这里我踩了几个坑,我们先来看下TabLayout的结构:
其中mCustomView是我们自定义的Tab布局,Tab类是对TabView和mCustomView的包装。很显然,我们需要对TabView设置布局参数,所以方案来了:
LinearLayout tabStrip = getTabStrip(); if (tabStrip == null) { return; } for (int i = 0; i < tabStrip.getChildCount(); i++) { LinearLayout tabView = (LinearLayout) tabStrip.getChildAt(i); //tab中的图标可以超出父容器 tabView.setClipChildren(false); tabView.setClipToPadding(false); tabView.setPadding(0, 30, 0, 30); }
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
getTabStrip()
方法上面贴过。
这样,我们就可以做出下面类似的气泡效果:
最后贴上整个项目:TabLayoutDemo,欢迎star~
- 顶
- 2
- 踩
- 0
- 上一篇github使用SSH Clone工程避免多次输入用户密码
- 下一篇史上最简单教程:向第三方jar包注入自己的代码
- • Android仿今日头条和知乎等App顶部滑动导航实现代码分析及源码下载
- • 腾讯云容器服务架构实现介绍--董晓杰
- • 安卓原生时间选择器,DatePicker和TimePicker并用,弹窗dialog
- • 容器技术在58同城的实践--姚远
- • 带有4个tab的ViewPager,且自定义指示器
- • Tensorflow项目实战-文本分类
- • 自定义View实现顶部Tab指示器
- • MySQL深入浅出
- • Android---Tablayout自定义Tab的背景和字体的颜色变化
- • Python可以这样学(第三季:多线程与多进程编程)
- • TabLayout自定义Tab的title
- • 华为工程师,带你实战C++
- • 自定义tab,viewpager实现仿tablayout切换效果
- • 自定义一个好看点的TabLayout.Tab
- • Android---Tablayout自定义Tab的背景和字体的颜色变化
- • Android---Tablayout自定义tab
- 可自定义图片指示器并支持自定义Tab宽度的TabLayout
- 可自定义图片指示器并支持自定义Tab宽度的TabLayout
- 自定义TabLayout的Tab
- SmartTabLayout____可支持自定义的TabLayout
- 自定义Tablayout下线的宽度
- TabLayout自定义Tab的title
- TabLayout的指示器宽度问题
- TabLayout设置指示器的宽度
- 史上最巧妙自定义tablayout指示器
- Android---Tablayout自定义tab
- 自定义一个好看点的TabLayout.Tab
- Android Tablayout 自定义Tab布局的使用
- 设置Tablayout的tab宽度
- 一看就会的自定义指示器适用tab与viewpager
- 自定义View实现顶部Tab指示器
- 自定义view:图片显示指示器
- CSS自定义制表符(TAB)宽度
- android.support.design.widget.TabLayout设置指示器的宽度
- postman下载安装
- QT ComBoBox的简单应用
- 够用一年的Git 技巧
- Leetcode 算法设计 第十三周
- Windows Tensorflow GPU安装
- 可自定义图片指示器并支持自定义Tab宽度的TabLayout
- 弹窗居中遮罩
- 【分享】浅谈并发下的HashMap
- JAVA 定时器时间格式
- CCF 1029 信息加密
- POJ
- 常系数齐次线性递推优化矩阵快速幂
- 工作笔记-数据库自动备份
- spring-security番外篇