自定义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;
}
}
后续更正中。。。