本文为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>
复制代码
属性参数详解
参数 | 用处 |
---|---|
textNormalSize | tab未被选中时字体大小 |
textSelectSize | tab被选中时字体大小 |
textNormalColor | tab未被选中时字体颜色 |
textSelectColor | tab被选中时字体颜色 |
underLineHeight | tablayout下面整个一条的线的高度 |
underlineColor | tablayout下面整个一条的线的颜色 |
indicatorHeight | 文字下方指示器的高度 |
indicatorWidth | 文字下方指示器的宽度 |
indicatorSpacing | 文字下方指示器距离上面文字的间距 |
indicatorColor | 文字下方指示器的颜色 |
isTextBold | 文字选中时是否加粗 |
tabPadding | 文字左右间的间隙大小 |
tabSpaceEqual | tab中的文字是否等分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()}
}
复制代码