自定义TabLayout

本文为kotlin仿开眼视频Android客户端的后续补充内容,本篇为大家介绍如何对TabLayout进行定制使用,基于项目需求,本篇主要对部分功能进行了定制,如:指示器距离文字的距离、文字选中加粗、文字选中变大等

本文部分代码参考:FlycoTabLayout

效果图

效果-1

效果-2

参数详解

属性源码

<declare-styleable name="MyTabLayout"><attr name="textNormalSize" format="dimension" /><attr name="textSelectSize" format="dimension" /><attr name="textNormalColor" format="color" /><attr name="textSelectColor" format="color" /><attr name="underLineHeight" format="dimension" /><attr name="underlineColor" format="color" /><attr name="indicatorHeight" format="dimension" /><attr name="indicatorWidth" format="dimension" /><attr name="indicatorSpacing" format="dimension" /><attr name="isTextBold" format="boolean" /><attr name="tabPadding" format="dimension" /><attr name="indicatorColor" format="color" /><attr name="tabSpaceEqual" format="boolean" /></declare-styleable>
复制代码

属性参数详解

参数用处
textNormalSizetab未被选中时字体大小
textSelectSizetab被选中时字体大小
textNormalColortab未被选中时字体颜色
textSelectColortab被选中时字体颜色
underLineHeighttablayout下面整个一条的线的高度
underlineColortablayout下面整个一条的线的颜色
indicatorHeight文字下方指示器的高度
indicatorWidth文字下方指示器的宽度
indicatorSpacing文字下方指示器距离上面文字的间距
indicatorColor文字下方指示器的颜色
isTextBold文字选中时是否加粗
tabPadding文字左右间的间隙大小
tabSpaceEqualtab中的文字是否等分tablayout的宽度,参考效果-2

与viewpager合并使用

var mAdapter = MyPagerAdapter(fragmentManager)
viewpager.adapter = mAdapter
//tab列表
val stringArray = mTitle.toArray(arrayOfNulls<String>(0))
tab_layout.setViewPager(viewpager, stringArray as Array<String>)
viewpager.offscreenPageLimit = 3
viewpager.currentItem = 0
viewpager.addOnPageChangeListener(this)
复制代码

源码

使用参考详见:github.com/momentslz/E…

源码文件下载地址:
github.com/momentslz/E…

或如代码如下:

/*** Created by moment on 2018/2/22.* FlycoTabLayout 仿写,并提取部分功能原地址链接:* https://github.com/H07000223/FlycoTabLayout*/
open class TabLayout(context: Context, attrs: AttributeSet) : HorizontalScrollView(context, attrs), ViewPager.OnPageChangeListener {private var mContext: Context? = nullprivate var textNormalSize: Float = 0.toFloat()private var textSelectSize: Float = 0.toFloat()private var textNormalColor: Int = 0private var textSelectColor: Int = 0private var underlineHeight: Float = 0.toFloat()private var underlineColor: Int = 0private var indicatorHeight: Float = 0.toFloat()private var indicatorWidth: Float = 0.toFloat()private var indicatorSpacing: Float = 0.toFloat()private var isTextBold: Boolean = falseprivate var tabPadding: Float = 0.toFloat()private var indicatorColor: Int = 0private var tabSpaceEqual: Boolean = falseprivate var selectListener: OnTagSelectListener? = nullprivate var mTabsContainer: LinearLayout? = nullprivate var mCurrentTab = 0private var mViewPager: ViewPager? = nullvar tabCount: Int = 0private setprivate var mTitles: ArrayList<String>? = nullprivate val mRectPaint = Paint(Paint.ANTI_ALIAS_FLAG)private val mIndicatorDrawable = GradientDrawable()/*** indicator*/private val mIndicatorMarginLeft = 0fprivate val mIndicatorMarginTop = 0fprivate val mIndicatorMarginRight = 0f/*** 用于绘制显示器*/private val mIndicatorRect = Rect()private var mCurrentPositionOffset: Float = 0.toFloat()/*** 用于实现滚动居中*/private val mTabRect = Rect()private var mLastScrollX: Int = 0init {initResource(context, attrs)}private fun initResource(context: Context, attrs: AttributeSet) {mContext = contextval typedArray = context.obtainStyledAttributes(attrs, R.styleable.MyTabLayout)textNormalSize = typedArray.getDimension(R.styleable.MyTabLayout_textNormalSize, sp2px(context, 14f).toFloat())textSelectSize = typedArray.getDimension(R.styleable.MyTabLayout_textSelectSize, textNormalSize)textNormalColor = typedArray.getColor(R.styleable.MyTabLayout_textNormalColor, Color.GRAY)textSelectColor = typedArray.getColor(R.styleable.MyTabLayout_textSelectColor, Color.BLACK)underlineHeight = typedArray.getDimension(R.styleable.MyTabLayout_underLineHeight, dp2px(context, 0.5f).toFloat())indicatorWidth = typedArray.getDimension(R.styleable.MyTabLayout_indicatorWidth, -1f)indicatorHeight = typedArray.getDimension(R.styleable.MyTabLayout_indicatorHeight, dp2px(context, 3f).toFloat())indicatorSpacing = typedArray.getDimension(R.styleable.MyTabLayout_indicatorSpacing, dp2px(context, 6f).toFloat())isTextBold = typedArray.getBoolean(R.styleable.MyTabLayout_isTextBold, true)underlineColor = typedArray.getColor(R.styleable.MyTabLayout_underlineColor, Color.parseColor("#EEEEEE"))indicatorColor = typedArray.getColor(R.styleable.MyTabLayout_indicatorColor, Color.BLACK)tabSpaceEqual = typedArray.getBoolean(R.styleable.MyTabLayout_tabSpaceEqual, false)tabPadding = typedArray.getDimension(R.styleable.MyTabLayout_tabPadding, (if (tabSpaceEqual) dp2px(context, 5f) else dp2px(context, 20f)).toFloat())typedArray.recycle()mRectPaint.color = underlineColormIndicatorDrawable.setColor(indicatorColor)mTabsContainer = LinearLayout(context)val lp = ViewGroup.LayoutParams(FrameLayout.LayoutParams.MATCH_PARENT, FrameLayout.LayoutParams.MATCH_PARENT)mTabsContainer!!.layoutParams = lpmTabsContainer!!.gravity = Gravity.CENTER_VERTICALisFillViewport = truesetWillNotDraw(false)isVerticalScrollBarEnabled = falseisHorizontalScrollBarEnabled = falseaddView(mTabsContainer)}//setter and getterfun setCurrentTab(currentTab: Int) {this.mCurrentTab = currentTabmViewPager!!.currentItem = currentTab}fun setCurrentTab(currentTab: Int, smoothScroll: Boolean) {this.mCurrentTab = currentTabmViewPager!!.setCurrentItem(currentTab, smoothScroll)}fun addTab(position: Int, title: String?, textView: TextView?) {if (textView != null) {if (title != null) {textView.gravity = Gravity.CENTERtextView.setTextSize(TypedValue.COMPLEX_UNIT_PX, textNormalSize)textView.setTextColor(textNormalColor)textView.text = titletextView.setOnClickListener { v ->val position = mTabsContainer!!.indexOfChild(v)if (position != -1) {if (mViewPager!!.currentItem != position) {mViewPager!!.currentItem = position} else {if (selectListener != null) {selectListener!!.onTagSelected(position, if (mTitles != null) mTitles!![position] else mViewPager!!.adapter.getPageTitle(position).toString())}}}}}}val lp = if (tabSpaceEqual)LinearLayout.LayoutParams(0, ViewGroup.LayoutParams.MATCH_PARENT, 1.0f)elseLinearLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.MATCH_PARENT)lp.gravity = Gravity.CENTERmTabsContainer!!.addView(textView, position, lp)}override fun onDraw(canvas: Canvas) {super.onDraw(canvas)val height = heightval paddingLeft = paddingLeftvar top = 0var bottom = heightif (mTabsContainer != null && mTabsContainer!!.getChildAt(mCurrentTab) != null) {top = mTabsContainer!!.getChildAt(mCurrentTab).topbottom = mTabsContainer!!.getChildAt(mCurrentTab).bottom}val tabHeight = top + bottomcanvas.drawRect(paddingLeft.toFloat(), height - underlineHeight, (mTabsContainer!!.width + paddingLeft).toFloat(), height.toFloat(), mRectPaint)calcIndicatorRect()if (indicatorHeight > 0 && indicatorWidth > 0) {mIndicatorDrawable.setBounds(paddingLeft + mIndicatorMarginLeft.toInt() + mIndicatorRect.left,tabHeight - tabPadding.toInt() * 2 + indicatorSpacing.toInt(),paddingLeft + mIndicatorRect.right - mIndicatorMarginRight.toInt(),tabHeight - tabPadding.toInt() * 2 + indicatorSpacing.toInt() + indicatorHeight.toInt())mIndicatorDrawable.draw(canvas)} else {mIndicatorDrawable.setBounds(paddingLeft + mIndicatorMarginLeft.toInt() + mIndicatorRect.left,height - indicatorHeight.toInt(),paddingLeft + mIndicatorRect.right - mIndicatorMarginRight.toInt(),height)mIndicatorDrawable.draw(canvas)}}private fun calcIndicatorRect() {val currentTabView = mTabsContainer!!.getChildAt(this.mCurrentTab) ?: returnvar left = currentTabView.left.toFloat()var right = currentTabView.right.toFloat()if (this.mCurrentTab < tabCount - 1) {val nextTabView = mTabsContainer!!.getChildAt(this.mCurrentTab + 1)val nextTabLeft = nextTabView.left.toFloat()val nextTabRight = nextTabView.right.toFloat()left += mCurrentPositionOffset * (nextTabLeft - left)right += mCurrentPositionOffset * (nextTabRight - right)}mIndicatorRect.left = left.toInt()mIndicatorRect.right = right.toInt()mTabRect.left = left.toInt()mTabRect.right = right.toInt()if (indicatorWidth >= 0) {//indicatorWidth大于0时,圆角矩形以及三角形var indicatorLeft = currentTabView.left + (currentTabView.width - indicatorWidth) / 2if (this.mCurrentTab < tabCount - 1) {val nextTab = mTabsContainer!!.getChildAt(this.mCurrentTab + 1)indicatorLeft += mCurrentPositionOffset * (currentTabView.width / 2 + nextTab.width / 2)}mIndicatorRect.left = indicatorLeft.toInt()mIndicatorRect.right = (mIndicatorRect.left + indicatorWidth).toInt()}}/*** 关联ViewPager,用于不想在ViewPager适配器中设置titles数据的情况*/fun setViewPager(vp: ViewPager?, titles: Array<String>) {if (vp == null || vp.adapter == null) {throw IllegalStateException("ViewPager or ViewPager adapter can not be NULL !")}if (titles == null || titles.size == 0) {throw IllegalStateException("Titles can not be EMPTY !")}if (titles.size != vp.adapter.count) {throw IllegalStateException("Titles length must be the same as the page count !")}this.mViewPager = vpmTitles = ArrayList()Collections.addAll(mTitles!!, *titles)this.mViewPager!!.removeOnPageChangeListener(this)this.mViewPager!!.addOnPageChangeListener(this)notifyDataSetChanged()}/*** 关联ViewPager,用于连适配器都不想自己实例化的情况*/fun setViewPager(vp: ViewPager?, titles: Array<String>?, fa: FragmentActivity, fragments: ArrayList<Fragment>) {if (vp == null) {throw IllegalStateException("ViewPager can not be NULL !")}if (titles == null || titles.isEmpty()) {throw IllegalStateException("Titles can not be EMPTY !")}this.mViewPager = vpthis.mViewPager!!.adapter = InnerPagerAdapter(fa.supportFragmentManager, fragments, titles)this.mViewPager!!.removeOnPageChangeListener(this)this.mViewPager!!.addOnPageChangeListener(this)notifyDataSetChanged()}/*** 更新数据*/private fun notifyDataSetChanged() {mTabsContainer!!.removeAllViews()this.tabCount = if (mTitles == null) mViewPager!!.adapter.count else mTitles!!.sizevar tabView: TextViewfor (i in 0 until tabCount) {tabView = TextView(mContext)val pageTitle = if (mTitles == null) mViewPager!!.adapter.getPageTitle(i) else mTitles!![i]addTab(i, pageTitle.toString(), tabView)}updateTabStyles()}private fun updateTabStyles() {for (i in 0 until tabCount) {val v = mTabsContainer!!.getChildAt(i)val tv_tab_title = v as TextViewif (tv_tab_title != null) {tv_tab_title.setTextColor(if (i == mCurrentTab) textSelectColor else textNormalColor)tv_tab_title.setTextSize(TypedValue.COMPLEX_UNIT_PX, if (i == mCurrentTab) textSelectSize else textNormalSize)tv_tab_title.setPadding(tabPadding.toInt(), tabPadding.toInt(), tabPadding.toInt(), tabPadding.toInt())if (isTextBold) {tv_tab_title.paint.isFakeBoldText = i == mCurrentTab}}}}override fun onPageScrolled(position: Int, positionOffset: Float, positionOffsetPixels: Int) {this.mCurrentTab = positionthis.mCurrentPositionOffset = positionOffsetscrollToCurrentTab()invalidate()}/*** HorizontalScrollView滚到当前tab,并且居中显示*/private fun scrollToCurrentTab() {if (tabCount <= 0) {return}val offset = (mCurrentPositionOffset * mTabsContainer!!.getChildAt(mCurrentTab).width).toInt()/**当前Tab的left+当前Tab的Width乘以positionOffset */var newScrollX = mTabsContainer!!.getChildAt(mCurrentTab).left + offsetif (mCurrentTab > 0 || offset > 0) {/**HorizontalScrollView移动到当前tab,并居中 */newScrollX -= width / 2 - paddingLeftcalcIndicatorRect()newScrollX += (mTabRect.right - mTabRect.left) / 2}if (newScrollX != mLastScrollX) {mLastScrollX = newScrollX/** scrollTo(int x,int y):x,y代表的不是坐标点,而是偏移量* x:表示离起始位置的x水平方向的偏移量* y:表示离起始位置的y垂直方向的偏移量*/scrollTo(newScrollX, 0)}}override fun onSaveInstanceState(): Parcelable {val bundle = Bundle()bundle.putParcelable("instanceState", super.onSaveInstanceState())bundle.putInt("mCurrentTab", mCurrentTab)return bundle}override fun onRestoreInstanceState(state: Parcelable?) {var state = stateif (state is Bundle) {val bundle = state as Bundle?mCurrentTab = bundle!!.getInt("mCurrentTab")state = bundle.getParcelable("instanceState")if (mCurrentTab != 0 && mTabsContainer!!.childCount > 0) {updateTabSelection(mCurrentTab)scrollToCurrentTab()}}super.onRestoreInstanceState(state)}private fun updateTabSelection(position: Int) {for (i in 0 until tabCount) {val tabView = mTabsContainer!!.getChildAt(i)val isSelect = i == positionval tab_title = tabView as TextViewif (tab_title != null) {tab_title.setTextColor(if (isSelect) textSelectColor else textNormalColor)tab_title.setTextSize(TypedValue.COMPLEX_UNIT_PX, if (isSelect) textSelectSize else textNormalSize)if (isTextBold) {tab_title.paint.isFakeBoldText = isSelect}}}}override fun onPageSelected(position: Int) = updateTabSelection(position)override fun onPageScrollStateChanged(state: Int) = Unitinternal inner class InnerPagerAdapter(fm: FragmentManager, fragments: ArrayList<Fragment>, private val titles: Array<String>) : FragmentPagerAdapter(fm) {private var fragments = ArrayList<Fragment>()init {this.fragments = fragments}override fun getCount(): Int = fragments.sizeoverride fun getPageTitle(position: Int): CharSequence = titles[position]override fun getItem(position: Int): Fragment = fragments[position]override fun destroyItem(container: ViewGroup?, position: Int, `object`: Any) =// 覆写destroyItem并且空实现,这样每个Fragment中的视图就不会被销毁// super.destroyItem(container, position, object);Unitoverride fun getItemPosition(`object`: Any?): Int = PagerAdapter.POSITION_NONE}interface OnTagSelectListener {fun onTagSelected(position: Int, tag: String)}fun addOnTagSelectListener(listener: OnTagSelectListener) {selectListener = listener}private fun dp2px(context: Context, dp: Float): Int {val scale = context.resources.displayMetrics.densityreturn (dp * scale + 0.5f).toInt()}private fun sp2px(context: Context, sp: Float): Int {val scale = context.resources.displayMetrics.scaledDensityreturn (sp * scale + 0.5f).toInt()}
}
复制代码

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/391183.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

ml dl el学习_DeepChem —在生命科学和化学信息学中使用ML和DL的框架

ml dl el学习Application of Machine Learning and Deep Learning for Drug Discovery, Genomics, Microsocopy and Quantum Chemistry can create radical impact and holds the potential to significantly accelerate the process of medical research and vaccine developm…

响应式网站设计_通过这个免费的四小时课程,掌握响应式网站设计

响应式网站设计This video tutorial from Kevin Powell teaches you to build responsive websites from scratch. 凯文鲍威尔(Kevin Powell)的这段视频教程教您从头开始构建响应式网站。 The course starts with explaining the core concepts needed to start thinking resp…

2017-2018-1 20179215《Linux内核原理与分析》第二周作业

20179215《Linux内核原理与分析》第二周作业 这一周主要了解了计算机是如何工作的&#xff0c;包括现在存储程序计算机的工作模型、X86汇编指令包括几种内存地址的寻址方式和push、pop、call、re等几个重要的汇编指令。主要分为两部分进行这周的学习总结。第一部分对学习内容进…

python:单例模式--使用__new__(cls)实现

单例模式&#xff1a;即一个类有且仅有一个实例。 那么通过python怎么实现一个类只能有一个实例呢。 class Earth:"""假如你是神&#xff0c;你可以创造地球"""print 欢迎来到地球# 生成一个地球 a Earth() print id(a)# 再生成一个地球 b Ear…

重学TCP协议(5) 自连接

1.自连接是什么 在发起连接时&#xff0c;TCP/IP的协议栈会先选择source IP和source port&#xff0c;在没有显示调用bind()的情况下&#xff0c;source IP由路由表确定&#xff0c;source port由TCP/IP协议栈从local port range中选取尚未使用的port。 如果destination IP正…

Gradle复制文件/目录方法

2019独角兽企业重金招聘Python工程师标准>>> gradle复制文件/文件夹方法 复制文件 //复制IDE生成的classes.jar文件到build/libs中&#xff0c;并改名为FileUtils.jar. task copyFile(type:Copy) {delete build/libs/FileUtils.jarfrom(build/intermediates/bundles…

用户参与度与活跃度的区别_用户参与度突然下降

用户参与度与活跃度的区别disclaimer: I don’t work for Yammer, this is a public data case study, I’ve written it in a narrative format to make this case study more engaging to read.免责声明&#xff1a;我不为Yammer工作&#xff0c;这是一个公共数据案例研究&am…

python:__new__()与__init__()

参考&#xff1a;https://blog.csdn.net/qq_41020281/article/details/79638370 转载于:https://www.cnblogs.com/gcgc/p/11585599.html

重学TCP协议(6) 四次挥手

1. 四次挥手 客户端进程发出连接释放报文&#xff0c;并且停止发送数据。释放数据报文首部&#xff0c;FIN1&#xff0c;其序列号为sequ&#xff08;等于前面已经传送过来的数据的最后一个字节的序号加1&#xff09;&#xff0c;此时&#xff0c;客户端进入FIN-WAIT-1&#xff…

mysql数据库部分操作指令

用cmd开启服务时拒绝访问. 原因:不是管理员用户&#xff0c;没有权限 将服务中的 MySQL设置为手动启动&#xff0c; 否则 开机自动启动. 启动mysql服务&#xff0c;用管理员权限打开dos界面 windowsX A 打开开始界面 点击管理员开启cmd 启动服务&#xff1a;net start …

推箱子2-向右推!_保持冷静并砍箱子-哔

推箱子2-向右推!Hack The Box (HTB) is an online platform allowing you to test your penetration testing skills. It contains several challenges that are constantly updated. Some of them simulating real world scenarios and some of them leaning more towards a C…

UML建模图实战笔记

一、前言 UML&#xff1a;Unified Modeling Language&#xff08;统一建模语言&#xff09;&#xff0c;使用UML进行建模的作用有哪些&#xff1a; 可以更好的理解问题可以及早的发现错误或者被遗漏的点可以更加方便的进行组员之间的沟通支持面向对象软件开发建模&#xff0c;可…

数据草拟:使您的团队热爱数据的研讨会

Learn the rules to Data Draw Up; a fun way to get your teams invested in data.了解数据收集的规则&#xff1b; 一种让您的团队投入数据的有趣方式。 Let’s keep things short. Metrics are one of the most important things in Product Management. They help us to u…

python:列表推导式

python中有种独特的语法&#xff1a;推导式&#xff0c;可以将代码压缩到1行&#xff0c;但是不使用也不影响。 有三种&#xff1a;列表、字典、集合&#xff08;注意没有元组推导式&#xff09; 列表推导式 # 1、一行代码实现1—100之和(知识点&#xff1a;列表推导式) print(…

WPF中删除打开过的图片

WPF中删除打开过的图片 原文:WPF中删除打开过的图片在WPF中&#xff0c;当我们删除打开过的图片时&#xff0c;往往会遇到"...无法删除&#xff0c;文件正在被另一个进程使用"的异常。即使当前文件是打开后关闭过的也不行。 这个问题的原因很简单&#xff0c;是因为W…

深入理解InnoDB(5)-文件系统

1. 数据库和文件系统的关系 像 InnoDB 、 MyISAM 这样的存储引擎都是把表存储在文件系统上的。当我们想读取数据的时候&#xff0c;这些存储引擎会从文件系统中把数据读出来返回给我们&#xff0c;当我们想写入数据的时候&#xff0c;这些存储引擎会把这些数据又写回文件系统。…

vim捐赠_#PayItBackwards-一位freeCodeCamp毕业生如何向事业捐赠10,000美元

vim捐赠On Monday my phone suddenly started buzzing. Shawn Wang, AKA Swyx, had just tweeted about a donation hed made to freeCodeCamp.org.星期一&#xff0c;我的电话突然开始嗡嗡作响。 Awn Swyx的Shawn Wang刚刚在推特上发布了他对freeCodeCamp.org的捐款。 I glan…

Digital River拉来Netconcepts站台 亚太营销服务升级

它是大洋彼岸的一家网络软件下载、分销商&#xff0c;很多重量级的软件行业领军企业都是其客户&#xff0c;它一直低调摸索亚太营销的路子&#xff0c;在今年九月份&#xff0c;它一改常态&#xff0c;高调宣布入华&#xff0c;三个月后&#xff0c;它带来了最新消息&#xff1…

按下按钮_按下

按下按钮Updated with the latest data: 23/8/2020更新最新数据&#xff1a;23/8/2020 As restrictions are lifted for Laois and Offaly, difficult times are set to continue for the people of Kildare, at least for another couple of weeks, as they continue to fight…

windows中怎么添加定时任务

linux中有crontab定时任务&#xff0c;很方便 其实windows也有类似的 需求&#xff1a;定时执行python脚本 1、Windows键R&#xff0c;调出此窗口&#xff0c;输入compmgmt.msc 2、 转载于:https://www.cnblogs.com/gcgc/p/11594467.html