自定义view(SurfaceView绘制可缩放、旋转、移动的简单视图)

自定义view(SurfaceView绘制可缩放、旋转、移动的简单视图)

package com.ahtelit.zbv.myapplication;


import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.DashPathEffect;
import android.graphics.Paint;
import android.graphics.RectF;
import android.util.AttributeSet;
import android.util.DisplayMetrics;
import android.util.Log;
import android.view.MotionEvent;
import android.view.SurfaceHolder;
import android.view.SurfaceView;

/**
 * Created by Administrator on 2018/4/19.
 * qzx
 */

public class SimpleTransformView extends SurfaceView implements SurfaceHolder.Callback, Runnable {

    private boolean isDestroy;
    private SurfaceHolder mSurfaceHolder;
    private Canvas mCanvas;
    private Paint mPaint;

    //绘制的位置和大小和上下左右的等数据
    private RectF rectF;//canvas绘制矩形有两种:一种是int的还有就是这个float
    private float rectf_range_width = 200.0f;//矩形的长度
    private float rectf_range_height = 200.0f;
    private static final float TIPS_MARGIN = 80.0f;
    private static final float TIPS_RANGE = 100.0f;//tips的矩形高度
    private static final float DASH_RANGE = 10.0F;//触碰虚线生效的范围

    private static final float Circle_Radius = 20.0f;//可缩放的圆圈半径
    private float side_left;
    private float side_top;
//    private float side_right;
//    private float side_bottom;

    private float pad_width;
    private float pad_height;

    //绘制的视图类型
    private int drawType = 0;//默认为文本类型
    private static final int TYPE_TEXT = 0;//文本
    private static final int TYPE_IMG = 1;//图片

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

    public SimpleTransformView(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public SimpleTransformView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        initConfig();
    }

    private void initConfig() {
        mSurfaceHolder = getHolder();
        mSurfaceHolder.addCallback(this);

        //配置基础的画笔参数
        mPaint = new Paint();
        mPaint.setFlags(Paint.ANTI_ALIAS_FLAG);
        mPaint.setStyle(Paint.Style.FILL);
//        mPaint.setStyle(Paint.Style.STROKE);//描边绘制

        //保持常亮
        setKeepScreenOn(true);

        //触摸可以得到焦点
        setFocusable(true);
        setFocusableInTouchMode(true);

        //默认绘制的视图大小---矩形
        rectF = new RectF(100, 100, 100 + rectf_range_width, 100 + rectf_range_height);
        side_left = rectF.left;
        side_top = rectF.top;
//        side_right=rectF.left+RECTF_RANGE;
//        side_bottom=rectF.top+RECTF_RANGE;

        //获取手机屏幕大小
        DisplayMetrics dm = getResources().getDisplayMetrics();
        pad_width = dm.widthPixels;
        pad_height = dm.heightPixels;
    }

//    @TargetApi(Build.VERSION_CODES.LOLLIPOP)
//    public SimpleTransformView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
//        super(context, attrs, defStyleAttr, defStyleRes);
//    }

    @Override
    public void surfaceCreated(SurfaceHolder holder) {

        new Thread(this).start();
        isDestroy = false;

    }

    @Override
    public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {

    }

    @Override
    public void surfaceDestroyed(SurfaceHolder holder) {

        isDestroy = true;

    }

    @Override
    public void run() {

        while (!isDestroy) {
            try {
                mCanvas = mSurfaceHolder.lockCanvas();
                drawUI();
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                if (mCanvas != null)
                    mSurfaceHolder.unlockCanvasAndPost(mCanvas);
            }

        }

    }

    private String testText = "七子笑";
    private String tipsText = "旋转";
    private float hadRotateAngle = 0;//已经旋转过的角度
    private float rotateAngle = 0;//要旋转的角度
    private String tipsDegree = 0 + "°";//显示角度提示
    private float wordSize = 20.0f;

    private void drawUI() {
        if (drawType == TYPE_TEXT) {

            mCanvas.drawColor(Color.WHITE);//清屏操作
//            if (isCanRotate) {
            mCanvas.save();
                /*
                如果不设置旋转点默认是起始位置,可能使(0,0)
                并且Canvas的旋转都是数学坐标系的Y轴向上的为其实0度旋转
                --->据说旋转的是画布,视图属性还是在原来的位置的
                 */
            mCanvas.rotate(rotateAngle, rectF.centerX(), rectF.centerY());
            commonView();
            mCanvas.restore();
//            } else {
//                commonView();
//            }

            /*所以fontMetrics的bottom是正数=bottom-baseline;的top是负数=top-baseline
              除了fontMetrics.leading是负数=top-ascent,其余的都是和baseline挂钩
             */
//            float baseline=(side_top+side_bottom-(fontMetrics.bottom+fontMetrics.top))/2;
            //通过FontMetrics的baseline绘制
//            mCanvas.drawText(testText,rectF.centerX(),baseline,mPaint);
        }

    }

    private void commonView() {
        //绘制文本视图---背景、文字
        mPaint.setColor(Color.GRAY);
        mPaint.setStyle(Paint.Style.FILL);
        mCanvas.drawRect(rectF, mPaint);
        mPaint.setColor(Color.WHITE);
        mPaint.setTextAlign(Paint.Align.CENTER);//绘制在x,y虚拟矩形中间
//            Paint.FontMetrics fontMetrics = mPaint.getFontMetrics();
        mPaint.setTextSize(wordSize);
        mCanvas.drawText(testText, rectF.left + rectF.width() / 2, rectF.top + rectF.height() / 2, mPaint);

        //绘制缩放视图
        if (isShowScaleView) {
            mPaint.setColor(Color.RED);
            mCanvas.drawCircle(rectF.left, rectF.top, Circle_Radius, mPaint);//左上角
            mCanvas.drawCircle(rectF.left, rectF.bottom, Circle_Radius, mPaint);//左下角
            mCanvas.drawCircle(rectF.right, rectF.top, Circle_Radius, mPaint);//右上角
            mCanvas.drawCircle(rectF.right, rectF.bottom, Circle_Radius, mPaint);//右下角
        }

        //绘制旋转提示的文本和矩形背景
        if (isShowRotateView) {
            mPaint.setColor(Color.BLACK);
            mCanvas.drawRect(rectF.left, rectF.top - TIPS_MARGIN - TIPS_RANGE, rectF.right, rectF.top - TIPS_MARGIN, mPaint);
            mPaint.setColor(Color.WHITE);
            Paint.FontMetrics fontMetrics = new Paint.FontMetrics();
            float baseLine = (rectF.top * 2 - 2 * TIPS_MARGIN - TIPS_RANGE - (fontMetrics.top + fontMetrics.bottom)) / 2;
            tipsDegree = rotateAngle + "°";
            mCanvas.drawText(tipsDegree, rectF.left + rectF.width() / 2, baseLine, mPaint);

            //虚线---触碰到可以旋转
            mPaint.setColor(Color.GRAY);
            mPaint.setPathEffect(new DashPathEffect(new float[]{15, 3}, 0));
            mCanvas.drawLine(rectF.left + rectF.width() / 2, rectF.top, rectF.left + rectF.width() / 2, rectF.top - TIPS_MARGIN, mPaint);

        } else {
            mPaint.setColor(Color.GRAY);
            mCanvas.drawRect(rectF.left, rectF.top - TIPS_MARGIN - TIPS_RANGE, rectF.right, rectF.top - TIPS_MARGIN, mPaint);
            mPaint.setColor(Color.WHITE);
            Paint.FontMetrics fontMetrics = new Paint.FontMetrics();
            float baseLine = (rectF.top * 2 - 2 * TIPS_MARGIN - TIPS_RANGE - (fontMetrics.top + fontMetrics.bottom)) / 2;
            mCanvas.drawText(tipsText, rectF.left + rectF.width() / 2, baseLine, mPaint);
        }
    }

//    private static final float INVALID_RANGE = 10;//设置无效的范围点

    private float downX;//触摸点的起始位置-X
    private float downY;//触摸点的起始位置-Y

    private boolean isCanMove = false;
    private boolean isShowScaleView = false;//是否显示可缩放的view
    private boolean isCanScale = false;
    private boolean isCanRotate = false;
    private boolean isShowRotateView = false;//是否显示可旋转的view
//    private boolean isHadRotateDegree = false;//是否有旋转的操作即有旋转角度

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        switch (event.getAction() & MotionEvent.ACTION_MASK) {
            case MotionEvent.ACTION_DOWN:
                //有旋转角度
                if (hadRotateAngle != 0) {
                    Log.d("zbv", "右旋转角=" + hadRotateAngle);
                    float currentLocations[] = getBeforeViewLocation(hadRotateAngle, event.getX(), event.getY());
                    downX = currentLocations[0];
                    downY = currentLocations[1];
                } else {
                    downX = event.getX();
                    downY = event.getY();
                }
                if (isShowRotateView) {
                    if (downX > (rectF.left + rectF.width() / 2 - DASH_RANGE) && downX < (rectF.left + rectF.width() / 2 + DASH_RANGE)
                            && downY < rectF.top && (downY > rectF.top - TIPS_MARGIN)) {
                        Log.d("zbv", "触碰到虚线了");
                        isCanRotate = true;
                    }
                } else {
                    if (rectF.contains(downX, downY)) {
                        Log.d("zbv", "在绘制范围内可以显示缩放视图");
                        isShowScaleView = true;
                        isCanMove = true;
                    } else {
                        Log.d("zbv", "绘制区域外隐藏缩放视图");
                        isShowScaleView = false;
                    }

                    if (downX > rectF.left && downX < rectF.right && downY > (rectF.top - TIPS_MARGIN - TIPS_RANGE)
                            && downY < (rectF.top - TIPS_MARGIN)) {
                        Log.d("zbv", "触摸到旋转区域");
                        isShowRotateView = true;
                        isCanMove = false;
                    }

                    //触摸到可缩放区域-左上、左下、右上、右下
                    if (((rectF.left - Circle_Radius) < downX && downX < (rectF.left + Circle_Radius)
                            && (rectF.top - Circle_Radius) < downY && downY < (rectF.top + Circle_Radius))
                            || ((rectF.left - Circle_Radius) < downX && downX < (rectF.left + Circle_Radius)
                            && (rectF.bottom - Circle_Radius) < downY && downY < (rectF.bottom + Circle_Radius))
                            || ((rectF.right - Circle_Radius) < downX && downX < (rectF.right + Circle_Radius)
                            && (rectF.top - Circle_Radius) < downY && downY < (rectF.top + Circle_Radius))
                            || ((rectF.right - Circle_Radius) < downX && downX < (rectF.right + Circle_Radius)
                            && (rectF.bottom - Circle_Radius) < downY && downY < (rectF.bottom + Circle_Radius))) {
                        Log.d("zbv", "触摸到要缩放的视图区域了");
                        isCanScale = true;
                        isCanMove = false;//不可以移动了
                        //重置触摸点为了缩放
                        downX=event.getX();
                        downY=event.getY();
                    }

//                    if (event.getX() > (rectF.left + rectF.width() / 2 - DASH_RANGE) && event.getX() < (rectF.left + rectF.width() / 2 + DASH_RANGE)
//                            && event.getY() < rectF.top && (event.getY() > rectF.top + TIPS_MARGIN)) {
//                        Log.d("zbv", "触碰到虚线了");
//                        isCanRotate = true;
//                    }
                }

                break;
            case MotionEvent.ACTION_MOVE:
                if (isCanMove) {
                    if (!rectF.contains(event.getX(), event.getY())) {
//                        Log.d("zbv", "移动手指出了绘制视图"+rectf_range_width);
                        rectF.left = event.getX();
                        rectF.top = event.getY();
                        rectF.right = rectF.left + rectf_range_width;
                        rectF.bottom = rectF.top + rectf_range_height;
                        side_left = rectF.left;
                        side_top = rectF.top;

                    } else {
                        Log.d("zbv", "移动手指仍在绘制视图");
                    }
                }
                if (isCanScale) {
                    rectF.left = side_left;
                    rectF.left += (event.getX() - downX);
                    rectf_range_width = rectF.right - rectF.left;
//                        Log.d("zbv","scale of rectLeft="+rectF.left+";width="+rectf_range_width);
                    rectF.top = side_top;
                    rectF.top += (event.getY() - downY);
                    rectf_range_height = rectF.bottom - rectF.top;
//                        Log.d("zbv","scale of rectLeft="+rectF.right+";height="+rectf_range_height);
                }
                if (isCanRotate) {
                    //计算两直线间的角度---矩形中心点、其实触摸点、移动的触摸点
                    float angle_1 = (float) Math.atan2(downY - (rectF.top + rectF.height() / 2), downX - (rectF.left + rectF.width() / 2));
                    float angle_2 = (float) Math.atan2(event.getY() - (rectF.top + rectF.height() / 2), event.getX() - (rectF.left + rectF.width() / 2));
                    int jiaodu_1 = (int) Math.toDegrees(angle_1);
                    int jiaodu_2 = (int) Math.toDegrees(angle_2);

                    rotateAngle = jiaodu_2 - jiaodu_1;

//                    Log.d("zbv","rotateAngle="+rotateAngle);

                    if (rotateAngle > 180.0f) {
                        rotateAngle -= 360.0f;
                    }

//                    Log.d("zbv", "起始触摸点到中心点角度为=" + angle_1 + ";角度为=" + jiaodu_1
//                            + ";移动点到中心点角度为=" + angle_2 + ";角度为=" + jiaodu_2);
                }
                break;
            case MotionEvent.ACTION_UP:
                //抬起-如果可以缩放就置为原始不缩放状态
                if (isCanScale) {
                    isCanScale = false;
                }
                if (isCanRotate) {
                    isCanRotate = false;
                    isShowRotateView = false;
                    hadRotateAngle = rotateAngle;
                }
                break;
            //多点触摸---当一根手指已经down
            case MotionEvent.ACTION_POINTER_DOWN:
                break;
            case MotionEvent.ACTION_POINTER_UP:
                break;
        }
        return true;
    }

    /*
    为了处理canvas旋转导致的视图属性没有变化从而使得触摸点的无法生效
    所以反推原始点即:知道圆点、和旋转后的点以及角度--->推导出旋转前的点
     */
    private float[] getBeforeViewLocation(float angle, float dot_x, float dot_y) {
        float jiaodu = -angle;
        float[] locations = new float[2];
        double arcValue = Math.toRadians(jiaodu);
        float sin = (float) Math.sin(arcValue);
        float cos = (float) Math.cos(arcValue);
        locations[0] = (dot_x - (rectF.left + rectF.width() / 2)) * cos - (dot_y - (rectF.top + rectF.height() / 2)) * sin + rectF.left + rectF.width() / 2;
        locations[1] = (dot_y - (rectF.top + rectF.height() / 2)) * cos + (dot_x - (rectF.left + rectF.width() / 2)) * sin + rectF.top + rectF.height() / 2;
        return locations;
    }

    /**
     * 公开绘制类型的方法:
     * 文本---文本
     * 图片---资源图
     */
    public int getDrawType(int type) {
        if (type == TYPE_TEXT) {
            drawType = TYPE_TEXT;
        } else if (type == TYPE_IMG) {
            drawType = TYPE_IMG;
        }
        return 0;
    }
}

后续更正中。。。

猜你喜欢

转载自blog.csdn.net/zb52588/article/details/80019489
今日推荐