如何实现一个可拖拽的H线

2019-08-13  本文已影响0人  galaxy_zheng

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,

    }
}

涉及到的工具类MathUtils

上一篇下一篇

猜你喜欢

热点阅读