這篇文章主要介紹Kotlin如何自定義View之標(biāo)尺控件,文中介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們一定要看完!
員工經(jīng)過(guò)長(zhǎng)期磨合與沉淀,具備了協(xié)作精神,得以通過(guò)團(tuán)隊(duì)的力量開發(fā)出優(yōu)質(zhì)的產(chǎn)品。成都創(chuàng)新互聯(lián)公司堅(jiān)持“專注、創(chuàng)新、易用”的產(chǎn)品理念,因?yàn)椤皩W⑺詫I(yè)、創(chuàng)新互聯(lián)網(wǎng)站所以易用所以簡(jiǎn)單”。公司專注于為企業(yè)提供成都做網(wǎng)站、成都網(wǎng)站建設(shè)、微信公眾號(hào)開發(fā)、電商網(wǎng)站開發(fā),微信小程序定制開發(fā),軟件定制網(wǎng)站開發(fā)等一站式互聯(lián)網(wǎng)企業(yè)服務(wù)。
按照慣例,我們先來(lái)看看效果圖
一、先總結(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布局文件中所自定義的屬性通過(guò)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í)候才添加上的
三、下面我們就開始來(lái)看看代碼啦
1、自定義View的屬性,首先在res/values/ 下建立一個(gè)attrs.xml , 在里面定義我們的需要用到的屬性以及聲明相對(duì)應(yīng)屬性的取值類型
一定要引入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手勢(shì)事件)的速率。 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表示最長(zhǎng)的那根(也就是 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 //所有刻度 共有多長(zhǎng) 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)造方法中說(shuō)過(guò)默認(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)過(guò)measure、layout和draw三個(gè)過(guò)程才能最終將一個(gè)View繪制出來(lái),其中:
測(cè)量——onMeasure():用來(lái)測(cè)量View的寬和高來(lái)決定View的大小
布局——onLayout():用來(lái)確定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)在我把完整的代碼貼出來(lái)
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手勢(shì)事件)的速率。 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表示最長(zhǎng)的那根(也就是 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 //所有刻度 共有多長(zhǎng) 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)還沒(méi)有結(jié)束 if (mScroller!!.currX == mScroller!!.finalX) { countMoveEnd() } else { val xPosition = mScroller!!.currX mMove = mLastX - xPosition changeMoveAndValue() mLastX = xPosition } } } }
在頁(yè)面中,我們要給自定義的標(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)
以上是“Kotlin如何自定義View之標(biāo)尺控件”這篇文章的所有內(nèi)容,感謝各位的閱讀!希望分享的內(nèi)容對(duì)大家有幫助,更多相關(guān)知識(shí),歡迎關(guān)注創(chuàng)新互聯(lián)行業(yè)資訊頻道!