layout: post
title: '如何实现一个可拖拽的圆并且可以改变大小'
subtitle: '转载请注明出处'
date: 2019-08-12
categories: Android View 自定义View
cover: 'http://bpic.588ku.com/back_pic/05/61/11/465b46e23671e61.jpg'
tags: Android View
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.PointF;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import androidx.annotation.Nullable;
public class DragCircle extends View {
private static final float POINT_RADIUS = 10; // dp,锚点绘制半价
private static final float TOUCH_POINT_CATCH_DISTANCE = 15; //dp,触摸点捕捉到锚点的最小距离
private static final int DEFAULT_LINE_COLOR = Color.parseColor("#F33737");
private float mDensity;
private PointF[] points; //点
float mLineWidth = 1.0f; // 选区线的宽度
int mLineColor = DEFAULT_LINE_COLOR; // 选区线的颜色
private Path mPointLinePath = new Path();
private Paint mLinePaint;
private PointF mDraggingPoint = null;
public DragCircle(Context context) {
super(context);
}
public DragCircle(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
mDensity = getResources().getDisplayMetrics().density;
initPaints();
initPoints();
}
public DragCircle(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
private void initPaints() {
mLinePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mLinePaint.setColor(mLineColor);
mLinePaint.setStrokeWidth(mLineWidth);
mLinePaint.setStyle(Paint.Style.STROKE);
}
private void initPoints() {
PointF[] points = new PointF[3];
points[0] = new PointF(-50, -50);
points[1] = new PointF(-50, -50);
points[2] = new PointF(-50, -50);
this.points = points;
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
//绘制线
onDrawLines(canvas);
//绘制锚点
onDrawPoints(canvas);
//绘制圆
onDrawCircle(canvas);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
int action = event.getAction();
boolean handle = true;
switch (action) {
case MotionEvent.ACTION_DOWN:
mDraggingPoint = getNearbyPoint(event);
if (mDraggingPoint == null) {
points[0].x = event.getX();
points[0].y = event.getY();
points[1].x = event.getX();
points[1].y = event.getY();
mDraggingPoint = getNearbyPoint(event);
handle = true; //返回true 触发手势
}
break;
case MotionEvent.ACTION_MOVE:
toImagePointSize(mDraggingPoint, event);
break;
case MotionEvent.ACTION_UP:
mDraggingPoint = null;
break;
}
invalidate();
return handle || super.onTouchEvent(event);
}
private void toImagePointSize(PointF dragPoint, MotionEvent event) {
if (dragPoint == null) {
return;
}
PlottingScale.DragPointType pointType = getPointType(dragPoint);
int x = (int) event.getX();
int y = (int) event.getY();
if (pointType != null) {
switch (pointType) {
case LEFT:
dragPoint.y = y;
dragPoint.x = x;
points[2].x = (points[0].x + points[1].x) / 2;
points[2].y = (points[0].y + points[1].y) / 2;
break;
case RIGHT:
dragPoint.y = y;
dragPoint.x = x;
points[2].x = (points[0].x + points[1].x) / 2;
points[2].y = (points[0].y + points[1].y) / 2;
break;
case CENTER:
points[0].x = points[0].x + (x - dragPoint.x);
points[0].y = points[0].y + (y - dragPoint.y);
points[1].x = points[1].x + (x - dragPoint.x);
points[1].y = points[1].y + (y - dragPoint.y);
dragPoint.y = y;
dragPoint.x = x;
break;
default:
break;
}
}
}
private PlottingScale.DragPointType getPointType(PointF dragPoint) {
if (dragPoint == null) return null;
PlottingScale.DragPointType type;
if (checkPoints(points)) {
for (int i = 0; i < points.length; i++) {
if (dragPoint == points[i]) {
type = PlottingScale.DragPointType.values()[i];
return type;
}
}
}
return null;
}
private PointF getNearbyPoint(MotionEvent event) {
if (checkPoints(points)) {
for (PointF p : points) {
if (isTouchPoint(p, event)) return p;
}
}
return null;
}
private boolean isTouchPoint(PointF p, MotionEvent event) {
float x = event.getX();
float y = event.getY();
float px = p.x;
float py = p.y;
double distance = Math.sqrt(Math.pow(x - px, 2) + Math.pow(y - py, 2));
if (p == points[2]) {
if (distance < (MathUtils.getDistance(points[0], points[1]) / 2.0)) {
return true;
}
}
if (distance < dp2px(TOUCH_POINT_CATCH_DISTANCE)) {
return true;
}
return false;
}
protected void onDrawLines(Canvas canvas) {
Path path = resetPointPath();
if (path != null) {
canvas.drawPath(path, mLinePaint);
}
}
protected void onDrawPoints(Canvas canvas) {
if (!checkPoints(points)) {
return;
}
for (int i = 0; i < points.length; i++) {
PointF point = points[i];
if (i != points.length - 1) {
canvas.drawCircle(point.x, point.y, dp2px(POINT_RADIUS), mLinePaint);
}
}
// for (PointF point : points) {
// canvas.drawCircle(point.x, point.y, dp2px(POINT_RADIUS), mLinePaint);
// }
}
protected void onDrawCircle(Canvas canvas) {
if (!checkPoints(points)) {
return;
}
float radus = (float) (MathUtils.getDistance(points[0], points[1]) / 2.0);
canvas.drawCircle(points[2].x, points[2].y, radus, mLinePaint);
}
public boolean checkPoints(PointF[] points) {
return points != null && points.length == 3
&& points[0] != null && points[1] != null && points[2] != null;
}
private Path resetPointPath() {
if (!checkPoints(points)) {
return null;
}
mPointLinePath.reset();
PointF p0 = points[0];
PointF p1 = points[1];
PointF p2 = points[2];
mPointLinePath.moveTo(p0.x, p0.y);
mPointLinePath.lineTo(p1.x, p1.y);
mPointLinePath.moveTo(p2.x, p2.y);
return mPointLinePath;
}
private float dp2px(float dp) {
return dp * mDensity;
}
enum DragPointType {
LEFT,
RIGHT,
CENTER
}
}
涉及到的工具类MathUtils