自定义指示器Indicator&Tablayout

前言

太久没发CSDN,最近排名降得厉害,因为平时比较忙,加之内卷严重,着实有心无力,此次抽空发一个项目中用到的控件,懒得去找了自己撸一个,放这里方便大家也方便自己

Tablayout

效果图

在这里插入图片描述
如果效果是你想要的 请接着往下看

XML
  <com.guide.lib_common.components.UnitSelectView
        android:id="@+id/unit"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="@dimen/lib_common_10dp"
        app:backColor="@color/lib_common_222124"
        app:boardColor="@color/lib_common_0A84FF"
        app:itemHeight="@dimen/lib_common_38dp"
        app:itemWidth="@dimen/lib_common_130dp"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/layout_head"
        app:selectColor="@color/lib_common_0A84FF"
        app:selectStr="英制,公制"
        app:selectTextSize="@dimen/lib_common_16sp"
        app:unselectColor="@color/lib_common_c1c1c1" />

 <com.guide.lib_common.components.UnitSelectView
       android:id="@+id/unit_select"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:backColor="@color/lib_common_222124"
        app:boardColor="@color/lib_common_444444"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:selectColor="@color/lib_common_0A84FF"
        app:selectStr="yard,m"
        app:selectTextSize="@dimen/lib_common_16sp"
        app:unselectColor="@color/lib_common_c1c1c1" /> 

PS:selectStr方便调试,实际使用中使用其setUnits,详情见下方源码

Style
<declare-styleable name="UnitSelectView">
	<attr name="unselectColor" format="color|reference" />
	<attr name="selectColor" format="color|reference" />
	<attr name="backColor" format="color|reference" />
	<attr name="boardColor" format="color|reference" />
	<attr name="selectStr" format="string" />
	<attr name="selectTextSize" format="reference" />
	<attr name="itemWidth" format="reference" />
	<attr name="itemHeight" format="reference" />
</declare-styleable>
参数名含义
unselectColor未选中颜色
selectColor选中颜色
backColor背景颜色
boardColor边框颜色
selectStr文本(可扩展)
selectTextSize文本字体大小
itemWidth单块宽度
itemHeight单块高度
源代码
class UnitSelectView : View {{
    val mContext: Context
    var mSelectStr: String
    var mTotalCount: Int
    var mSelectColor: Int
    var mUnselectColor: Int
    var mBoardColor: Int
    var mBackColor: Int
    var mTextsize: Float = 0F
    var mItemWidth: Float = 0F
    var mItemHeight: Float = 0F
    var mPadding: Float = 0F
    var mStrokeWidth: Float = 0F
    var mOffest: Float = 0F
    var mRadiusSize: Float = 0F

    var mFatherHeight: Float = 0F
    var mFatherWidth: Float = 0F

    lateinit var mFatherRectF: RectF
    lateinit var mTextPaint: TextPaint
    lateinit var mBackPaint: Paint
    lateinit var mBoardPaint: Paint

    lateinit var mSonRectFs: ArrayList<RectF>
    lateinit var mSontexts: List<String>
    lateinit var mChooseSontexts: List<String>

    public interface OnClickListener {
        fun onClick(touchIndex: Int)
        fun outOftouch()
    }

    var mOnClickListener: OnClickListener? = null


    constructor(context: Context) : this(context, null)

    constructor(context: Context, attrs: AttributeSet?) : this(context, attrs, 0)

    constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : super(
        context, attrs, defStyleAttr
    ) {
        mContext = context
        val a = context.obtainStyledAttributes(attrs, R.styleable.UnitSelectView)
        mTextsize = a.getDimension(R.styleable.UnitSelectView_selectTextSize, dp2px(14f))
        mItemWidth = a.getDimension(R.styleable.UnitSelectView_itemWidth, dp2px(62f))
        mItemHeight = a.getDimension(R.styleable.UnitSelectView_itemHeight, dp2px(30f))
        mContext.resources.getDimensionPixelSize(com.guide.lib_common.R.dimen.lib_common_60dp)
        mSelectStr = a.getString(R.styleable.UnitSelectView_selectStr) ?: mContext.getString(
            com.guide.lib_common.R.string.test
        )

        //test code 方便预览
        mSontexts = mSelectStr.split(",")
        mChooseSontexts = mSelectStr.split(",")
        Log.d("qqq", "mSelectStr " + mSontexts)
        mTotalCount = mSontexts.size
        //test code 方便预览

        mSelectColor = a.getColor(
            R.styleable.UnitSelectView_selectColor, ContextCompat.getColor(
                mContext, R.color.lib_common_c1c1c1
            )
        )
        mUnselectColor = a.getColor(
            R.styleable.UnitSelectView_unselectColor, ContextCompat.getColor(
                mContext, R.color.lib_common_white
            )
        )
        mBoardColor = a.getColor(
            R.styleable.UnitSelectView_boardColor, ContextCompat.getColor(
                mContext, R.color.lib_common_c1c1c1
            )
        )
        mBackColor = a.getColor(
            R.styleable.UnitSelectView_backColor, ContextCompat.getColor(
                mContext, R.color.lib_common_222124
            )
        )
        a.recycle()
        init()
    }


    private fun init() {
        setWillNotDraw(false)
        mPadding = dp2px(3F)
        mStrokeWidth = dp2px(1F)
        mOffest = dp2px(1F)
        mRadiusSize = dp2px(8F)
        initPaint()
    }

    private fun initPaint() {
        mTextPaint = TextPaint()
        mTextPaint.color = mSelectColor
        mTextPaint.textSize = mTextsize
        mTextPaint.style = Paint.Style.FILL

        mBoardPaint = Paint()
        mBoardPaint.color = mBoardColor
        mBoardPaint.isAntiAlias = true
        mBoardPaint.style = Paint.Style.STROKE
        mBoardPaint.strokeWidth = mStrokeWidth

        mBackPaint = Paint()
        mBackPaint.color = mBackColor
        mBackPaint.isAntiAlias = true
        mBackPaint.style = Paint.Style.FILL

//        fatherWidth = getTextW(textPaint, mSelectStr) + mPadding
//        fatherHeight = getTextH(textPaint, mSelectStr) + mPadding / 2
//        宽度限制死
//        mFatherWidth = mItemWidth * mTotalCount + mPadding * 2
//        mFatherHeight = mItemHeight + mPadding * 2
    }

    override fun onDetachedFromWindow() {
        super.onDetachedFromWindow()
    }

    override fun onDraw(canvas: Canvas) {
        super.onDraw(canvas)
        if (isTouch) {
            mTextPaint.color = mUnselectColor
        } else {
            mTextPaint.color = mSelectColor
        }

        //画背景
        canvas.drawRoundRect(
            mFatherRectF.left + mOffest,
            mFatherRectF.top + mOffest,
            mFatherRectF.right - mOffest,
            mFatherRectF.bottom - mOffest,
            mRadiusSize, mRadiusSize, mBackPaint
        )

        //画外边框
        mBoardPaint.style = Paint.Style.STROKE
        mBoardPaint.color = mBoardColor
        canvas.drawRoundRect(
            mFatherRectF.left + mOffest,
            mFatherRectF.top + mOffest,
            mFatherRectF.right - mOffest,
            mFatherRectF.bottom - mOffest,
            mRadiusSize,
            mRadiusSize,
            mBoardPaint
        )

        //画小框
        mBoardPaint.style = Paint.Style.FILL
        mBoardPaint.color = mSelectColor
        mSonRectFs.forEachIndexed { index, it ->
            Log.d("qqq", "画小框 index" + index)
            Log.d("qqq", "画小框 " + mSonRectFs)
            Log.d("qqq", "画小框 " + mSonRectFs.size)
            Log.d("qqq", "画小框 " + mSontexts.size)
            Log.d("qqq", "画小框 " + mChooseSontexts.size)
            Log.d("qqq", "画小框 " + mSontexts)
            Log.d("qqq", "画小框 it.right  " + it.right)
            if (index == touchIndex) {
                mTextPaint.color = mSelectColor
                canvas.drawRoundRect(it, mRadiusSize, mRadiusSize, mBoardPaint)
                mTextPaint.color = Color.WHITE
                canvas.drawText(
                    mChooseSontexts[index],
                    (it.left + it.right - getTextW(mTextPaint, mChooseSontexts[index])) / 2,
                    (getTextH(mTextPaint, mChooseSontexts[index]) + it.top + it.bottom) / 2,
                    mTextPaint
                )
            } else {
                mTextPaint.color = mUnselectColor
                canvas.drawText(
                    mChooseSontexts[index],
                    (it.left + it.right - getTextW(mTextPaint, mChooseSontexts[index])) / 2,
                    (getTextH(mTextPaint, mChooseSontexts[index]) + it.top + it.bottom) / 2,
                    mTextPaint
                )
            }
        }

    }

    override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec)

        val widthMode = MeasureSpec.getMode(widthMeasureSpec)
        val widthSize = MeasureSpec.getSize(widthMeasureSpec)
        val heightSize = MeasureSpec.getSize(heightMeasureSpec)


        //不能超过父布局大小
        if (mFatherWidth > widthSize) {
            mFatherWidth = widthSize.toFloat()
        }

        recreatRectF()
        Log.d("ssss", "widthMode " + widthMode)
        Log.d("ssss", "widthSize " + widthSize)
        Log.d("ssss", "heightSize " + heightSize)
        Log.d("ssss", "mItemHeight " + mItemHeight)
        Log.d("ssss", "mFatherHeight " + mFatherHeight)
    }

    private fun recreatRectF() {
        mFatherWidth = mItemWidth * mTotalCount + mPadding * 2
        mFatherHeight = mItemHeight + mPadding * 2

        mFatherRectF = RectF(0F, 0F, mFatherWidth, mFatherHeight)

        mSonRectFs = ArrayList()

        //画小框
        for (i in 0 until mTotalCount) {

            mSonRectFs.add(
                RectF(
                    mItemWidth * i + mPadding, mPadding,
                    mItemWidth + mItemWidth * i + mPadding, mItemHeight + mPadding
                )
            )
        }

        setMeasuredDimension(mFatherWidth.toInt(), mFatherHeight.toInt())
    }

    private fun dp2px(dpValue: Float): Float {
        val scale = Resources.getSystem().displayMetrics.density
        return dpValue * scale + 0.5f
    }

    private fun getTextH(pFont: TextPaint, text: String): Int {
        val rect = Rect()
        pFont.getTextBounds(text, 0, text.length, rect)
        return rect.height()
    }

    private fun getTextW(pFont: TextPaint, text: String): Float {
        return pFont.measureText(text)
    }

    var isTouch = false
    var touchIndex = -1
    var lastAction: Int = -1
    var actionDownTime: Long = 0
    var actionUpTime: Long = 0L
    var clickSpaceTime: Long = 250L
    var lastClickTime: Long = 0L

    override fun onTouchEvent(event: MotionEvent): Boolean {
        if (MotionEvent.ACTION_DOWN == event.action) {
            actionDownTime = System.currentTimeMillis()
            lastAction = MotionEvent.ACTION_DOWN
            isTouch = true
            invalidate()
        } else if (MotionEvent.ACTION_MOVE == event.action) {
            lastAction = MotionEvent.ACTION_MOVE
            isTouch = true

            invalidate()
        } else if (MotionEvent.ACTION_UP == event.action) {
            lastAction = MotionEvent.ACTION_UP
            actionUpTime = System.currentTimeMillis()
            val canClickSpace = actionUpTime - lastClickTime > clickSpaceTime
            val isClick = actionUpTime - actionDownTime < clickSpaceTime
            if (canClickSpace && isClick) {
                lastClickTime = actionUpTime
                var ret = -1
                for ((index, it) in mSonRectFs.withIndex()) {
                    if (it.contains(event.x, event.y)) {
                        touchIndex = index
                        ret = index
                        break
                    }
                }
                if (touchIndex == ret) {
                    mOnClickListener?.onClick(touchIndex)
                } else {
                    mOnClickListener?.outOftouch()
                }
            }
            isTouch = false
            invalidate()
        }
        return true
    }

    fun setUnits(
        list: List<Pair<String, String>>,
        bean: Enum<*>,
        mOnClickListener: OnClickListener
    ) {
        this.mOnClickListener = mOnClickListener
        mSontexts = list.map { it.first }
        mChooseSontexts = list.map { it.second }

        mTotalCount = mSontexts.size
        touchIndex = mSontexts.indexOf(bean.name)
        recreatRectF()
        postInvalidate()
    }

}

使用
unitSelect.setUnits(arr, value,
	object : UnitSelectView.OnClickListener {
	    override fun onClick(touchIndex: Int) {                       	   
           Log.d( "qqq","AppUnitList   ========================================="+ touchIndex)
	    }
	
	    override fun outOftouch() {
	
	    }
	})

Indicator

效果图

在这里插入图片描述

源代码
public class Indicator extends View {

    private static final int DEFAULT_TOTAL_INDEX = 5;
    private static final int DEFAULT_CURRENT_INDEX = 0;
    private static final int DEFAULT_CIRCLE_DISTANCE = 40;
    private static final int DEFAULT_CIRCLE_RADIUS = 8;
    private static final int DEFAULT_CIRCLE_SELECTED_RADIUS = 11;

    private int selectedColor;
    private int unselectedColor;
    private int currentIndex;
    private int totalIndex;
    private Paint paint;
    private int startX;
    private int startSelectedY;
    private int startY;
    private int centreX;

    public Indicator(Context context) {
        this(context, null);
    }

    public Indicator(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public Indicator(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        TypedArray typedArray = context.getTheme().obtainStyledAttributes(attrs, R.styleable.Indicator, defStyleAttr, 0);
        selectedColor = typedArray.getColor(R.styleable.Indicator_selectedColor, Color.LTGRAY);
        unselectedColor = typedArray.getColor(R.styleable.Indicator_unselectedColor, Color.WHITE);
        typedArray.recycle();
        totalIndex = DEFAULT_TOTAL_INDEX;
        currentIndex = DEFAULT_CURRENT_INDEX;
        paint = new Paint();
    }
    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        centreX = getWidth() / 2;
        startSelectedY = getHeight() / 2 - DEFAULT_CIRCLE_SELECTED_RADIUS;
        startY = getHeight() / 2 - DEFAULT_CIRCLE_RADIUS;
        if (totalIndex % 2 == 0){
            startX = centreX - (int)(1.0 * (totalIndex - 1)/2 * DEFAULT_CIRCLE_DISTANCE);
        }else{
            startX = centreX - totalIndex / 2 * DEFAULT_CIRCLE_DISTANCE;
        }
        paint.setAntiAlias(true);
        paint.setColor(unselectedColor);
        int tempX = startX;
        for(int i = 0 ; i < totalIndex ; i++ ){
            RectF rectF = new RectF(tempX - DEFAULT_CIRCLE_RADIUS,startY,
                    tempX + DEFAULT_CIRCLE_RADIUS,startY + 2 * DEFAULT_CIRCLE_RADIUS);
            if (i == currentIndex) {
                paint.setColor(selectedColor);
                rectF = new RectF(tempX - DEFAULT_CIRCLE_SELECTED_RADIUS,startSelectedY,
                        tempX + DEFAULT_CIRCLE_SELECTED_RADIUS,startSelectedY + 2 * DEFAULT_CIRCLE_SELECTED_RADIUS);
            }
            canvas.drawOval(rectF,paint);
            if (paint.getColor() == selectedColor)
                paint.setColor(unselectedColor);
            tempX += DEFAULT_CIRCLE_DISTANCE;
        }
    }
    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        setMeasuredDimension(measureWidth(widthMeasureSpec),measureHeight(heightMeasureSpec));
    }

    private int measureHeight(int measureSpec){
        int result;
        int specMode = MeasureSpec.getMode(measureSpec);
        int specSize = MeasureSpec.getSize(measureSpec);
        int desired = DEFAULT_CIRCLE_SELECTED_RADIUS * 2 + getPaddingBottom() + getPaddingTop();
        if(specMode == MeasureSpec.EXACTLY) {
            result = Math.max(desired,specSize);
        }else{
            if(specMode == MeasureSpec.AT_MOST){
                result = Math.min(desired,specSize);
            }
            else result = desired;
        }
        return result;
    }

    private int measureWidth(int measureSpec){
        int result;
        int specMode = MeasureSpec.getMode(measureSpec);
        int specSize = MeasureSpec.getSize(measureSpec);
        int desired = (totalIndex - 1) * DEFAULT_CIRCLE_DISTANCE + DEFAULT_CIRCLE_SELECTED_RADIUS * 2 + getPaddingLeft() + getPaddingRight();
        if(specMode == MeasureSpec.EXACTLY) {
            result = Math.max(desired,specSize);
        }else{
            if(specMode == MeasureSpec.AT_MOST){
                result = Math.min(desired,specSize);
            }else result = desired;
        }
        return result;
    }

    public void setTotalIndex(int totalIndex){
        int oldTotalIndex = this.totalIndex;
        if (totalIndex < 1)
            return;
        if (totalIndex < oldTotalIndex){
            if (currentIndex == totalIndex )
                currentIndex = totalIndex - 1;
        }
        this.totalIndex = totalIndex;
        invalidate();
    }
    public void setCurrentIndex(int currentIndex){
        this.currentIndex = currentIndex;
        invalidate();
    }
}
Style
<resources>
    <attr name="selectedColor" format="color" />
    <attr name="unselectedColor" format="color" />
    <declare-styleable name="Indicator">
        <attr name="selectedColor" />
        <attr name="unselectedColor" />
    </declare-styleable>
</resources>
使用

code

indicator.setCurrentIndex(position)

xml

<com.guide.lib_common.components.Indicator
	android:id="@+id/indicator"
	android:layout_width="wrap_content"
	android:layout_height="@dimen/lib_common_40dp"
	app:layout_constraintBottom_toBottomOf="parent"
	app:layout_constraintLeft_toLeftOf="@+id/viewPager"
	app:layout_constraintRight_toRightOf="@+id/viewPager"
	app:selectedColor="@color/lib_common_959595"
	app:unselectedColor="@color/lib_common_F2F2F7">
</com.guide.lib_common.components.Indicator>

  • 5
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值