開始之前bb幾句,雖然現(xiàn)在已經(jīng)有類似的開源項(xiàng)目,但如果想真正理解它,還是要自己去手寫一下,這不,趁著這兩天空閑,公司項(xiàng)目不忙,就自己寫了個(gè)demo,基本功能已完成,可能會(huì)有bug,回頭再優(yōu)化。
先說(shuō)一下基本思路:
1.先創(chuàng)建一個(gè)類,繼承View,先把刻度畫出來(lái)。
2.再創(chuàng)建一個(gè)類,繼承ViewGroup,這個(gè)類主要是控制內(nèi)部尺子的滾動(dòng)。
下面直接貼代碼:
RulerView:
package com.test.demotestobject.view
import android.content.Context
import android.graphics.Canvas
import android.graphics.Color
import android.graphics.Paint
import android.graphics.Path
import android.util.AttributeSet
import android.util.Log
import android.view.View
import androidx.annotation.Nullable
open class RulerView : View{
private lateinit var mPaint : Paint
private lateinit var path: Path
private var color:String = "#FF4081"
//尺子Y軸水平基點(diǎn)
private var startYDraw : Float=0f
//矯正刻度起始位置
private var firststep : Float = 0f
//每個(gè)刻度起始位置
private var cachStep: Float = 0f
//尺子長(zhǎng)度
private var ruleSize = 501
//一屏幕的刻度數(shù)
private var viewAll = 60
private lateinit var numListener:NumListener
private var isScrowComplent = false
constructor(context: Context?) : super(context) {
initView()
}
constructor(context:Context , @Nullable attrs: AttributeSet): super(context,attrs) {
initView()
}
constructor(context:Context , @Nullable attrs: AttributeSet,defStyleAttr:Int) : super(context,attrs,defStyleAttr){
initView()
}
private fun initView() {
mPaint = Paint()
path = Path()
mPaint.style=(Paint.Style.FILL)
mPaint.strokeWidth=(2f)
mPaint.color=(Color.parseColor(color))
mPaint.textAlign=(Paint.Align.CENTER)
mPaint.textSize=(14f)
mPaint.isAntiAlias=true
mPaint.isFilterBitmap=true
}
fun setScrollComplete(isScrollComplete:Boolean) {
this.isScrowComplent = isScrollComplete
invalidate()
}
/**
* 滾動(dòng)時(shí)調(diào)用(給RuleViewGroup調(diào)用,不要私自調(diào)用)
*
* @param changeX
*/
fun setChangeX(changeX:Float) {
isLastComplete=false
firststep += changeX
invalidate()
}
/**
* 置為中點(diǎn)使用(給RuleViewGroup調(diào)用,不要私自調(diào)用)
*
* @param changeX
*/
var isLastComplete: Boolean=false
fun setCenterChangeX(changeX:Float) {
firststep += changeX
if(!isLastComplete) {
invalidate()
}
}
/**
* 手動(dòng)輸入時(shí),設(shè)置當(dāng)前刻度值
*
* @param rule
*/
fun setNowRule(rule:Int ) {
firststep = (width / 2 - (width / viewAll * rule)).toFloat()
invalidate()
}
override fun onDraw(canvas: Canvas) {
super.onDraw(canvas)
startYDraw = ((height / 2 - 20).toFloat())
path.moveTo((width / 2).toFloat(), (startYDraw))
path.lineTo(((width / 2) + 10).toFloat(), (startYDraw) - 30)
path.lineTo(((width / 2) - 10).toFloat(), (startYDraw) - 30)
path.lineTo((width / 2).toFloat(), (startYDraw))
canvas.drawPath(path, mPaint)
//當(dāng)起始位置超過(guò)中點(diǎn)時(shí),則切換為中點(diǎn),這樣起始位置就不會(huì)跨過(guò)中點(diǎn)
if (firststep > width / 2) {
firststep = (width / 2).toFloat()
}
//-((width/viewAll)*(ruleSize-1)-(width/2))的計(jì)算結(jié)果為終點(diǎn)滑到中點(diǎn)時(shí),起始位置的結(jié)果,
// 控制器不要超過(guò)此點(diǎn),能保證終點(diǎn)不劃過(guò)中點(diǎn)
if (firststep <= -((width / viewAll) * (ruleSize - 1) - (width / 2))) {
firststep = (-((width / viewAll) * (ruleSize - 1) - (width / 2))).toFloat()
}
cachStep = firststep
for ( i in 0..ruleSize) {
Log.e("測(cè)試cacheStep",cachStep.toString())
if(cachStep>width){
break
}
if(cachStep>=0 || cachStep<=width) {
if ((i % 5) == 0) {
canvas.drawLine(cachStep, startYDraw, cachStep, (startYDraw) + 80, mPaint)
canvas.drawText(i.toString(), cachStep, (startYDraw) + 120, mPaint)
} else {
canvas.drawLine(cachStep, startYDraw, cachStep, (startYDraw) + 55, mPaint)
}
//當(dāng)前刻度在中點(diǎn)
if (cachStep <= (width / 2) && (cachStep + (width / viewAll)) >= (width / 2)) {
numListener.getnum(i)
//滾動(dòng)完成
if (isScrowComplent) {
if (cachStep + (width / viewAll / 2) >= (width / 2)) {
setCenterChangeX((width / 2) - cachStep)
} else {
setCenterChangeX((width / 2) - (width / viewAll) - cachStep)
}
isLastComplete=true
}
}
}
cachStep += (width / viewAll)
}
}
public fun setNumListener(numListener : NumListener ) {
this.numListener = numListener
}
public interface NumListener {
fun getnum(num:Int)
}
}
RulerViewGroup:
package com.test.demotestobject.view
import android.content.Context
import android.util.AttributeSet
import android.util.Log
import android.view.MotionEvent
import android.view.VelocityTracker
import android.widget.RelativeLayout
open class RulerViewGroup : RelativeLayout, Runnable {
private var mVelocityTracker: VelocityTracker? = null
//手指離開時(shí)的滾動(dòng)速度
private var velocityX = 0f
//當(dāng)前的觸摸點(diǎn)的X值
private var nowtouchX = 0f
//加速度
private val speedAdd = 2
//單位時(shí)間
private val unitTime = 5
//手指滑動(dòng)方向
private var isLeft = false
constructor(context: Context?) : super(context) {
intView()
}
constructor(context: Context?, attrs: AttributeSet?) : super(context, attrs) {
intView()
}
constructor(context: Context?, attrs: AttributeSet?, defStyleAttr: Int) : super(
context,
attrs,
defStyleAttr
) {
intView()
}
private fun intView() {}
override fun onTouchEvent(event: MotionEvent): Boolean {
if (mVelocityTracker == null) {
mVelocityTracker = VelocityTracker.obtain()
}
var eventAddedToVelocityTracker = false
val vtev = MotionEvent.obtain(event)
when (event.action) {
MotionEvent.ACTION_DOWN -> nowtouchX = event.x
MotionEvent.ACTION_MOVE -> {
(getChildAt(0) as RulerView).setScrollComplete(false)
try {
(getChildAt(0) as RulerView).setChangeX(event.x - nowtouchX)
Log.e("ChangeX", (event.x - nowtouchX).toString() + "")
nowtouchX = event.x
} catch (e: Exception) {
Log.e("RuleViewGroup", "布局錯(cuò)誤")
}
//計(jì)算實(shí)時(shí)滑動(dòng)速度
mVelocityTracker!!.addMovement(vtev)
eventAddedToVelocityTracker = true
mVelocityTracker!!.computeCurrentVelocity(unitTime, 1000f)
velocityX = mVelocityTracker!!.getXVelocity(event.getPointerId(0))
//用于run方法判斷加速度的正負(fù)
isLeft = velocityX < 0
}
MotionEvent.ACTION_UP -> //啟動(dòng)慣性滾動(dòng)
postDelayed(this, unitTime.toLong())
MotionEvent.ACTION_CANCEL -> releaseVelocityTracker()
else -> {
}
}
if (!eventAddedToVelocityTracker) {
mVelocityTracker!!.addMovement(vtev)
}
vtev.recycle()
return true
}
override fun run() {
if (isLeft) {
velocityX += speedAdd
if (velocityX >= 0) {
velocityX = 0f
}
} else {
velocityX -= speedAdd
if (velocityX <= 0) {
velocityX = 0f
}
}
try {
(getChildAt(0) as RulerView).setChangeX(velocityX)
} catch (e: Exception) {
Log.e("RuleViewGroup", "布局錯(cuò)誤")
}
if (velocityX != 0f) {
postDelayed(this, unitTime.toLong())
} else {
try {
(getChildAt(0) as RulerView).setScrollComplete(true)
} catch (e: Exception) {
Log.e("RuleViewGroup", "布局錯(cuò)誤")
}
}
}
//釋放VelocityTracker
private fun releaseVelocityTracker() {
if (null != mVelocityTracker) {
mVelocityTracker!!.clear()
mVelocityTracker!!.recycle()
mVelocityTracker = null
}
}
}
下面是布局文件:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<EditText
android:id="@+id/num"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:hint="請(qǐng)輸入刻度值"
android:inputType="number"
android:text=""
android:textSize="20dp" />
<TextView
android:id="@+id/setnum"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:inputType="number"
android:text=""
android:textSize="30dp" />
<Button
android:id="@+id/submit"
android:layout_width="100dp"
android:layout_height="wrap_content"
android:text="提交" />
</LinearLayout>
<com.example.myapplication.touch.RulerViewGroup
android:layout_width="match_parent"
android:layout_height="200dp">
<com.example.myapplication.touch.RulerView
android:id="@+id/ruler"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_centerInParent="true" />
</com.example.myapplication.touch.RulerViewGroup>
</LinearLayout>
以上代碼放到項(xiàng)目里可以直接用

image.png