【CustomView】Android SlidingTabLayout 按钮之间切换指示器滑动-简单实现

遇到这样的需求:

顶部的按钮之间切换,以实心形状从一个后面滑动到另一个后面。

第一反应是联想到了TabLayout底部的指示器滑动,建议去看看它的源码实现。

这里我就不扯它源码了,习惯直接上...差点忘了,先上实现效果:

sliding-tab-layout

#1  xml布局 >>> view_sliding_tab_layout.xml

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="wrap_content"
    android:layout_height="60dp"
    android:orientation="horizontal"
    android:padding="5dp">

    <View
        android:id="@+id/viewIndicator"
        android:layout_width="100dp"
        android:layout_height="wrap_content"
        android:background="@drawable/bg_button_category_purple"
        android:elevation="5dp"
        android:visibility="visible" />

    <TextView
        android:id="@+id/left_text"
        android:layout_width="100dp"
        android:layout_height="match_parent"
        android:elevation="6dp"
        android:gravity="center"
        android:textColor="@color/colorWhite"
        android:text="CATE A" />

    <TextView
        android:id="@+id/mid_text"
        android:layout_width="70dp"
        android:layout_height="match_parent"
        android:layout_marginStart="5dp"
        android:elevation="6dp"
        android:textColor="@color/colorBlack"
        android:layout_toEndOf="@+id/left_text"
        android:gravity="center"
        android:text="CATE B" />

    <TextView
        android:id="@+id/right_text"
        android:layout_width="100dp"
        android:layout_height="match_parent"
        android:layout_toEndOf="@+id/mid_text"
        android:layout_marginStart="5dp"
        android:textColor="@color/colorBlack"
        android:elevation="6dp"
        android:gravity="center"
        android:text="CATE C" />
</RelativeLayout>

cc: viewIndicator是用来根据按钮切换来调整移动位置显示

#2 CustomView >>> SlidingTabLayout

class SlidingTabLayout(context: Context, attrs: AttributeSet) : LinearLayout(context, attrs) {

    private var subInventoryText: TextView
    private var selectedText: TextView

    private var indicatorLeft = -1
    private var indicatorRight = -1
    private var indicatorAnimator: ValueAnimator? = null
    private var isIndicatorAnimatorEnd: Boolean = true

    companion object {
        private const val DEFAULT_TOGGLE_DURATION: Long = 300
    }

    init {
        LayoutInflater.from(context).inflate(R.layout.view_sliding_tab_layout2, this, true)

        //init text
        selectedText = left_text
        subInventoryText = left_text
        selectedText.setTextColor(resources.getColor(R.color.colorWhite))

        left_text.setOnClickListener {
            if (isIndicatorAnimatorEnd && subInventoryText != left_text) {
                isIndicatorAnimatorEnd = false
                subInventoryText = left_text
                animateIndicatorToPosition()
                toggleTabView()
            }
        }

        mid_text.setOnClickListener {
            if (isIndicatorAnimatorEnd && subInventoryText != mid_text) {
                isIndicatorAnimatorEnd = false
                subInventoryText = mid_text
                animateIndicatorToPosition()
                toggleTabView()
            }
        }

        right_text.setOnClickListener {
            if (isIndicatorAnimatorEnd && subInventoryText != right_text) {
                isIndicatorAnimatorEnd = false
                subInventoryText = right_text
                animateIndicatorToPosition()
                toggleTabView()
            }
        }
    }

    private fun toggleTabView() {
        subInventoryText.setTextColor(resources.getColor(R.color.colorWhite))
        selectedText.setTextColor(resources.getColor(R.color.colorBlack))
    }

    private fun setIndicatorPosition(left: Int, right: Int) {
        if (left != indicatorLeft || right != indicatorRight) {
            // If the indicator's left/right hasBackStack changed, invalidate
            indicatorLeft = left
            indicatorRight = right
            viewIndicator.left = indicatorLeft
            viewIndicator.right = indicatorRight
        }
    }

    private fun animateIndicatorToPosition() {
        if (indicatorAnimator != null && indicatorAnimator!!.isRunning) {
            indicatorAnimator!!.cancel()
        }
        val targetLeft: Int = subInventoryText?.left
        val targetRight: Int = subInventoryText?.right
        val startLeft: Int = selectedText?.left
        val startRight: Int = selectedText?.right

        if (startLeft != targetLeft || startRight != targetRight) {
            indicatorAnimator = ValueAnimator()
            val animator = indicatorAnimator
            animator?.interpolator = FastOutSlowInInterpolator()
            animator?.duration = DEFAULT_TOGGLE_DURATION
            animator?.setFloatValues(0F, 100F)
            animator?.addUpdateListener { animator1 ->
                val fraction = animator1.animatedFraction
                setIndicatorPosition(
                    lerpValue(startLeft, targetLeft, fraction),
                    lerpValue(startRight, targetRight, fraction)
                )
            }
            animator?.addListener(object : AnimatorListenerAdapter() {
                override fun onAnimationEnd(animator: Animator) {
                    selectedText = subInventoryText
                    isIndicatorAnimatorEnd = true
                }
            })
            animator?.start()
        }
    }

    private fun lerpValue(startValue: Int, endValue: Int, fraction: Float): Int {
        return startValue + (fraction * (endValue - startValue)).roundToInt()
    }

}

#3 补充下:扩展实现Int.toDp()

fun Int.toDp(): Float = (this * Resources.getSystem().displayMetrics.density)

 

简单实现效果,到这就结束lo:)
源码附上:下载链接

 

=======================更新 2020-07-13=====================

添加设置默认选中位置/添加手动选中位置

    //以下为添加代码
    private var tabPosition: Int = 0
    override fun onLayout(changed: Boolean, l: Int, t: Int, r: Int, b: Int) {
        super.onLayout(changed, l, t, r, b)
        //更新
        updateSelectedTab()
    }

    //可设置显示默认位置
    fun setSelectedTab(tabPosition: Int = 0) {
        this.tabPosition = tabPosition
    }
    //设置显示位置
    fun updateSelectedTab(position: Int = -1) {
        if (position > -1 && tabPosition != position) {
            this.tabPosition = position
        }
        when (tabPosition) {
            0 -> {
                subInventoryText = left_text
            }
            1 -> {
                subInventoryText = mid_text
            }
            2 -> {
                subInventoryText = right_text
            }
            else -> {
            }
        }
        if (position == -1){
            viewIndicator.left = subInventoryText?.left
            viewIndicator.right = subInventoryText?.right
        }else{
            animateIndicatorToPosition()
        }
        toggleTabView()
    }


同步更新至
Git地址:      SlidingTabLayout

 

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值