效果图如下
根据相对于图片本身的坐标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()
paint.color = Color.WHITE
paint.strokeWidth = 5f
paint.isAntiAlias = true
paint.isDither = true
paint.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 ...
|