layout: post
title: '如何实现一个可拖拽的H线'
subtitle: '转载请注明出处'
date: 2019-08-13
categories: Android View 自定义View
cover: 'http://bpic.588ku.com/back_pic/05/61/11/465b46e23671e61.jpg'
tags: Android View
untitled.gif
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 HLine 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;
private double angle;
private double distance;
private double angle_p0p3;
private double angle_p3p0;
private double distance_p0p3;
public HLine(Context context) {
super(context);
}
public HLine(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
public HLine(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
mDensity = getResources().getDisplayMetrics().density;
initPaints();
}
private void initPaints() {
mLinePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mLinePaint.setColor(mLineColor);
mLinePaint.setStrokeWidth(mLineWidth);
mLinePaint.setStyle(Paint.Style.STROKE);
}
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
super.onLayout(changed, left, top, right, bottom);
initPoints();
}
private void initPoints() {
PointF[] points = new PointF[6];
points[0] = new PointF(getWidth() / 5 * 2, getHeight() / 6 * 1);
points[1] = new PointF(getWidth() / 5 * 2, getHeight() / 6 * 3);
points[2] = new PointF(getWidth() / 5 * 2, getHeight() / 6 * 5);
points[3] = new PointF(getWidth() / 5 * 3, getHeight() / 6 * 1);
points[4] = new PointF(getWidth() / 5 * 3, getHeight() / 6 * 3);
points[5] = new PointF(getWidth() / 5 * 3, getHeight() / 6 * 5);
this.points = points;
distance_p0p3 = MathUtils.getDistance(points[0], points[3]);
angle_p0p3 = MathUtils.getAngleFromPoint(points[0], points[3]);
angle_p3p0 = MathUtils.getAngleFromPoint(points[3], points[0]);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.drawColor(Color.BLACK);
//绘制线
onDrawLines(canvas);
//绘制锚点
onDrawPoints(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) {
handle = false;
}
break;
case MotionEvent.ACTION_MOVE:
toImagePointSize(mDraggingPoint, event);
break;
case MotionEvent.ACTION_UP:
mDraggingPoint = null;
break;
}
invalidate();
return handle || super.onTouchEvent(event);
}
private static final String TAG = "HPlanningLine";
private void toImagePointSize(PointF dragPoint, MotionEvent event) {
if (dragPoint == null) {
return;
}
DragPointType pointType = getPointType(dragPoint);
float x = event.getX();
float y = event.getY() <= 0 ? 0 : event.getY();
if (pointType != null) {
switch (pointType) {
case LEFT_TOP:
if (!canMoveLeftTop(x, y)) return;
dragPoint.y = y;
dragPoint.x = x;
angle = MathUtils.getAngleFromPoint(points[2], points[0]);
distance = MathUtils.getDistance(points[1], points[2]);
points[1].x = (float) (points[2].x + distance * Math.cos(Math.toRadians(angle)));
points[1].y = (float) (points[2].y + distance * Math.sin(Math.toRadians(angle)));
angle = MathUtils.getAngleFromPoint(points[2], points[0]) - 270;
points[3].x = (float) (points[0].x + distance_p0p3 * Math.cos(Math.toRadians(angle_p0p3 + angle)));
points[3].y = (float) (points[0].y + distance_p0p3 * Math.sin(Math.toRadians(angle_p0p3 + angle)));
points[4].x = (float) (points[1].x + distance_p0p3 * Math.cos(Math.toRadians(angle_p0p3 + angle)));
points[4].y = (float) (points[1].y + distance_p0p3 * Math.sin(Math.toRadians(angle_p0p3 + angle)));
points[5].x = (float) (points[2].x + distance_p0p3 * Math.cos(Math.toRadians(angle_p0p3 + angle)));
points[5].y = (float) (points[2].y + distance_p0p3 * Math.sin(Math.toRadians(angle_p0p3 + angle)));
break;
case LEFT_MID:
dragPoint.y = y;
dragPoint.x = x;
angle = MathUtils.getAngleFromPoint(points[5], points[3]) - 270;
distance_p0p3 = MathUtils.getDistance(points[1], points[4]);
points[0].x = (float) (points[3].x + distance_p0p3 * Math.cos(Math.toRadians(angle_p3p0 + angle)));
points[0].y = (float) (points[3].y + distance_p0p3 * Math.sin(Math.toRadians(angle_p3p0 + angle)));
points[2].x = (float) (points[5].x + distance_p0p3 * Math.cos(Math.toRadians(angle_p3p0 + angle)));
points[2].y = (float) (points[5].y + distance_p0p3 * Math.sin(Math.toRadians(angle_p3p0 + angle)));
angle = MathUtils.getAngleFromPoint(points[0], points[2]);
distance = MathUtils.getDistance(points[0], points[1]);
points[1].x = (float) (points[0].x + distance * Math.cos(Math.toRadians(angle)));
points[1].y = (float) (points[0].y + distance * Math.sin(Math.toRadians(angle)));
points[4].x = (float) (points[3].x + distance * Math.cos(Math.toRadians(angle)));
points[4].y = (float) (points[3].y + distance * Math.sin(Math.toRadians(angle)));
break;
case LEFT_BOTTOM:
if (!canMoveLeftBottom(x, y)) return;
dragPoint.y = y;
dragPoint.x = x;
angle = MathUtils.getAngleFromPoint(points[0], points[2]);
distance = MathUtils.getDistance(points[1], points[0]);
points[1].x = (float) (points[0].x + distance * Math.cos(Math.toRadians(angle)));
points[1].y = (float) (points[0].y + distance * Math.sin(Math.toRadians(angle)));
angle = MathUtils.getAngleFromPoint(points[2], points[0]) - 270;
points[3].x = (float) (points[0].x + distance_p0p3 * Math.cos(Math.toRadians(angle_p0p3 + angle)));
points[3].y = (float) (points[0].y + distance_p0p3 * Math.sin(Math.toRadians(angle_p0p3 + angle)));
points[4].x = (float) (points[1].x + distance_p0p3 * Math.cos(Math.toRadians(angle_p0p3 + angle)));
points[4].y = (float) (points[1].y + distance_p0p3 * Math.sin(Math.toRadians(angle_p0p3 + angle)));
points[5].x = (float) (points[2].x + distance_p0p3 * Math.cos(Math.toRadians(angle_p0p3 + angle)));
points[5].y = (float) (points[2].y + distance_p0p3 * Math.sin(Math.toRadians(angle_p0p3 + angle)));
break;
case RIGHT_TOP:
if (!canMoveRightTop(x, y)) return;
dragPoint.y = y;
dragPoint.x = x;
angle = MathUtils.getAngleFromPoint(points[5], points[3]);
distance = MathUtils.getDistance(points[4], points[5]);
points[4].x = (float) (points[5].x + distance * Math.cos(Math.toRadians(angle)));
points[4].y = (float) (points[5].y + distance * Math.sin(Math.toRadians(angle)));
angle = MathUtils.getAngleFromPoint(points[5], points[3]) - 270;
points[0].x = (float) (points[3].x + distance_p0p3 * Math.cos(Math.toRadians(angle_p3p0 + angle)));
points[0].y = (float) (points[3].y + distance_p0p3 * Math.sin(Math.toRadians(angle_p3p0 + angle)));
points[1].x = (float) (points[4].x + distance_p0p3 * Math.cos(Math.toRadians(angle_p3p0 + angle)));
points[1].y = (float) (points[4].y + distance_p0p3 * Math.sin(Math.toRadians(angle_p3p0 + angle)));
points[2].x = (float) (points[5].x + distance_p0p3 * Math.cos(Math.toRadians(angle_p3p0 + angle)));
points[2].y = (float) (points[5].y + distance_p0p3 * Math.sin(Math.toRadians(angle_p3p0 + angle)));
break;
case RIGHT_MID:
dragPoint.y = y;
dragPoint.x = x;
angle = MathUtils.getAngleFromPoint(points[2], points[0]) - 270;
distance_p0p3 = MathUtils.getDistance(points[1], points[4]);
points[3].x = (float) (points[0].x + distance_p0p3 * Math.cos(Math.toRadians(angle_p0p3 + angle)));
points[3].y = (float) (points[0].y + distance_p0p3 * Math.sin(Math.toRadians(angle_p0p3 + angle)));
points[5].x = (float) (points[2].x + distance_p0p3 * Math.cos(Math.toRadians(angle_p0p3 + angle)));
points[5].y = (float) (points[2].y + distance_p0p3 * Math.sin(Math.toRadians(angle_p0p3 + angle)));
angle = MathUtils.getAngleFromPoint(points[3], points[5]);
distance = MathUtils.getDistance(points[3], points[4]);
points[1].x = (float) (points[0].x + distance * Math.cos(Math.toRadians(angle)));
points[1].y = (float) (points[0].y + distance * Math.sin(Math.toRadians(angle)));
points[4].x = (float) (points[3].x + distance * Math.cos(Math.toRadians(angle)));
points[4].y = (float) (points[3].y + distance * Math.sin(Math.toRadians(angle)));
break;
case RIGHT_BOTTOM:
if (!canMoveRightBottom(x, y)) return;
dragPoint.y = y;
dragPoint.x = x;
angle = MathUtils.getAngleFromPoint(points[3], points[5]);
distance = MathUtils.getDistance(points[4], points[3]);
points[4].x = (float) (points[3].x + distance * Math.cos(Math.toRadians(angle)));
points[4].y = (float) (points[3].y + distance * Math.sin(Math.toRadians(angle)));
angle = MathUtils.getAngleFromPoint(points[3], points[5]) - 270;
points[0].x = (float) (points[3].x + distance_p0p3 * Math.cos(Math.toRadians(angle_p0p3 + angle)));
points[0].y = (float) (points[3].y + distance_p0p3 * Math.sin(Math.toRadians(angle_p0p3 + angle)));
points[1].x = (float) (points[4].x + distance_p0p3 * Math.cos(Math.toRadians(angle_p0p3 + angle)));
points[1].y = (float) (points[4].y + distance_p0p3 * Math.sin(Math.toRadians(angle_p0p3 + angle)));
points[2].x = (float) (points[5].x + distance_p0p3 * Math.cos(Math.toRadians(angle_p0p3 + angle)));
points[2].y = (float) (points[5].y + distance_p0p3 * Math.sin(Math.toRadians(angle_p0p3 + angle)));
break;
default:
break;
}
}
}
private boolean canMoveLeftTop(float x, float y) {
if (MathUtils.getDistance(x, y, points[2].x, points[2].y) - MathUtils.getDistance(points[1], points[2]) - 2 * dp2px(POINT_RADIUS) <= 0) {
return false;
}
return true;
}
private boolean canMoveLeftBottom(float x, float y) {
if (MathUtils.getDistance(x, y, points[0].x, points[0].y) - MathUtils.getDistance(points[1], points[0]) - 2 * dp2px(POINT_RADIUS) <= 0) {
return false;
}
return true;
}
private boolean canMoveRightTop(float x, float y) {
if (MathUtils.getDistance(x, y, points[5].x, points[5].y) - MathUtils.getDistance(points[4], points[5]) - 2 * dp2px(POINT_RADIUS) <= 0) {
return false;
}
return true;
}
private boolean canMoveRightBottom(float x, float y) {
if (MathUtils.getDistance(x, y, points[3].x, points[3].y) - MathUtils.getDistance(points[4], points[3]) - 2 * dp2px(POINT_RADIUS) <= 0) {
return false;
}
return true;
}
private DragPointType getPointType(PointF dragPoint) {
if (dragPoint == null) return null;
DragPointType type;
if (checkPoints(points)) {
for (int i = 0; i < points.length; i++) {
if (dragPoint == points[i]) {
type = 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 (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 (PointF point : points) {
canvas.drawCircle(point.x, point.y, dp2px(POINT_RADIUS), mLinePaint);
}
}
public boolean checkPoints(PointF[] points) {
return points != null && points.length == 6
&& points[0] != null && points[1] != null && points[2] != null && points[3] != null && points[4] != null && points[5] != null;
}
private Path resetPointPath() {
if (!checkPoints(points)) {
return null;
}
mPointLinePath.reset();
PointF p0 = points[0];
PointF p1 = points[1];
PointF p2 = points[2];
PointF p3 = points[3];
PointF p4 = points[4];
PointF p5 = points[5];
mPointLinePath.moveTo(p0.x, p0.y);
mPointLinePath.lineTo(p2.x, p2.y);
mPointLinePath.moveTo(p3.x, p3.y);
mPointLinePath.lineTo(p5.x, p5.y);
mPointLinePath.moveTo(p1.x, p1.y);
mPointLinePath.lineTo(p4.x, p4.y);
return mPointLinePath;
}
private float dp2px(float dp) {
return dp * mDensity;
}
enum DragPointType {
LEFT_TOP,
LEFT_MID,
LEFT_BOTTOM,
RIGHT_TOP,
RIGHT_MID,
RIGHT_BOTTOM,
}
}
网友评论