Kotlin 自定義view之實現(xiàn)標(biāo)尺控件(選擇身高、體重等)

本篇文章講的是Kotlin 自定義view之實現(xiàn)標(biāo)尺控件Ruler,以選擇身高、體重等。開發(fā)中,當(dāng)我們需要獲取用戶的身高和體重等信息時,如果直接讓他們輸入,顯然體驗不夠好。像類似于唯品會、好輕等APP都是使用了類似于刻度尺的控件讓用戶滑動選擇身高體重,覺得很棒。網(wǎng)上已有人使用Java語言實現(xiàn)這樣的功能,但不影響我對其的學(xué)習(xí)。和往常一樣,主要還是想總結(jié)一下自定義view之實現(xiàn)標(biāo)尺控件的開發(fā)過程以及一些需要注意的地方。

按照慣例,我們先來看看效果圖


這里寫圖片描述

一、先總結(jié)下自定義View的步驟:
1、自定義View的屬性
2、在View的構(gòu)造方法中獲得我們自定義的屬性
3、重寫onMesure
4、重寫onDraw
其中onMesure方法不一定要重寫,但大部分情況下還是需要重寫的

二、View 的幾個構(gòu)造函數(shù)
1、constructor(mContext: Context)
—>java代碼直接new一個RulerView實例的時候,會調(diào)用這個只有一個參數(shù)的構(gòu)造函數(shù);
2、constructor(mContext: Context, attrs: AttributeSet)
—>在默認的XML布局文件中創(chuàng)建的時候調(diào)用這個有兩個參數(shù)的構(gòu)造函數(shù)。AttributeSet類型的參數(shù)負責(zé)把XML布局文件中所自定義的屬性通過AttributeSet帶入到View內(nèi);
3、constructor(mContext: Context, attrs: AttributeSet,defStyleAttr:Int)
—>構(gòu)造函數(shù)中第三個參數(shù)是默認的Style,這里的默認的Style是指它在當(dāng)前Application或者Activity所用的Theme中的默認Style,且只有在明確調(diào)用的時候才會調(diào)用
4、constructor(mContext: Context, attrs: AttributeSet,defStyleAttr:Int,defStyleRes:Int)
—>該構(gòu)造函數(shù)是在API21的時候才添加上的

三、下面我們就開始來看看代碼啦
1、自定義View的屬性,首先在res/values/ 下建立一個attrs.xml , 在里面定義我們的需要用到的屬性以及聲明相對應(yīng)屬性的取值類型

<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <TextView
        android:id="@+id/tv_weight_tip"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="體重"
        android:textColor="@android:color/black"
        android:textSize="14dp"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintVertical_bias="0.132" />
    <RelativeLayout
        android:id="@+id/rl_weight_ruler"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        app:layout_constraintTop_toBottomOf="@+id/tv_weight_tip"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent">

        <per.lijuan.rulerdome.RulerView
            android:id="@+id/ruler_weight"
            android:layout_width="match_parent"
            android:layout_height="58dp"
            android:layout_marginTop="24dp"
            app:alphaEnable="true"
            app:lineColor="@android:color/darker_gray"
            app:lineMaxHeight="40dp"
            app:lineMidHeight="30dp"
            app:lineMinHeight="20dp"
            app:lineSpaceWidth="10dp"
            app:lineWidth="2.5dp"
            app:textColor="@android:color/black"
            app:minValue="20"
            app:maxValue="200"
            app:perValue="0.1"
            app:selectorValue="55"/>

        <ImageView
            android:layout_width="14dp"
            android:layout_height="46dp"
            android:layout_centerHorizontal="true"
            android:layout_marginTop="6dp"
            android:scaleType="fitXY"
            android:src="@mipmap/ic_arrow"/>
    </RelativeLayout>

    <TextView
        android:id="@+id/tv_weight"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="11dp"
        android:maxHeight="30sp"
        android:textColor="@color/colorPrimary"
        android:textSize="24sp"
        app:layout_constraintTop_toBottomOf="@+id/rl_weight_ruler"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"/>
</android.support.constraint.ConstraintLayout>

一定要引入xmlns:app="http://schemas.android.com/apk/res-auto",Android Studio中我們可以使用res-atuo命名空間,就不用在添加自定義View全類名。

3、在View的構(gòu)造方法中,獲得我們的自定義的樣式

private var mMinVelocity:Int = 0
    private var mScroller: Scroller? = null//Scroller是一個專門用于處理滾動效果的工具類   用mScroller記錄/計算View滾動的位置,再重寫View的computeScroll(),完成實際的滾動
    private var mVelocityTracker: VelocityTracker?=null//主要用跟蹤觸摸屏事件(flinging事件和其他gestures手勢事件)的速率。
    private var mWidth:Int = 0
    private var mHeight:Int = 0

    private var mSelectorValue=50f      // 未選擇時 默認的值 滑動后表示當(dāng)前中間指針正在指著的值
    private var mMaxValue=200f          // 最大數(shù)值
    private var mMinValue=100f          //最小的數(shù)值
    private var mPerValue=1f            //最小單位(如 1:表示每2條刻度差為1;0.1:表示每2條刻度差為0.1

    private var mLineSpaceWidth = 5f    //  尺子刻度2條線之間的距離
    private var mLineWidth = 4f         //  尺子刻度的寬度
    private var mLineMaxHeight = 420f   //  尺子刻度分為3中不同的高度。 mLineMaxHeight表示最長的那根(也就是 10的倍數(shù)時的高度)
    private var mLineMidHeight = 30f    //  mLineMidHeight  表示中間的高度(也就是 5  15 25 等時的高度)
    private var mLineMinHeight = 17f    //  mLineMinHeight  表示最短的那個高度(也就是 1 2 3 4 等時的高度)

    private var mTextMarginTop = 10f
    private var mTextSize = 30f          //尺子刻度下方數(shù)字的大小
    private var mAlphaEnable=false       // 尺子 最左邊 最后邊是否需要透明 `(透明效果更好點)
    private var mTextHeight = 0.toFloat()//尺子刻度下方數(shù)字的高度
    private var mTextPaint: Paint?=null   // 尺子刻度下方數(shù)字(也就是每隔10個出現(xiàn)的數(shù)值)畫筆
    private var mLinePaint: Paint?=null   //  尺子刻度線的畫筆

    private var mTotalLine:Int = 0       //共有多少條 刻度
    private var mMaxOffset:Int = 0       //所有刻度 共有多長
    private var mOffset:Float = 0.toFloat()// 默認狀態(tài)下,mSelectorValue所在的位置  位于尺子總刻度的位置
    private var mLastX:Int = 0
    private var mMove: Int = 0
    private lateinit var mListener: OnValueChangeListener// 滑動后數(shù)值回調(diào)

    private var mLineColor:Int= Color.GRAY //刻度的顏色
    private var mTextColor:Int= Color.BLACK//文字的顏色

    constructor(mContext: Context) : super(mContext,null)

    constructor(mContext: Context, attrs: AttributeSet) : super(mContext, attrs,0)

    constructor(mContext: Context, attrs: AttributeSet,defStyleAttr:Int) : super(mContext, attrs,defStyleAttr) {
        init(mContext, attrs)
    }

    fun init(context: Context, attrs: AttributeSet){
        Log.d(TAG, "init")
        mScroller= Scroller(context)

        this.mLineSpaceWidth=myfloat(25.0f)
        this.mLineWidth=myfloat(2.0f)
        this.mLineMaxHeight=myfloat(100.0f)
        this.mLineMidHeight=myfloat(60.0f)
        this.mLineMinHeight=myfloat(40.0f)
        this.mTextHeight=myfloat(40.0f)

        val typedArray: TypedArray =context.obtainStyledAttributes(attrs,
                R.styleable.RulerView)

        mAlphaEnable= typedArray.getBoolean(R.styleable.RulerView_alphaEnable, mAlphaEnable)

        mLineSpaceWidth = typedArray.getDimension(R.styleable.RulerView_lineSpaceWidth, mLineSpaceWidth)
        mLineWidth = typedArray.getDimension(R.styleable.RulerView_lineWidth, mLineWidth)
        mLineMaxHeight = typedArray.getDimension(R.styleable.RulerView_lineMaxHeight, mLineMaxHeight)
        mLineMidHeight = typedArray.getDimension(R.styleable.RulerView_lineMidHeight, mLineMidHeight)
        mLineMinHeight = typedArray.getDimension(R.styleable.RulerView_lineMinHeight, mLineMinHeight)
        mLineColor = typedArray.getColor(R.styleable.RulerView_lineColor, mLineColor)

        mTextSize = typedArray.getDimension(R.styleable.RulerView_textSize, mTextSize)
        mTextColor = typedArray.getColor(R.styleable.RulerView_textColor, mTextColor)
        mTextMarginTop = typedArray.getDimension(R.styleable.RulerView_textMarginTop, mTextMarginTop)

        mSelectorValue = typedArray.getFloat(R.styleable.RulerView_selectorValue, 0.0f)
        mMinValue = typedArray.getFloat(R.styleable.RulerView_minValue, 0.0f)
        mMaxValue = typedArray.getFloat(R.styleable.RulerView_maxValue, 100.0f)
        mPerValue = typedArray.getFloat(R.styleable.RulerView_perValue, 0.1f)

        mMinVelocity= ViewConfiguration.get(getContext()).scaledMinimumFlingVelocity

        mTextPaint = Paint(Paint.ANTI_ALIAS_FLAG)
        mTextPaint!!.textSize = mTextSize
        mTextPaint!!.color = mTextColor
        mTextHeight = getFontHeight(mTextPaint!!)

        mLinePaint = Paint(Paint.ANTI_ALIAS_FLAG)
        mLinePaint!!.strokeWidth = mLineWidth
        mLinePaint!!.color = mLineColor
    }

我們重寫了3個構(gòu)造方法,在上面的構(gòu)造方法中說過默認的布局文件調(diào)用的是兩個參數(shù)的構(gòu)造方法,所以記得讓所有的構(gòu)造方法調(diào)用三個參數(shù)的構(gòu)造方法,然后在三個參數(shù)的構(gòu)造方法中獲得自定義屬性。
一開始一個參數(shù)的構(gòu)造方法和兩個參數(shù)的構(gòu)造方法是這樣的:

constructor(mContext: Context) : super (mContext)

    constructor(mContext: Context, attrs: AttributeSet?) : super(mContext, attrs)

有一點要注意的是super應(yīng)該改成this,然后讓一個參數(shù)的構(gòu)造方法引用兩個參數(shù)的構(gòu)造方法,兩個參數(shù)的構(gòu)造方法引用三個參數(shù)的構(gòu)造方法,代碼如下:

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

    constructor(mContext: Context, attrs: AttributeSet?) : this(mContext, attrs!!,0)

    constructor(mContext: Context, attrs: AttributeSet,defStyleAttr:Int) : super(mContext, attrs,defStyleAttr) {
        init(mContext, attrs)
    }

4、重寫onDraw方法

override fun onDraw(canvas: Canvas) {
        super.onDraw(canvas)
        var left: Float
        var height: Float
        var value: String
        var alpha = 0
        var scale: Float
        val srcPointX = mWidth / 2
        for (i in 0 until mTotalLine) {
            left = srcPointX.toFloat() + mOffset + i * mLineSpaceWidth

            if (left < 0 || left > mWidth) {
                continue  //先畫默認值在正中間,左右各一半的view。多余部分暫時不畫(也就是從默認值在中間,畫旁邊左右的刻度線)
            }

            if (i % 10 == 0) {
                height = mLineMaxHeight
            } else if (i % 5 == 0) {
                height = mLineMidHeight
            } else {
                height = mLineMinHeight
            }
            if (mAlphaEnable) {
                scale = 1 - Math.abs(left - srcPointX) / srcPointX
                alpha = (255f * scale * scale).toInt()

                mLinePaint!!.setAlpha(alpha)
            }
            canvas.drawLine(left, 0f, left, height, mLinePaint)

            if (i % 10 == 0) {
                value = (mMinValue + i * mPerValue / 10).toInt().toString()
                if (mAlphaEnable) {
                    mTextPaint!!.alpha = alpha
                }
                canvas.drawText(value, left - mTextPaint!!.measureText(value) / 2,
                        height + mTextMarginTop + mTextHeight, mTextPaint)    // 在為整數(shù)時,畫 數(shù)值
            }
        }
    }

View的繪制流程是從ViewRoot的performTravarsals方法開始的,經(jīng)過measure、layout和draw三個過程才能最終將一個View繪制出來,其中:

測量——onMeasure():用來測量View的寬和高來決定View的大小
布局——onLayout():用來確定View在父容器ViewGroup中的放置位置
繪制——onDraw():負責(zé)將View繪制在屏幕上

5、重寫onTouchEvent方法
onTouchEvent()是View自帶的接口,Android系統(tǒng)提供了默認的實現(xiàn),用于處理觸摸事件。當(dāng)我們對標(biāo)尺控件向左向右滑動時,此方法就會被調(diào)用。

override fun onTouchEvent(event: MotionEvent): Boolean {
        Log.d(TAG, "onTouchEvent")

        val action = event.action
        val xPosition = event.x.toInt()

        if (mVelocityTracker == null) {
            mVelocityTracker = VelocityTracker.obtain()
        }
        mVelocityTracker!!.addMovement(event)

        when (action) {
            MotionEvent.ACTION_DOWN -> {
                mScroller!!.forceFinished(true)
                mLastX = xPosition
                mMove = 0
            }
            MotionEvent.ACTION_MOVE -> {
                mMove = mLastX - xPosition
                changeMoveAndValue()
            }
            MotionEvent.ACTION_UP, MotionEvent.ACTION_CANCEL -> {
                countMoveEnd()
                countVelocityTracker()
                return false
            }
            else -> {
            }
        }

        mLastX = xPosition
        return true
    }

現(xiàn)在我把完整的代碼貼出來

package per.lijuan.rulerdome

import android.content.Context
import android.content.res.TypedArray
import android.graphics.Canvas
import android.graphics.Color
import android.graphics.Paint
import android.util.AttributeSet
import android.util.Log
import android.view.MotionEvent
import android.view.VelocityTracker
import android.view.View
import android.view.ViewConfiguration
import android.widget.Scroller

/**
 * Created by juan on 2018/5/11.
 */
class RulerView: View {
    private val TAG : String = "RulerView"

    private var mMinVelocity:Int = 0
    private var mScroller: Scroller? = null//Scroller是一個專門用于處理滾動效果的工具類   用mScroller記錄/計算View滾動的位置,再重寫View的computeScroll(),完成實際的滾動
    private var mVelocityTracker: VelocityTracker?=null//主要用跟蹤觸摸屏事件(flinging事件和其他gestures手勢事件)的速率。
    private var mWidth:Int = 0
    private var mHeight:Int = 0

    private var mSelectorValue=50f      // 未選擇時 默認的值 滑動后表示當(dāng)前中間指針正在指著的值
    private var mMaxValue=200f          // 最大數(shù)值
    private var mMinValue=100f          //最小的數(shù)值
    private var mPerValue=1f            //最小單位(如 1:表示每2條刻度差為1;0.1:表示每2條刻度差為0.1

    private var mLineSpaceWidth = 5f    //  尺子刻度2條線之間的距離
    private var mLineWidth = 4f         //  尺子刻度的寬度
    private var mLineMaxHeight = 420f   //  尺子刻度分為3中不同的高度。 mLineMaxHeight表示最長的那根(也就是 10的倍數(shù)時的高度)
    private var mLineMidHeight = 30f    //  mLineMidHeight  表示中間的高度(也就是 5  15 25 等時的高度)
    private var mLineMinHeight = 17f    //  mLineMinHeight  表示最短的那個高度(也就是 1 2 3 4 等時的高度)

    private var mTextMarginTop = 10f
    private var mTextSize = 30f          //尺子刻度下方數(shù)字的大小
    private var mAlphaEnable=false       // 尺子 最左邊 最后邊是否需要透明 `(透明效果更好點)
    private var mTextHeight = 0.toFloat()//尺子刻度下方數(shù)字的高度
    private var mTextPaint: Paint?=null   // 尺子刻度下方數(shù)字(也就是每隔10個出現(xiàn)的數(shù)值)畫筆
    private var mLinePaint: Paint?=null   //  尺子刻度線的畫筆

    private var mTotalLine:Int = 0       //共有多少條 刻度
    private var mMaxOffset:Int = 0       //所有刻度 共有多長
    private var mOffset:Float = 0.toFloat()// 默認狀態(tài)下,mSelectorValue所在的位置  位于尺子總刻度的位置
    private var mLastX:Int = 0
    private var mMove: Int = 0
    private lateinit var mListener: OnValueChangeListener// 滑動后數(shù)值回調(diào)

    private var mLineColor:Int= Color.GRAY //刻度的顏色
    private var mTextColor:Int= Color.BLACK//文字的顏色

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

    constructor(mContext: Context, attrs: AttributeSet?) : this(mContext, attrs!!,0)

    constructor(mContext: Context, attrs: AttributeSet,defStyleAttr:Int) : super(mContext, attrs,defStyleAttr) {
        init(mContext, attrs)
    }

    fun init(context: Context, attrs: AttributeSet){
        Log.d(TAG, "init")
        mScroller= Scroller(context)

        this.mLineSpaceWidth=myfloat(25.0f)
        this.mLineWidth=myfloat(2.0f)
        this.mLineMaxHeight=myfloat(100.0f)
        this.mLineMidHeight=myfloat(60.0f)
        this.mLineMinHeight=myfloat(40.0f)
        this.mTextHeight=myfloat(40.0f)

        val typedArray: TypedArray =context.obtainStyledAttributes(attrs,
                R.styleable.RulerView)

        mAlphaEnable= typedArray.getBoolean(R.styleable.RulerView_alphaEnable, mAlphaEnable)

        mLineSpaceWidth = typedArray.getDimension(R.styleable.RulerView_lineSpaceWidth, mLineSpaceWidth)
        mLineWidth = typedArray.getDimension(R.styleable.RulerView_lineWidth, mLineWidth)
        mLineMaxHeight = typedArray.getDimension(R.styleable.RulerView_lineMaxHeight, mLineMaxHeight)
        mLineMidHeight = typedArray.getDimension(R.styleable.RulerView_lineMidHeight, mLineMidHeight)
        mLineMinHeight = typedArray.getDimension(R.styleable.RulerView_lineMinHeight, mLineMinHeight)
        mLineColor = typedArray.getColor(R.styleable.RulerView_lineColor, mLineColor)

        mTextSize = typedArray.getDimension(R.styleable.RulerView_textSize, mTextSize)
        mTextColor = typedArray.getColor(R.styleable.RulerView_textColor, mTextColor)
        mTextMarginTop = typedArray.getDimension(R.styleable.RulerView_textMarginTop, mTextMarginTop)

        mSelectorValue = typedArray.getFloat(R.styleable.RulerView_selectorValue, 0.0f)
        mMinValue = typedArray.getFloat(R.styleable.RulerView_minValue, 0.0f)
        mMaxValue = typedArray.getFloat(R.styleable.RulerView_maxValue, 100.0f)
        mPerValue = typedArray.getFloat(R.styleable.RulerView_perValue, 0.1f)

        mMinVelocity= ViewConfiguration.get(getContext()).scaledMinimumFlingVelocity

        mTextPaint = Paint(Paint.ANTI_ALIAS_FLAG)
        mTextPaint!!.textSize = mTextSize
        mTextPaint!!.color = mTextColor
        mTextHeight = getFontHeight(mTextPaint!!)

        mLinePaint = Paint(Paint.ANTI_ALIAS_FLAG)
        mLinePaint!!.strokeWidth = mLineWidth
        mLinePaint!!.color = mLineColor
    }

    private fun myfloat(paramFloat:Float):Float{
        return 0.5f+paramFloat*1.0f
    }

    private fun getFontHeight(paint: Paint):Float{
        val fm = paint.fontMetrics
        return fm.descent - fm.ascent
    }

    /**
     * 設(shè)置默認的參數(shù)
     * @param selectorValue 未選擇時 默認的值 滑動后表示當(dāng)前中間指針正在指著的值
     * @param minValue   最大數(shù)值
     * @param maxValue   最小的數(shù)值
     * @param per   最小單位(如1:表示每2條刻度差為1;0.1:表示每2條刻度差為0.1;其中身高mPerValue為1,體重mPerValue 為0.1)
     */
    fun setValue(selectorValue: Float, minValue: Float, maxValue: Float, per: Float) {
        this.mSelectorValue = selectorValue
        this.mMaxValue = maxValue
        this.mMinValue = minValue
        this.mPerValue = per * 10.0f
        this.mTotalLine = ((mMaxValue * 10 - mMinValue * 10) / mPerValue).toInt() + 1


        mMaxOffset = (-(mTotalLine - 1) * mLineSpaceWidth).toInt()
        mOffset = (mMinValue - mSelectorValue) / mPerValue * mLineSpaceWidth * 10f
        Log.d(TAG, "mOffset:" + mOffset + ",mMaxOffset:" + mMaxOffset
                + ",mTotalLine:" + mTotalLine)
        invalidate()
        visibility = View.VISIBLE
    }

    fun setOnValueChangeListener(listener: OnValueChangeListener) {
        mListener = listener
    }

    override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {

        super.onSizeChanged(w, h, oldw, oldh)
        if (w > 0 && h > 0) {
            mWidth = w
            mHeight = h
        }
    }

    override fun onDraw(canvas: Canvas) {
        super.onDraw(canvas)
        var left: Float
        var height: Float
        var value: String
        var alpha = 0
        var scale: Float
        val srcPointX = mWidth / 2
        for (i in 0 until mTotalLine) {
            left = srcPointX.toFloat() + mOffset + i * mLineSpaceWidth

            if (left < 0 || left > mWidth) {
                continue  //先畫默認值在正中間,左右各一半的view。多余部分暫時不畫(也就是從默認值在中間,畫旁邊左右的刻度線)
            }

            if (i % 10 == 0) {
                height = mLineMaxHeight
            } else if (i % 5 == 0) {
                height = mLineMidHeight
            } else {
                height = mLineMinHeight
            }
            if (mAlphaEnable) {
                scale = 1 - Math.abs(left - srcPointX) / srcPointX
                alpha = (255f * scale * scale).toInt()

                mLinePaint!!.setAlpha(alpha)
            }
            canvas.drawLine(left, 0f, left, height, mLinePaint)

            if (i % 10 == 0) {
                value = (mMinValue + i * mPerValue / 10).toInt().toString()
                if (mAlphaEnable) {
                    mTextPaint!!.alpha = alpha
                }
                canvas.drawText(value, left - mTextPaint!!.measureText(value) / 2,
                        height + mTextMarginTop + mTextHeight, mTextPaint)    // 在為整數(shù)時,畫 數(shù)值
            }
        }
    }

    override fun onTouchEvent(event: MotionEvent): Boolean {
        Log.d(TAG, "onTouchEvent")

        val action = event.action
        val xPosition = event.x.toInt()

        if (mVelocityTracker == null) {
            mVelocityTracker = VelocityTracker.obtain()
        }
        mVelocityTracker!!.addMovement(event)

        when (action) {
            MotionEvent.ACTION_DOWN -> {
                mScroller!!.forceFinished(true)
                mLastX = xPosition
                mMove = 0
            }
            MotionEvent.ACTION_MOVE -> {
                mMove = mLastX - xPosition
                changeMoveAndValue()
            }
            MotionEvent.ACTION_UP, MotionEvent.ACTION_CANCEL -> {
                countMoveEnd()
                countVelocityTracker()
                return false
            }
            else -> {
            }
        }

        mLastX = xPosition
        return true
    }

    private fun countVelocityTracker() {
        Log.d(TAG, "countVelocityTracker")
        mVelocityTracker!!.computeCurrentVelocity(1000)  //初始化速率的單位
        val xVelocity = mVelocityTracker!!.xVelocity //當(dāng)前的速度
        if (Math.abs(xVelocity) > mMinVelocity) {
            mScroller!!.fling(0, 0, xVelocity.toInt(), 0, Integer.MIN_VALUE, Integer.MAX_VALUE, 0, 0)
        }
    }

    /**
     * 滑動結(jié)束后,若是指針在2條刻度之間時,改變mOffset 讓指針正好在刻度上。
     */
    private fun countMoveEnd() {
        mOffset -= mMove.toFloat()
        if (mOffset <= mMaxOffset) {
            mOffset = mMaxOffset.toFloat()
        } else if (mOffset >= 0) {
            mOffset = 0f
        }

        mLastX = 0
        mMove = 0

        mSelectorValue = mMinValue + Math.round(Math.abs(mOffset) * 1.0f / mLineSpaceWidth) * mPerValue / 10.0f
        mOffset = (mMinValue - mSelectorValue) * 10.0f / mPerValue * mLineSpaceWidth

        notifyValueChange()
        postInvalidate()
    }

    /**
     * 滑動后的操作
     */
    private fun changeMoveAndValue() {
        mOffset -= mMove.toFloat()

        if (mOffset <= mMaxOffset) {
            mOffset = mMaxOffset.toFloat()
            mMove = 0
            mScroller!!.forceFinished(true)
        } else if (mOffset >= 0) {
            mMove = 0
            mScroller!!.forceFinished(true)
        }
        mSelectorValue = mMinValue + Math.round(Math.abs(mOffset) * 1.0f / mLineSpaceWidth) * mPerValue / 10.0f


        notifyValueChange()
        postInvalidate()
    }

    private fun notifyValueChange() {
        if (null != mListener) {
            mListener.onValueChange(mSelectorValue)
        }
    }

    /**
     * 滑動后的回調(diào)
     */
    interface OnValueChangeListener{
        fun onValueChange(value: Float)
    }

    override fun computeScroll() {
        Log.d(TAG, "computeScroll")
        super.computeScroll()
        if (mScroller!!.computeScrollOffset()) {//mScroller.computeScrollOffset()返回true表示滑動還沒有結(jié)束
            if (mScroller!!.currX == mScroller!!.finalX) {
                countMoveEnd()
            } else {
                val xPosition = mScroller!!.currX
                mMove = mLastX - xPosition
                changeMoveAndValue()
                mLastX = xPosition
            }
        }
    }
}

在頁面中,我們要給自定義的標(biāo)尺設(shè)置默認的參數(shù):未選擇時默認的值、最大數(shù)值、最小的數(shù)值以及最小單位

//體重的view
        mWeightRuler!!.setOnValueChangeListener(object : RulerView.OnValueChangeListener {
            override fun onValueChange(value: Float) {
                weight = value
                mTvWeight!!.text = weight.toString() + "kg"
            }
        })
        mWeightRuler!!.setValue(55f, 20f, 200f, 0.1f)

參考資料:
https://github.com/panacena/RuleView

源碼下載

有什么疑問的,請在下面留言,有不足的還望指導(dǎo),感謝各位_

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容