0.

Recently I occasionally buy a two-color ball, a week to buy a 6 yuan, also not much, is to gather together a lively, for life to add a small interest. As a conspiracy theorist, I felt that machine selection might not be “random” enough, so I decided to write my own program to select my number. For this reason, I wrote a control modeled after a slot machine. The above first

1.

Let’s design it

1.1 Support to get data from numbers and arrays

1.2 Draw two numbers, the current number and the next number

1.3 Set cheap quantity and realize acceleration

1.4 Start and Stop, add callback when stop

1.5 Supports text size and color

2.

Cut the crap and start coding

package com.skateboard.numberrunningview import android.content.Context import android.graphics.Canvas import android.graphics.Color import android.graphics.Paint import android.support.v4.view.ViewCompat import android.util.AttributeSet import android.view.View class NumberRunningView(context: Context, attributes: AttributeSet?) : View(context, Attributes) {private var numberColor = color. WHITE private var numberSize = 15.0f // Start the number or array subscript var min = 0 // The maximum number or array subscript var Max = 0 // The current number or array subscript private var now = min // The distance to be moved per refresh private var offset = 0 private var paint = Paint(Paint.ANTI_ALIAS_FLAG) private var isStart =falseVar speed = 10f private var curSpeed = 0f private var speedOffset = 0.1f var dataList: List<Int>? = nullset(value) { field = value min = 0 max = (value? .size ? : 1) - 1 now = min offset = 0 } var onNumberSelectedListener: OnNumberSelectedListenern? = null init {if(attributes ! = null) { parseAttrs(attributes) } initPaint() } constructor(context: Context) : this(context, null) private fun parseAttrs(attributes: AttributeSet) { val typedArray = context.obtainStyledAttributes(attributes, R.styleable.NumberRunningView) min = typedArray.getInt(R.styleable.NumberRunningView_min, min) max = typedArray.getInt(R.styleable.NumberRunningView_max, max) maxSpeed = typedArray.getFloat(R.styleable.NumberRunningView_maxSpeed, maxSpeed) numberColor = typedArray.getColor(R.styleable.NumberRunningView_numberColor, numberColor) numberSize = typedArray.getDimension(R.styleable.NumberRunningView_numberSize, numberSize) speedOffset = typedArray.getFloat(R.styleable.NumberRunningView_speedOffset, 0.1f) TypeDarray.recycle () now = min} private funinitPaint() { paint.textSize = numberSize paint.color = numberColor } override fun onDraw(canvas: Canvas?) { super.onDraw(canvas) canvas? .let { drawNow(it) drawNext(it) calCurSpeed() calOffset() } } private funcalCurSpeed() {
        curSpeed += speedOffset
        if (curSpeed > maxSpeed) curSpeed = maxSpeed
    }

    private fun drawNow(canvas: Canvas) {
        val curDataList = dataList
        var nowNum = "0"
        nowNum = if(curDataList ! = null) { curDataList[now].toString() }else {
            now.toString()
        }
        val numWidth = paint.measureText(nowNum)
        canvas.drawText(nowNum, width / 2 - numWidth / 2, height / 2 - offset + paint.textSize / 2, paint)
    }


    private fun drawNext(canvas: Canvas) {
        val curDataList = dataList
        var nextNum = ""
        if (curDataList == null) {
            nextNum = if (now + 1 > max) {
                min.toString()
            } else {
                (now + 1).toString()
            }

        } else {
            nextNum = if (now + 1 > max) {
                curDataList[min].toString()
            } else{ (curDataList[now + 1]).toString() } } val numWidth = paint.measureText(nextNum) canvas.drawText(nextNum, Width / 2 - numWidth / 2, 1.5f * height-offset + paintcalOffset() {
        if (isStart) {
            if (offset == height) {
                offset = 0
                if (now + 1 > max) {
                    now = min
                } else {
                    now += 1
                }
            } else if (offset + curSpeed > height) {
                offset = height
            } else {
                offset = (offset + curSpeed).toInt()
            }
            postInvalidate()
        } else {
            if(offset ! = 0 && offset ! = height) { offset =if (offset + curSpeed > height) {
                    height
                } else {
                    (offset + curSpeed).toInt()
                }
                postInvalidate()
            } else {
                if (offset == 0) {
                    val curDataList = dataList
                    if(curDataList ! = null) { onNumberSelectedListener? .onNumberSelected(curDataList[now]) }else{ onNumberSelectedListener? .onNumberSelected(now) } }else {
                    val curDataList = dataList
                    if(curDataList ! = null) { onNumberSelectedListener? .onNumberSelected(if (now == max) curDataList[min] else curDataList[now + 1])
                    } else{ onNumberSelectedListener? .onNumberSelected(if (now == max) min else now + 1)
                    }
                }
            }
        }
    }

    fun start() {
        if (isStart) {
            return
        }
        curSpeed = 0f
        isStart = true
        if (ViewCompat.isAttachedToWindow(this)) {
            postInvalidate()
        }
    }

    fun stop() {
        isStart = false
    }
   
    interface OnNumberSelectedListenern {

        fun onNumberSelected(num: Int)
    }
}
Copy the code

The code is pretty simple, it’s not that difficult, so let’s look at the onDraw method, okay

override fun onDraw(canvas: Canvas?) { super.onDraw(canvas) canvas? .let { drawNow(it) drawNext(it) calCurSpeed() calOffset() } }Copy the code

Draw the current number, draw the next number, calculate the current speed, calculate the offset, and this speed is mainly used to look like there is an acceleration in the reversal of the number at the beginning. Look at the drawNow method

 private fun drawNow(canvas: Canvas) {
        val curDataList = dataList
        var nowNum = "0"
        nowNum = if(curDataList ! = null) { curDataList[now].toString() }else {
            now.toString()
        }
        val numWidth = paint.measureText(nowNum)
        canvas.drawText(nowNum, width / 2 - numWidth / 2, height / 2 - offset + paint.textSize / 2, paint)
    }
Copy the code

First, obtain the data according to different data sources, then calculate the position of text drawing, and then draw drawNext, which is basically a judgment problem of the next number calOffset method

 private fun calOffset() {
        if (isStart) {
            if (offset == height) {
                offset = 0
                if (now + 1 > max) {
                    now = min
                } else {
                    now += 1
                }
            } else if (offset + curSpeed > height) {
                offset = height
            } else {
                offset = (offset + curSpeed).toInt()
            }
            postInvalidate()
        } else {
            if(offset ! = 0 && offset ! = height) { offset =if (offset + curSpeed > height) {
                    height
                } else {
                    (offset + curSpeed).toInt()
                }
                postInvalidate()
            } else {
                if (offset == 0) {
                    val curDataList = dataList
                    if(curDataList ! = null) { onNumberSelectedListener? .onNumberSelected(curDataList[now]) }else{ onNumberSelectedListener? .onNumberSelected(now) } }else {
                    val curDataList = dataList
                    if(curDataList ! = null) { onNumberSelectedListener? .onNumberSelected(if (now == max) curDataList[min] else curDataList[now + 1])
                    } else{ onNumberSelectedListener? .onNumberSelected(if (now == max) min else now + 1)
                    }
                }
            }
        }
    }
Copy the code

If it is running, determine the current offset of the control, reset to 0 if it is equal to the height of the control, then set now to the next number, otherwise add speed to recalculate the offset. If it is not running, determine the current offset, and if it is neither zero nor control height, it is in an intermediate state. Move it to the next position and call the callback function. There is no other key, you need to look at the code, okay

3.

github

Follow my official account