Android动态画线 坐标画线动画

效果图如下

根据相对于图片本身的坐标xy数组 基于view的左上角为原点 在图片上动态画线 

//参考数据类型

//pointList
[PointEntity(pointX=1, pointY=1), PointEntity(pointX=2, pointY=2), PointEntity(pointX=3, pointY=3)]

//lineList
[LinePointEntity(startX=1, startY=1, endX=2, endY=2), LinePointEntity(startX=2, startY=2, endX=3, endY=3)]
//协程
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.5"
import android.content.Context
import android.graphics.Canvas
import android.graphics.Paint
import android.util.AttributeSet
import android.util.Log
import android.widget.FrameLayout
import kotlinx.coroutines.*
import java.util.concurrent.CopyOnWriteArrayList
import kotlin.math.absoluteValue


class DrawLineView : FrameLayout {

    private val TAG = "DrawLineView"

    private var mWidth = 0
    private var mHeight = 0

    private var drawJob: Job? = null

    private var threadWorking = false

    private var mPaint: Paint? = null

    //线的端点
    private val linePointList: MutableList<LinePointEntity> = ArrayList()

    //线的点
    private var drawPointList: CopyOnWriteArrayList<PointEntity> = CopyOnWriteArrayList()

    constructor(context: Context) : super(context)

    constructor(context: Context, attributeSet: AttributeSet) : super(context, attributeSet)

    constructor(context: Context, attributeSet: AttributeSet, defStyleAttr: Int) : super(
        context,
        attributeSet,
        defStyleAttr
    )

    override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec)
        setMeasuredDimension(mWidth, mHeight)
    }

    override fun onDraw(canvas: Canvas?) {
        super.onDraw(canvas)
        if (drawJob == null && linePointList.isNotEmpty()) {
            startDrawLine()
        } else {
            drawPoint(canvas)
        }
    }

    fun setBackgroundImage(res: Int): DrawLineView {
        setBackgroundResource(res)
        return this
    }

    fun setPaint(paint: Paint): DrawLineView {
        this.mPaint = paint
        return this
    }

    fun updateView(width: Int, height: Int): DrawLineView {
        this.mWidth = width
        this.mHeight = height
        return this
    }

    fun drawLine(list: List<LinePointEntity>) {
        drawJob?.cancel()
        drawJob = null
        linePointList.clear()
        linePointList.addAll(list)
        invalidate()
    }

    private fun drawPoint(canvas: Canvas?) {
        for (entity in drawPointList) {
            mPaint?.let {
                canvas?.drawPoint(entity.pointX, entity.pointY, it)
            }
        }
    }

    //添加需要画的点
    private fun addPoint(pointX: Float, pointY: Float) {
        drawPointList.add(PointEntity(pointX, pointY))
    }

    private fun startDrawLine() {
        Log.d(TAG, "startDrawLine...")
        drawJob = GlobalScope.launch {
            for (entity in linePointList) {

                entity.apply {
                    //两点之间的距离
                    val distanceX = (endX - startX).absoluteValue
                    val distanceY = (endY - startY).absoluteValue
                    //每个单位偏移量
                    var offsetX = 0f
                    var offsetY = 0f
                    //避免在横向或竖向描点过快 通过距离大小使用不同方向为基本1个单位的偏移量
                    if (distanceX > distanceY) {
                        offsetX = if (startX < endX) {
                            1f
                        } else {
                            -1f
                        }
                        offsetY = if (startY < endY) {
                            1f * distanceY / distanceX * offsetX.absoluteValue
                        } else {
                            -1f * distanceY / distanceX * offsetX.absoluteValue
                        }
                    } else {
                        offsetY = if (startY < endY) {
                            1f
                        } else {
                            -1f
                        }
                        offsetX = if (startX < endX) {
                            1f * distanceX / distanceY * offsetY.absoluteValue
                        } else {
                            -1f * distanceX / distanceY * offsetY.absoluteValue
                        }
                    }
                    var currentX = 1f * startX
                    var currentY = 1f * startY
                    while (true) {
                        if (
                            isActive && if (distanceX > distanceY) {
                                if (offsetX > 0) {
                                    currentX < endX
                                } else {
                                    currentX > endX
                                }
                            } else {
                                if (offsetY > 0) {
                                    currentY < endY
                                } else {
                                    currentY > endY
                                }
                            }
                        ) {

                            currentX += offsetX
                            currentY += offsetY
                            addPoint(currentX, currentY)
                            delay(1)
                            invalidate()
                        } else {
                            Log.w(TAG, "end draw ----> break")
                            break
                        }
                    }
                }

            }
        }
        drawJob?.start()
    }

    override fun onDetachedFromWindow() {
        super.onDetachedFromWindow()
        Log.d(TAG, "onDetachedFromWindow job cancel...")
        drawJob?.cancel()
    }


}

data class LinePointEntity(
    val startX: Float,
    val startY: Float,
    val endX: Float,
    val endY: Float
)

data class PointEntity(
    val pointX: Float,
    val pointY: Float
)
<?xml version="1.0" encoding="utf-8"?>
<androidx.appcompat.widget.LinearLayoutCompat xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <androidx.appcompat.widget.AppCompatButton
        android:id="@+id/btn_start_draw"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="画线" />

    <com.znan.androidtest.screen.widget.view.DrawLineView
        android:id="@+id/view_draw_line"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" />

</androidx.appcompat.widget.LinearLayoutCompat>
    private fun initDrawLineView() {
        val screenWidth = resources.displayMetrics.widthPixels

        //原图大小
        val imageWidth = 3480
        val imageHeight = 2160

        val viewHeight = 1f * imageHeight / imageWidth * screenWidth

        //坐标在手机屏幕上对应的缩放比例
        val rateX = 1f * screenWidth / imageWidth
        val rateY = 1f * viewHeight / imageHeight

        //随机需要画线的点
        val pointList: MutableList<PointEntity> = ArrayList()
        repeat(8) {
            pointList.add(
                PointEntity(
                    rateX * getRandomNumber(imageWidth),
                    rateY * getRandomNumber(imageHeight)
                )
            )
        }
        //将画线点格式化线数组的端点
        val lineList: MutableList<LinePointEntity> = ArrayList()
        for (i in pointList.indices) {
            if (i < pointList.size - 1) {
                lineList.add(
                    LinePointEntity(
                        pointList[i].pointX,
                        pointList[i].pointY,
                        pointList[i+1].pointX,
                        pointList[i+1].pointY
                    )
                )

            }
        }

        val paint = Paint().apply {
            color = Color.WHITE
            strokeWidth = 5f
            isAntiAlias = true
            isDither = true
            style = Paint.Style.STROKE
        }

        //画线
        view_draw_line.updateView(screenWidth, viewHeight.toInt())
            .setPaint(paint)
            .setBackgroundImage(R.mipmap.bg_map_3m)
            .drawLine(lineList)

    }

 
    fun getRandomNumber(maxNumber: Int = 10000): Int {
        return (0..maxNumber).random()
    }

//以下可忽略

    inner class DrawThread : Thread() {
        override fun run() {
            super.run()
            threadWorking = true
            Log.d(TAG, "DrawThread run...")
            for (entity in linePointList) {
                entity.apply {
                    //两点之间的距离
                    val distanceX = (endX - startX).absoluteValue
                    val distanceY = (endY - startY).absoluteValue
                    //每个单位偏移量
                    var offsetX = 0f
                    var offsetY = 0f
                    //避免在横向或竖向描点过快 通过距离大小使用不同方向为基本1个单位的便宜量
                    if (distanceX > distanceY) {
                        offsetX = if (startX < endX) {
                            1f
                        } else {
                            -1f
                        }
                        offsetY = if (startY < endY) {
                            1f * distanceY / distanceX * offsetX.absoluteValue
                        } else {
                            -1f * distanceY / distanceX * offsetX.absoluteValue
                        }
                    } else {
                        offsetY = if (startY < endY) {
                            1f
                        } else {
                            -1f
                        }
                        offsetX = if (startX < endX) {
                            1f * distanceX / distanceY * offsetY.absoluteValue
                        } else {
                            -1f * distanceX / distanceY * offsetY.absoluteValue
                        }
                    }
                    var currentX = 1f * startX
                    var currentY = 1f * startY

                    while (true) {
                        if (
                            isAlive && threadWorking && if (distanceX > distanceY) {
                                if (offsetX > 0) currentX < endX else currentX > endX
                            } else {
                                if (offsetY > 0) currentY < endY else currentY > endY
                            }
                        ) {
                            currentX += offsetX
                            currentY += offsetY
                            addPoint(currentX, currentY)

                        } else {
                            break
                        }
                    }
                }

            }
        }
    }
    //计算点位的线程
    protected class DrawThread extends Thread {

        @Override
        public void run() {
            super.run();

            Log.d(TAG, "DrawThread run :" + linePointList.size());
            for (LinePointEntity entity : linePointList) {
                //两点之间横向距离
                int distanceX = Math.abs((entity.getEndX() - entity.getStartX()));
                //两点之间竖向距离
                int distanceY = Math.abs(entity.getEndY() - entity.getStartY());

                //避免在横向或竖向描点过快 通过距离大小使用不同方向为基本1个单位的便宜量
                if (distanceX > distanceY) {

                    //横向偏移量
                    int offsetX;
                    //竖向偏移量
                    Float offsetY;
                    if (entity.getStartX() < entity.getEndX()) {
                        offsetX = 1;
                    } else {
                        offsetX = -1;
                    }

                    if (entity.getStartY() < entity.getEndY()) {
                        offsetY = 1f * distanceY / distanceX * Math.abs(offsetX);
                    } else {
                        offsetY = -1f * distanceY / distanceX * Math.abs(offsetX);
                    }

                    //当前绘制位置x y 坐标
                    int currentX = entity.getStartX();
                    Float currentY = 1f * entity.getStartY();

                    while (true) {
                        //偏移量的正负影响结束位置条件判断
                        if (this.isAlive() && threadWorking && offsetX > 0 ? currentX < entity.getEndX() : currentX > entity.getEndX()) {
                            currentX += offsetX;
                            currentY += offsetY;
                            addPoint(currentX, currentY.intValue());
                            try {
                                Thread.sleep(1);
                            } catch (InterruptedException e) {
                                e.printStackTrace();
                                break;
                            }
                            invalidate();
                        } else {
                            break;
                        }
                    }
                } else {

                    //横向偏移量
                    Float offsetX;
                    //竖向偏移量
                    int offsetY;
                    if (entity.getStartY() < entity.getEndY()) {
                        offsetY = 1;
                    } else {
                        offsetY = -1;
                    }

                    if (entity.getStartX() < entity.getEndX()) {
                        offsetX = 1f * distanceX / distanceY * Math.abs(offsetY);
                    } else {
                        offsetX = -1f * distanceX / distanceY * Math.abs(offsetY);
                    }

                    //当前绘制位置x y 坐标
                    Float currentX = 1f * entity.getStartX();
                    int currentY = entity.getStartY();

                    while (true) {
                        //偏移量的正负影响结束位置条件判断
                        if (this.isAlive() && offsetY > 0 ? currentY < entity.getEndY() : currentY > entity.getEndY()) {
                            currentX += offsetX;
                            currentY += offsetY;
                            addPoint(currentX.intValue(), currentY);
                            try {
                                Thread.sleep(1);
                            } catch (InterruptedException e) {
                                e.printStackTrace();
                                break;
                            }
                            invalidate();
                        } else {
                            break;
                        }
                    }
                }

            }
        }
    }

The end ...

  • 0
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值