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

一、先總結(jié)下自定義View的步驟:
1、自定義View的屬性
2、在View的構(gòu)造方法中獲得我們自定義的屬性
3、重寫onMesure
4、重寫onDraw
其中onMesure方法不一定要重寫,但大部分情況下還是需要重寫的
二、View 的幾個(gè)構(gòu)造函數(shù)
1、constructor(mContext: Context)
—>java代碼直接new一個(gè)RulerView實(shí)例的時(shí)候,會(huì)調(diào)用這個(gè)只有一個(gè)參數(shù)的構(gòu)造函數(shù);
2、constructor(mContext: Context, attrs: AttributeSet)
—>在默認(rèn)的XML布局文件中創(chuàng)建的時(shí)候調(diào)用這個(gè)有兩個(gè)參數(shù)的構(gòu)造函數(shù)。AttributeSet類型的參數(shù)負(fù)責(zé)把XML布局文件中所自定義的屬性通過AttributeSet帶入到View內(nèi);
3、constructor(mContext: Context, attrs: AttributeSet,defStyleAttr:Int)
—>構(gòu)造函數(shù)中第三個(gè)參數(shù)是默認(rèn)的Style,這里的默認(rèn)的Style是指它在當(dāng)前Application或者Activity所用的Theme中的默認(rèn)Style,且只有在明確調(diào)用的時(shí)候才會(huì)調(diào)用
4、constructor(mContext: Context, attrs: AttributeSet,defStyleAttr:Int,defStyleRes:Int)
—>該構(gòu)造函數(shù)是在API21的時(shí)候才添加上的
三、下面我們就開始來看看代碼啦
1、自定義View的屬性,首先在res/values/ 下建立一個(gè)attrs.xml , 在里面定義我們的需要用到的屬性以及聲明相對(duì)應(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是一個(gè)專門用于處理滾動(dòng)效果的工具類 用mScroller記錄/計(jì)算View滾動(dòng)的位置,再重寫View的computeScroll(),完成實(shí)際的滾動(dòng)
private var mVelocityTracker: VelocityTracker?=null//主要用跟蹤觸摸屏事件(flinging事件和其他gestures手勢事件)的速率。
private var mWidth:Int = 0
private var mHeight:Int = 0
private var mSelectorValue=50f // 未選擇時(shí) 默認(rèn)的值 滑動(dòng)后表示當(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ù)時(shí)的高度)
private var mLineMidHeight = 30f // mLineMidHeight 表示中間的高度(也就是 5 15 25 等時(shí)的高度)
private var mLineMinHeight = 17f // mLineMinHeight 表示最短的那個(gè)高度(也就是 1 2 3 4 等時(shí)的高度)
private var mTextMarginTop = 10f
private var mTextSize = 30f //尺子刻度下方數(shù)字的大小
private var mAlphaEnable=false // 尺子 最左邊 最后邊是否需要透明 `(透明效果更好點(diǎn))
private var mTextHeight = 0.toFloat()//尺子刻度下方數(shù)字的高度
private var mTextPaint: Paint?=null // 尺子刻度下方數(shù)字(也就是每隔10個(gè)出現(xiàn)的數(shù)值)畫筆
private var mLinePaint: Paint?=null // 尺子刻度線的畫筆
private var mTotalLine:Int = 0 //共有多少條 刻度
private var mMaxOffset:Int = 0 //所有刻度 共有多長
private var mOffset:Float = 0.toFloat()// 默認(rèn)狀態(tài)下,mSelectorValue所在的位置 位于尺子總刻度的位置
private var mLastX:Int = 0
private var mMove: Int = 0
private lateinit var mListener: OnValueChangeListener// 滑動(dòng)后數(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è)構(gòu)造方法,在上面的構(gòu)造方法中說過默認(rèn)的布局文件調(diào)用的是兩個(gè)參數(shù)的構(gòu)造方法,所以記得讓所有的構(gòu)造方法調(diào)用三個(gè)參數(shù)的構(gòu)造方法,然后在三個(gè)參數(shù)的構(gòu)造方法中獲得自定義屬性。
一開始一個(gè)參數(shù)的構(gòu)造方法和兩個(gè)參數(shù)的構(gòu)造方法是這樣的:
constructor(mContext: Context) : super (mContext) constructor(mContext: Context, attrs: AttributeSet?) : super(mContext, attrs)
有一點(diǎn)要注意的是super應(yīng)該改成this,然后讓一個(gè)參數(shù)的構(gòu)造方法引用兩個(gè)參數(shù)的構(gòu)造方法,兩個(gè)參數(shù)的構(gòu)造方法引用三個(gè)參數(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 //先畫默認(rèn)值在正中間,左右各一半的view。多余部分暫時(shí)不畫(也就是從默認(rèn)值在中間,畫旁邊左右的刻度線)
}
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í),畫 數(shù)值
}
}
}
View的繪制流程是從ViewRoot的performTravarsals方法開始的,經(jīng)過measure、layout和draw三個(gè)過程才能最終將一個(gè)View繪制出來,其中:
測量——onMeasure():用來測量View的寬和高來決定View的大小
布局——onLayout():用來確定View在父容器ViewGroup中的放置位置
繪制——onDraw():負(fù)責(zé)將View繪制在屏幕上
5、重寫onTouchEvent方法
onTouchEvent()是View自帶的接口,Android系統(tǒng)提供了默認(rèn)的實(shí)現(xiàn),用于處理觸摸事件。當(dāng)我們對(duì)標(biāo)尺控件向左向右滑動(dòng)時(shí),此方法就會(huì)被調(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是一個(gè)專門用于處理滾動(dòng)效果的工具類 用mScroller記錄/計(jì)算View滾動(dòng)的位置,再重寫View的computeScroll(),完成實(shí)際的滾動(dòng)
private var mVelocityTracker: VelocityTracker?=null//主要用跟蹤觸摸屏事件(flinging事件和其他gestures手勢事件)的速率。
private var mWidth:Int = 0
private var mHeight:Int = 0
private var mSelectorValue=50f // 未選擇時(shí) 默認(rèn)的值 滑動(dòng)后表示當(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ù)時(shí)的高度)
private var mLineMidHeight = 30f // mLineMidHeight 表示中間的高度(也就是 5 15 25 等時(shí)的高度)
private var mLineMinHeight = 17f // mLineMinHeight 表示最短的那個(gè)高度(也就是 1 2 3 4 等時(shí)的高度)
private var mTextMarginTop = 10f
private var mTextSize = 30f //尺子刻度下方數(shù)字的大小
private var mAlphaEnable=false // 尺子 最左邊 最后邊是否需要透明 `(透明效果更好點(diǎn))
private var mTextHeight = 0.toFloat()//尺子刻度下方數(shù)字的高度
private var mTextPaint: Paint?=null // 尺子刻度下方數(shù)字(也就是每隔10個(gè)出現(xiàn)的數(shù)值)畫筆
private var mLinePaint: Paint?=null // 尺子刻度線的畫筆
private var mTotalLine:Int = 0 //共有多少條 刻度
private var mMaxOffset:Int = 0 //所有刻度 共有多長
private var mOffset:Float = 0.toFloat()// 默認(rèn)狀態(tài)下,mSelectorValue所在的位置 位于尺子總刻度的位置
private var mLastX:Int = 0
private var mMove: Int = 0
private lateinit var mListener: OnValueChangeListener// 滑動(dòng)后數(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è)置默認(rèn)的參數(shù)
* @param selectorValue 未選擇時(shí) 默認(rèn)的值 滑動(dòng)后表示當(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 //先畫默認(rèn)值在正中間,左右各一半的view。多余部分暫時(shí)不畫(也就是從默認(rèn)值在中間,畫旁邊左右的刻度線)
}
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í),畫 數(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)
}
}
/**
* 滑動(dòng)結(jié)束后,若是指針在2條刻度之間時(shí),改變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()
}
/**
* 滑動(dòng)后的操作
*/
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)
}
}
/**
* 滑動(dòng)后的回調(diào)
*/
interface OnValueChangeListener{
fun onValueChange(value: Float)
}
override fun computeScroll() {
Log.d(TAG, "computeScroll")
super.computeScroll()
if (mScroller!!.computeScrollOffset()) {//mScroller.computeScrollOffset()返回true表示滑動(dòng)還沒有結(jié)束
if (mScroller!!.currX == mScroller!!.finalX) {
countMoveEnd()
} else {
val xPosition = mScroller!!.currX
mMove = mLastX - xPosition
changeMoveAndValue()
mLastX = xPosition
}
}
}
}
在頁面中,我們要給自定義的標(biāo)尺設(shè)置默認(rèn)的參數(shù):未選擇時(shí)默認(rèn)的值、最大數(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
總結(jié)
以上就是這篇文章的全部內(nèi)容了,希望本文的內(nèi)容對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,如果有疑問大家可以留言交流,謝謝大家對(duì)腳本之家的支持。
相關(guān)文章
android異步任務(wù)設(shè)計(jì)思詳解(AsyncTask)
AsyncTask在Android十分常用,那為什么如此常用呢,不用行不行呢,內(nèi)部又是怎么實(shí)現(xiàn)的呢,為什么Java的API中沒有這個(gè)類呢,看完本文后,你將會(huì)知道答案2014-02-02
Android星級(jí)評(píng)分條實(shí)現(xiàn)評(píng)分界面
這篇文章主要為大家詳細(xì)介紹了Android星級(jí)評(píng)分條實(shí)現(xiàn)評(píng)分界面,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-05-05
android自動(dòng)生成dimens適配文件的圖文教程詳解(無需Java工具類)
這篇文章主要介紹了android自動(dòng)生成dimens適配文件,無需Java工具類,本文通過圖文并茂的形式給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-03-03
在Android中創(chuàng)建菜單項(xiàng)Menu以及獲取手機(jī)分辨率的解決方法
本篇文章小編為大家介紹,在Android中創(chuàng)建菜單項(xiàng)Menu以及獲取手機(jī)分辨率的解決方法。需要的朋友參考下2013-04-04

