自定義View實戰九:實現一個可以拖動大小,移動區域的矩形框

目錄

0、相關文章:

1、MoveAndCropRectView.java

2、DrawRect5Activity.java

3、activity_draw_rect5.xml


0、相關文章:

參考文章:

android自定義可縮放,移動圖像裁剪框

Android 實現一個可以拖動大小,移動區域的矩形框

1、MoveAndCropRectView.java

public class MoveAndCropRectView extends View {

    // 繪製 損害框和損害名稱
    private Paint mPaint;
    private RectF mRectF;

    // 邊緣字體
    private BorderedText mBorderedText;

    // 標題 或 名字
    private String mTitle;
    // 概率
    private float mConfidence;

    // 矩形框 corner 的角度:直角、圓角
    private int mCornerAngle;

    //直角 默認
    public static final int RIGHT_CORNER = 0;
    //圓角
    public static final int ROUND_CORNER = 1;

    // Remove Rect
    private int MODE;
    private static final int MODE_OUTSIDE = 0x000000aa;/*170*/
    private static final int MODE_INSIDE = 0x000000bb;/*187*/
    private static final int MODE_POINT = 0X000000cc;/*204*/
    private static final int MODE_ILLEGAL = 0X000000dd;/*221*/

    private float startX;/*start X location*/
    private float startY;/*start Y location*/
    private float endX;/*end X location*/
    private float endY;/*end Y location*/

    private float currentX;/*X coordinate values while finger press*/
    private float currentY;/*Y coordinate values while finger press*/

    private float memoryX;/*the last time the coordinate values of X*/
    private float memoryY;/*the last time the coordinate values of Y*/

    private float mCoverWidth;/*width of selection box*/
    private float mCoverHeight;/*height of selection box*/

    private static final int ACCURACY = 100;/*touch accuracy*/
    private int pointPosition;/*vertex of a rectangle*/

    private static final float minWidth = 100.0f;/*the minimum width of the rectangle*/
    private static final float minHeight = 200.0f;/*the minimum height of the rectangle*/

    private onLocationListener mLocationListener;/*listen to the Rect */

    private static final float EDGE_WIDTH = 1.8f;

    public MoveAndCropRectView(Context context) {
        this(context, null);
    }

    public MoveAndCropRectView(Context context, @Nullable AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public MoveAndCropRectView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        initDatas(context);
    }

    private void initDatas(Context context) {
        mPaint = new Paint();
        mRectF = new RectF();

        //畫筆設置空心
        mPaint.setStyle(Paint.Style.STROKE);
        mPaint.setColor(Color.RED);
        mPaint.setStrokeWidth(5);
        mPaint.setAntiAlias(true);

        float textSizePx = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
                18.0f, context.getResources().getDisplayMetrics());
        mBorderedText = new BorderedText(textSizePx);


        currentX = 0;
        currentY = 0;
    }

    private boolean firstDraw = true;

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);

//        switch (mCornerAngle) {
//            case RIGHT_CORNER:// 繪製 損害框(直角矩形框)
//                drawRect(canvas);
//                break;
//            case ROUND_CORNER:// 繪製 損害框(圓角矩形框)
//                drawRoundRect(canvas);
//                break;
//        }

        if (firstDraw) {
            firstDraw = false;
            startX = mRectF.left;
            startY = mRectF.top;
            endX = mRectF.right;
            endY = mRectF.bottom;

            mCoverWidth = mRectF.width();
            mCoverHeight = mRectF.height();
        }


        if (mLocationListener != null) {
            mLocationListener.locationRect(startX, startY, endX, endY);
        }

//        LogUtils.d("onDraw -- startX: " + startX);

        canvas.drawLine(startX - EDGE_WIDTH, startY - EDGE_WIDTH,
                endX + EDGE_WIDTH, startY - EDGE_WIDTH, mPaint);/*up -*/
        canvas.drawLine(startX - EDGE_WIDTH, endY + EDGE_WIDTH,
                endX + EDGE_WIDTH, endY + EDGE_WIDTH, mPaint);/*down -*/
        canvas.drawLine(startX - EDGE_WIDTH, startY - EDGE_WIDTH,
                startX - EDGE_WIDTH, endY + EDGE_WIDTH, mPaint);/*left |*/
        canvas.drawLine(endX + EDGE_WIDTH, startY - EDGE_WIDTH,
                endX + EDGE_WIDTH, endY + EDGE_WIDTH, mPaint);/*righ |*/

        // 繪製名稱 和 概率
        final String labelString =
                !TextUtils.isEmpty(mTitle)
                        ? String.format("%s %.2f", mTitle, (100 * mConfidence))
                        : String.format("%.2f", (100 * mConfidence));

        // 在 直角矩形框 上寫字
        mBorderedText.drawText(canvas,
                startX,
                startY, labelString + "%",
                mPaint);
    }

    @SuppressWarnings("NullableProblems")
    @Override
    public boolean onTouchEvent(MotionEvent event) {
        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
//                LogUtils.d("ACTION_DOWN");
                memoryX = event.getX();
                memoryY = event.getY();
                checkMode(memoryX, memoryY);
                break;
            case MotionEvent.ACTION_MOVE: {
                currentX = event.getX();
                currentY = event.getY();
                switch (MODE) {
                    case MODE_ILLEGAL:
                        recoverFromIllegal(currentX, currentY);
                        postInvalidate();
                        break;
                    case MODE_OUTSIDE:
                        //do nothing;
                        break;
                    case MODE_INSIDE:
                        moveByTouch(currentX, currentY);
                        postInvalidate();
                        break;
                    default:
                        /*MODE_POINT*/
                        moveByPoint(currentX, currentY);
                        postInvalidate();
                        break;
                }
            }
            break;
            case MotionEvent.ACTION_UP:
//                mPaint.setColor(getContext().getResources().getColor(R.color.orange));
                postInvalidate();
                break;
            default:
                break;
        }
        return true;
    }

    /*點擊頂點附近時的縮放處理*/
    @SuppressWarnings("SuspiciousNameCombination")
    private void moveByPoint(float bx, float by) {
//        LogUtils.d("moveByPoint");
        switch (pointPosition) {
            case 0:/*left-up*/
                mCoverWidth = Math.abs(endX - bx);
                mCoverHeight = Math.abs(endY - by);
                //noinspection SuspiciousNameCombination
                if (!checkLegalRect(mCoverWidth, mCoverHeight)) {
                    MODE = MODE_ILLEGAL;
                } else {
                    refreshLocation(bx, by, endX, endY);
                }
                break;
            case 1:/*right-up*/
                mCoverWidth = Math.abs(bx - startX);
                mCoverHeight = Math.abs(endY - by);
                if (!checkLegalRect(mCoverWidth, mCoverHeight)) {
                    MODE = MODE_ILLEGAL;
                } else {
                    refreshLocation(startX, by, bx, endY);
                }
                break;
            case 2:/*left-down*/
                mCoverWidth = Math.abs(endX - bx);
                mCoverHeight = Math.abs(by - startY);
                if (!checkLegalRect(mCoverWidth, mCoverHeight)) {
                    MODE = MODE_ILLEGAL;
                } else {
                    refreshLocation(bx, startY, endX, by);
                }
                break;
            case 3:/*right-down*/
                mCoverWidth = Math.abs(bx - startX);
                mCoverHeight = Math.abs(by - startY);
                if (!checkLegalRect(mCoverWidth, mCoverHeight)) {
                    MODE = MODE_ILLEGAL;
                } else {
                    refreshLocation(startX, startY, bx, by);
                }
                break;
            default:
                break;
        }
    }

    /*刷新矩形的座標*/
    private void refreshLocation(float isx, float isy, float iex, float iey) {
        this.startX = isx;
        this.startY = isy;
        this.endX = iex;
        this.endY = iey;

        mCoverWidth = endX - startX;
        mCoverHeight = endY - startY;

//        LogUtils.d("refreshLocation -- startX: " + startX);
    }

    /*檢測矩形是否達到最小值*/
    private boolean checkLegalRect(float cHeight, float cWidth) {
        return (cHeight > minHeight && cWidth > minWidth);
    }

    /*從非法狀態恢復,這裏處理的是達到最小值後能拉伸放大*/
    private void recoverFromIllegal(float rx, float ry) {
        if ((rx > startX && ry > startY) && (rx < endX && ry < endY)) {
            MODE = MODE_ILLEGAL;
        } else {
            MODE = MODE_POINT;
        }
    }

    private void checkMode(float cx, float cy) {
        if (cx > startX && cx < endX && cy > startY && cy < endY) {
            MODE = MODE_INSIDE;
        } else if (nearbyPoint(cx, cy) < 4) {
            MODE = MODE_POINT;
        } else {
            MODE = MODE_OUTSIDE;
        }
    }

    /*矩形隨手指移動*/
    private void moveByTouch(float mx, float my) {/*move center point*/
        LogUtils.d("moveByTouch");
        float dX = mx - memoryX;
        float dY = my - memoryY;


        startX += dX;
        startY += dY;

//        LogUtils.d("moveByTouch -- mCoverWidth: " + mCoverWidth);

        endX = startX + mCoverWidth;
        endY = startY + mCoverHeight;

        memoryX = mx;
        memoryY = my;
    }

    /*判斷點(inX,inY)是否靠近矩形的4個頂點*/
    private int nearbyPoint(float floatX, float floatY) {
        if ((Math.abs(startX - floatX) <= ACCURACY && (Math.abs(floatY - startY) <= ACCURACY))) {/*left-up angle*/
            pointPosition = 0;
            return 0;
        }
        if ((Math.abs(endX - floatX) <= ACCURACY && (Math.abs(floatY - startY) <= ACCURACY))) {/*right-up  angle*/
            pointPosition = 1;
            return 1;
        }
        if ((Math.abs(startX - floatX) <= ACCURACY && (Math.abs(floatY - endY) <= ACCURACY))) {/*left-down angle*/
            pointPosition = 2;
            return 2;
        }
        if ((Math.abs(endX - floatX) <= ACCURACY && (Math.abs(floatY - endY) <= ACCURACY))) {/*right-down angle*/
            pointPosition = 3;
            return 3;
        }
        pointPosition = 100;
        return 100;
    }

    // 設置矩形框
    public void setRectF(RectF rectf) {
        this.mRectF = rectf;
    }

    public void setTitle(String title) {
        mTitle = title;
    }

    public void setConfidence(float confidence) {
        mConfidence = confidence;
    }

    public void setCornerAngle(int cornerAngle) {
        this.mCornerAngle = cornerAngle;
    }

    // 繪製 損害框(直角矩形框)
    private void drawRect(Canvas canvas) {

        canvas.drawRect(mRectF, mPaint);

        // 繪製名稱 和 概率
        final String labelString =
                !TextUtils.isEmpty(mTitle)
                        ? String.format("%s %.2f", mTitle, (100 * mConfidence))
                        : String.format("%.2f", (100 * mConfidence));

        // 在 直角矩形框 上寫字
        mBorderedText.drawText(canvas,
                mRectF.left,
                mRectF.top, labelString + "%",
                mPaint);
    }

    // 繪製 損害框(圓角矩形框)
    private void drawRoundRect(Canvas canvas) {
        float cornerSize = Math.min(mRectF.width(), mRectF.height()) / 8.0f;
        canvas.drawRoundRect(mRectF, cornerSize, cornerSize, mPaint);

        // 繪製名稱 和 概率
        final String labelString =
                !TextUtils.isEmpty(mTitle)
                        ? String.format("%s %.2f", mTitle, (100 * mConfidence))
                        : String.format("%.2f", (100 * mConfidence));

        // 在 圓角矩形框 上寫字
        mBorderedText.drawText(canvas,
                mRectF.left + cornerSize,
                mRectF.top, labelString + "%",
                mPaint);
    }

    public void setLocationListener(onLocationListener mLocationListener) {
        this.mLocationListener = mLocationListener;
    }

    public interface onLocationListener {
        void locationRect(float startX, float startY, float endX, float endY);
    }


}

2、DrawRect5Activity.java

public class DrawRect5Activity extends AppCompatActivity {

    @BindView(R.id.ivBackGround)
    ImageView ivBackGround;
    @BindView(R.id.moveAndCropRectView)
    MoveAndCropRectView moveAndCropRectView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_draw_rect5);
        ButterKnife.bind(this);

        RectF rectF = new RectF(600, 600, 1200, 1200);
        moveAndCropRectView.setRectF(rectF);
        moveAndCropRectView.setTitle("網狀裂縫");

        moveAndCropRectView.setLocationListener(new MoveAndCropRectView.onLocationListener() {
            @Override
            public void locationRect(float startX, float startY, float endX, float endY) {
//                LogUtils.d("[ " + startX + "--" + startY + "--" + endX + "--" + endY + " ]");
            }
        });

    }


}

3、activity_draw_rect5.xml

<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <ImageView
        android:id="@+id/ivBackGround"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:background="@mipmap/road_details_bg" />

    <com.gs.common3.aView.cropRectView.moveRectAndCrop.MoveAndCropRectView
        android:id="@+id/moveAndCropRectView"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />

</android.support.constraint.ConstraintLayout>

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章