由于android自带的tablayout不能实现某些效果,所以我们简单自定义一个,能够满足大部分的使用场景

要点1:继承HorizontalScrollView,linearlayout中addview每个tab
要点2:viewpager的addOnPageChangeListener监听滚动设置指示器的位置,然后在ondraw方法中使用GradientDrawable设置bounds,draw(canvas)画指示器
要点3:滚动时自动使tab滚到中间,即x=tab.left-屏幕宽度/2+tab.width/2
要点4:为了保证指示器的y坐标一致,需要事先设置相对标题为选中加粗后的标题,使用Paint.getTextBounds()测量标题的宽高,计算指示器的marginTop

代码:
style文件:

<?xml version="1.0" encoding="utf-8"?>
<resources><declare-styleable name="CustomTabLayout"><attr name="tab_width" format="dimension"/><attr name="tab_height" format="dimension"/><attr name="scrollable" format="boolean"/><attr name="even_screen" format="boolean"/><attr name="title_color" format="color"/><attr name="title_select_color" format="color"/><attr name="title_size" format="dimension"/><attr name="title_select_size" format="dimension"/><attr name="show_indicator" format="boolean"/><attr name="indicator_height" format="dimension"/><attr name="indicator_color" format="color"/><attr name="indicator_radius" format="dimension"/><attr name="indicator_marginTop" format="dimension"/><attr name="indicator_marginBottom" format="dimension"/><attr name="indicator_width_equal_title" format="boolean"/><attr name="tab_background_color" format="color"/><attr name="tab_select_background_color" format="color"/><attr name="indicator_width" format="dimension"/></declare-styleable>
</resources>

customtablayout.java

import android.content.Context
import android.graphics.*
import android.graphics.drawable.GradientDrawable
import android.util.AttributeSet
import android.view.Gravity
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.HorizontalScrollView
import android.widget.LinearLayout
import android.widget.RelativeLayout
import android.widget.TextView
import androidx.core.view.get
import androidx.core.view.size
import androidx.viewpager.widget.ViewPager
import com.JTJK.jpl.R
import com.JTJK.jpl.util.*class CustomTabLayout : HorizontalScrollView {private lateinit var mTabsContainer:LinearLayoutprivate var mViewPager:ViewPager?=nullprivate var mTitles:MutableList<String> = mutableListOf()private var mEventScreen=trueprivate var mTabWidth=0fprivate var mTabHeight=0fprivate var mCurrentPosition=0private var mPositionOffset=0fprivate var mTitleColor=Color.BLACKprivate var mTitleSize=0fprivate var mTitleSelectColor=Color.BLACKprivate var mTitleSelectSize=0fprivate var mShowIndicator=trueprivate var mIndicatorHeight=0fprivate var mIndicatorRadius=0fprivate var mIndicatorMarginTop=0fprivate var mIndicatorMarginBottom=0fprivate var mTabBackGroundColor=Color.WHITEprivate var mTabSelectBackGroundColor=Color.WHITEprivate var mTabsContainerHeight=0private var mIndicatorDrawable=GradientDrawable()private var mTabCount=0private var mIndicatorWidthEqualTitle=trueprivate var mIndicatorBounds=Rect()private var mIndicatorWidth=0fprivate var mDefaultTextSize=0fprivate var mDefaultIndicatorHeight=6fprivate var mIndicatorColor=Color.BLUEconstructor(context: Context) : super(context) {}constructor(context: Context, attrs: AttributeSet):this(context,attrs,0){}constructor(context: Context, attrs: AttributeSet, defStyle:Int):this(context,attrs,defStyle,0){}constructor(context: Context, attrs: AttributeSet, defStyle:Int, defRes:Int):super(context,attrs,defStyle,defRes){initView(context,attrs)}private fun initView(context:Context,attrs: AttributeSet){setWillNotDraw(false)val ta=context.obtainStyledAttributes(attrs, R.styleable.CustomTabLayout)mEventScreen=ta.getBoolean(R.styleable.CustomTabLayout_even_screen,true)mTabWidth=ta.getDimension(R.styleable.CustomTabLayout_tab_width,0f)mTabHeight=ta.getDimension(R.styleable.CustomTabLayout_tab_height,0f)mTitleColor=ta.getColor(R.styleable.CustomTabLayout_title_color,Color.BLACK)mTitleSelectColor=ta.getColor(R.styleable.CustomTabLayout_title_select_color,Color.BLACK)mTitleSize=ta.getDimension(R.styleable.CustomTabLayout_title_size,0f)mTitleSelectSize=ta.getDimension(R.styleable.CustomTabLayout_title_select_size,0f)mTabBackGroundColor=ta.getColor(R.styleable.CustomTabLayout_tab_background_color,Color.WHITE)mTabSelectBackGroundColor=ta.getColor(R.styleable.CustomTabLayout_tab_select_background_color,Color.WHITE)mShowIndicator=ta.getBoolean(R.styleable.CustomTabLayout_show_indicator,true)mIndicatorHeight=ta.getDimension(R.styleable.CustomTabLayout_indicator_height,0f)if(mShowIndicator && mIndicatorHeight==0f){mIndicatorHeight=mDefaultIndicatorHeight.dp2px()}mIndicatorRadius=ta.getDimension(R.styleable.CustomTabLayout_indicator_radius,0f)mIndicatorMarginTop=ta.getDimension(R.styleable.CustomTabLayout_indicator_marginTop,0f)mIndicatorMarginBottom=ta.getDimension(R.styleable.CustomTabLayout_indicator_marginBottom,0f)mIndicatorWidth=ta.getDimension(R.styleable.CustomTabLayout_indicator_width,0f)mIndicatorWidthEqualTitle=ta.getBoolean(R.styleable.CustomTabLayout_indicator_width_equal_title,true)if(mIndicatorWidth>0f){mIndicatorWidthEqualTitle=false}mIndicatorColor=ta.getColor(R.styleable.CustomTabLayout_indicator_color,ColorUtil.getResuorceColor(context,R.color.indicator))ta.recycle()mTabsContainer=LinearLayout(context)mTabsContainer.gravity=Gravity.CENTERaddView(mTabsContainer)}fun setViewPager(vp:ViewPager){if(vp.adapter==null){throw Throwable("viewpager adapter can not be null")}mTabCount = vp.adapter!!.countif(mTabCount==0){throw Throwable("viewpager adapter count can not be zero")}mViewPager=vpmTitles.clear()for(i in 0 until mTabCount){addTab(i)}vp.addOnPageChangeListener(object : ViewPager.OnPageChangeListener{override fun onPageScrollStateChanged(state: Int) {}override fun onPageScrolled(position: Int,positionOffset: Float,positionOffsetPixels: Int) {mPositionOffset=positionOffsetif(positionOffset==0f){mCurrentPosition=positionselectTab(mCurrentPosition)scrollToMiddle()}calculateIndicatorBounds(position)invalidate()}override fun onPageSelected(position: Int) {//onPageSelected会在onPageScrolled的中途调用if(mCurrentPosition!=position){mCurrentPosition=position}}})//初始选中第一项/*if(mShowIndicator){Handler(Looper.myLooper()!!).post {//selectTab(0)//calculateIndicatorBounds()}}
*/}//滚动到中间private fun scrollToMiddle(){if(mTabsContainer.size-1<mCurrentPosition) returnmViewPager?.let {val tabView= mTabsContainer[mCurrentPosition]var x=tabView.left-ScreenUtil.getScreenWidth()/2+tabView.width/2scrollTo(x,0)}}private fun getTab(i:Int):View?{if(mTabsContainer.childCount-1>=i){return mTabsContainer[i]}return null}private fun addTab(i:Int){val tabView=LayoutInflater.from(context).inflate(R.layout.item_custom_tab,mTabsContainer,false)if(mTabWidth>0f){tabView.layoutParams.width=mTabWidth.toInt()mEventScreen=false}else{if(mEventScreen){tabView.layoutParams.width= ScreenUtil.getScreenWidth()/mViewPager!!.adapter!!.count}}if(mTabHeight>0){tabView.layoutParams.height=mTabHeight.toInt()}mTabsContainerHeight=tabView.layoutParams.height//tabView.setBackgroundColor(mTabBackGroundColor)val tabTitle=tabView.findViewById<TextView>(R.id.tv_tab_title)val title=mViewPager!!.adapter!!.getPageTitle(i).toString()mTitles.add(title)tabTitle.text=(title)tabTitle.setTextColor(mTitleColor)mDefaultTextSize=tabTitle.textSizeif(mTitleSize>0f)tabTitle.textSize=mTitleSize.px2sp()//setTitleMarginTop(tabTitle)tabView.setOnClickListener{if(mViewPager?.currentItem==i){return@setOnClickListener}selectTab(i)}if(mTitleSize>0f){tabTitle.textSize=mTitleSize.px2sp()}mTabsContainer.addView(tabView)}/* private fun setTitleMarginTop(tabTitle:TextView){val bounds = getTextBounds(tabTitle) ?: return//居中val titleTop=(mTabsContainerHeight-(bounds.bottom-bounds.top+mIndicatorHeight)+mIndicatorMarginTop+mIndicatorMarginBottom)/2(tabTitle.layoutParams as RelativeLayout.LayoutParams).topMargin=titleTop.toInt()}*/private fun getTextBounds(title:String,textSize:Float,isBold:Boolean):Rect?{if(title.isNullOrEmpty()) return nullval titlePaint=Paint(Paint.ANTI_ALIAS_FLAG)titlePaint.textSize=textSizetitlePaint.isFakeBoldText=isBoldval bounds=Rect()titlePaint.getTextBounds(title,0,title.length,bounds)return bounds}private fun selectTab(i:Int){if(mTabsContainer.childCount-1<i) returnval tabView=mTabsContainer[i]val tabTitle=tabView.findViewById<TextView>(R.id.tv_tab_title)//val defaultTextSize=tabTitle.textSizemViewPager?.currentItem=i//选中样式tabTitle.typeface=Typeface.defaultFromStyle(Typeface.BOLD)tabTitle.setTextColor(mTitleSelectColor)if(mTitleSelectSize>0f)tabTitle.textSize=mTitleSelectSize.px2sp()//未选中样式for(j in 0 until mViewPager!!.adapter!!.count){if(j!=i && mTabsContainer.childCount-1>=j){val tv=mTabsContainer[j]val tt=tv.findViewById<TextView>(R.id.tv_tab_title)tt.typeface=Typeface.defaultFromStyle(Typeface.NORMAL)tt.setTextColor(mTitleColor)if(mTitleSize>0f){tt.textSize=mTitleSize.px2sp()}else{tt.textSize=mDefaultTextSize.px2sp()}}}// calculateIndicatorBounds()}private fun calculateIndicatorBounds(position:Int){if(!mShowIndicator || mTabCount==0) returnval tabView=mTabsContainer[position]val tabTitle=tabView.findViewById<TextView>(R.id.tv_tab_title)val bounds = getTextBounds(tabTitle.text.toString(),if(mTitleSelectSize>0f) mTitleSelectSize else mDefaultTextSize,true) ?: returnvar left=0var top=0var right=0var bottom=0/*top=(tabTitle.bottom+mIndicatorMarginTop).toInt()bottom=(top+mIndicatorHeight-mIndicatorMarginBottom).toInt()if(mIndicatorWidthEqualTitle){left=tabTitle.leftright=tabTitle.right}else{if(mIndicatorWidth>0f){left=((tabView.layoutParams.width-mIndicatorWidth)/2).toInt()right=left+mIndicatorWidth.toInt()}}val offset=(tabView.layoutParams.width*mPositionOffset).toInt()left+=tabView.left+offsetright+=tabView.left+offset*/top=(tabView.height-(tabView.height-(bounds.bottom-bounds.top))/2+mIndicatorMarginTop).toInt()bottom=(top+mIndicatorHeight-mIndicatorMarginBottom).toInt()if(mIndicatorWidthEqualTitle){left=(tabView.width-(bounds.right-bounds.left))/2right=left+(bounds.right-bounds.left)}else{if(mIndicatorWidth>0f){left=((tabView.layoutParams.width-mIndicatorWidth)/2).toInt()right=left+mIndicatorWidth.toInt()}}val offset=(tabView.layoutParams.width*mPositionOffset).toInt()left+=tabView.left+offsetright+=tabView.left+offsetmIndicatorBounds.apply {this.left=leftthis.top=topthis.right=rightthis.bottom=bottom}}override fun onDraw(canvas: Canvas?) {super.onDraw(canvas)if(canvas==null || mTabCount<=0 || !mShowIndicator) returnmIndicatorDrawable.bounds=mIndicatorBoundsmIndicatorDrawable.setColor(mIndicatorColor)if(mIndicatorRadius>0f){mIndicatorDrawable.cornerRadius=mIndicatorRadius}mIndicatorDrawable.draw(canvas)}}

效果:

1665486292888192

android自定义TabLayout相关推荐

  1. Android 自定义TabLayout

    默认的TabLayout是没有花里胡哨的功能的,比如说我们可能希望它有边框,或者有背景颜色,或者外边框.因此就需要更改一些属性,或者在代码中实现.比如下图这种带边框,字体改变. 实现思路: tabla ...

  2. android tablayout 自定义,TabLayout的自定义

    TabLayout的自定义,主要是通过setCustomView方法来添加自定义布局实现. 自定义TabLayout的实现主要包含以下几个步骤 ●创建自定义布局(这里我加了一个动画控件,可以替换成其他 ...

  3. android tablayout 自定义,TabLayout用法详解及自定义样式

    TabLayout的默认样式: app:theme="@style/Widget.Design.TabLayout" 从系统定义的该样式继续深入: fill fixed 264dp ...

  4. Android 自定义viewpage + videoview 实现竖屏视频播放效果

    Android 自定义viewpage + videoview 实现竖屏视频播放效果 效果图 实现步骤 前提概要 自定义 viewpage 自定义 videoview 主逻辑代码 效果图 由于用的是v ...

  5. Android 原生 TabLayout 使用全解析

    前言 为什么会有这篇文章呢,是因为之前关于TabLayout的使用陆陆续续也写了好几篇了,感觉比较分散,且不成体系,写这篇文章的目的就是希望能把各种效果的实现一次性讲齐,所以也有了标题的「看这篇就够了 ...

  6. android 圆角边框边框渐变,Android深度定制化TabLayout:圆角,渐变色,背景边框,圆角渐变下划线,基于Android原生TabLayout...

    Android深度定制化TabLayout:圆角,渐变色,背景边框,圆角渐变下划线,基于Android原生TabLayout 在附录1的基础上丰富自定义的TabLayout,这次增加两个内容: 1,当 ...

  7. Android原生TabLayout使用全解析,看这篇就够了

    前言 为什么会有这篇文章呢,是因为之前关于TabLayout的使用陆陆续续也写了好几篇了,感觉比较分散,且不成体系,写这篇文章的目的就是希望能把各种效果的实现一次性讲齐,所以也有了标题的「看这篇就够了 ...

  8. Android自定义ViewGroup基本步骤

    1.自定义属性,获取自定义属性,可参考 ​ Android自定义View基本步骤 ​ 2.onMeasure() 方法,for循环测量子View,根据子View的宽高来计算自己的宽 高 3.onDra ...

  9. Android自定义View —— TypedArray

    在上一篇中Android 自定义View Canvas -- Bitmap写到了TypedArray 这个属性 下面也简单的说一下TypedArray的使用 TypedArray 的作用: 用于从该结 ...

最新文章

  1. 连接wifi的主机设置静态ip(固定ip)后无法上网的原因
  2. hdu_2227_Find the nondecreasing subsequences_树状数组,离散化
  3. jsp导出数据时离开页面_您应该在要离开的公司开始使用数据
  4. 10年经验+20个数据管理项目,我总结出这4个用数据改变企业的精华
  5. LeetCode 496. 下一个更大元素 I
  6. mysql where非常规用法_MySQL where 条件的这个坑你碰到过没
  7. Spring-web-RequestContextHolder/RequestAttributes/HttpServletRequest
  8. android 广告字幕,Android编程实现类似天气预报图文字幕垂直滚动效果的方法
  9. C语言:fseek与ftell函数
  10. 异贝,通过移动互联网技术,为中小微实体企业联盟、线上链接、线上线下自定义营销方案推送。案例14
  11. Virtual Display Manager(windows虚拟显示器软件)官方中文版V3.3.2.44650 | Win7/win10虚拟显示器下载
  12. 安装JDK11并配置环境变量(附百度网盘下载地址)
  13. 高一计算机教学,高一信息技术教学计划参考
  14. 关于培训机构~程序员培训
  15. wordcloud----canvas 绘制标签云/词云web版wordle(一)
  16. android keep class,Android混淆快速配置之@Keep
  17. 毕业设计-基于机器视觉的车型识别系统
  18. JAVA编写程序实现,由键盘输入两个整数,输出其中较大的数。
  19. Springboot项目引入Bootstrap后,图标不能正常显示,报:Failed to decode downloaded font
  20. 如何用fiddler抓取apple手机上app的请求

热门文章

  1. axios delete传递参数方式
  2. 卷积神经网络降维方法,深度神经网络降维方法
  3. 支付宝:沙箱环境简单使用
  4. html水调歌头实验总结,水调歌头明月几时有反思总结
  5. 智慧园区在建设过程中存在的问题
  6. debounce vs throttle
  7. 存word到oracle,Word、Excel、PDF存入oracle 数据库
  8. 前端页面居中对齐总结
  9. Flutter showDialog/showModalBottomSheet刷新UI
  10. scala语言编写的maven项目打jar包运行