Java 如何在画布上检索信息
我想做一个“可缩放的绘画”,我指的是一个我可以缩放、平移、拖动画布,然后在画布上画画的绘画 我有一个无法解决的问题:当我在缩放画布时绘制时,我检索X和Y坐标,并有效地在画布上绘制它。但由于画布缩放,这些坐标不正确 我试图纠正这些错误(乘以(缩放权重/屏幕高度)),但我找不到一种方法来检索必须在原始/未缩放屏幕上绘制的位置 这是我的代码:Java 如何在画布上检索信息,java,android,canvas,drawing,zooming,Java,Android,Canvas,Drawing,Zooming,我想做一个“可缩放的绘画”,我指的是一个我可以缩放、平移、拖动画布,然后在画布上画画的绘画 我有一个无法解决的问题:当我在缩放画布时绘制时,我检索X和Y坐标,并有效地在画布上绘制它。但由于画布缩放,这些坐标不正确 我试图纠正这些错误(乘以(缩放权重/屏幕高度)),但我找不到一种方法来检索必须在原始/未缩放屏幕上绘制的位置 这是我的代码: public class PaintView extends View { public static int BRUSH_SIZE = 20; public
public class PaintView extends View {
public static int BRUSH_SIZE = 20;
public static final int DEFAULT_COLOR = Color.BLACK;
public static final int DEFAULT_BG_COLOR = Color.WHITE;
private static final float TOUCH_TOLERANCE = 4;
private float mX, mY;
private SerializablePath mPath;
private Paint mPaint;
private ArrayList<FingerPath> paths = new ArrayList<>();
private ArrayList<FingerPath> tempPaths = new ArrayList<>();
private int currentColor;
private int backgroundColor = DEFAULT_BG_COLOR;
private int strokeWidth;
private boolean emboss;
private boolean blur;
private boolean eraser;
private MaskFilter mEmboss;
private MaskFilter mBlur;
private Bitmap mBitmap;
private Canvas mCanvas;
private Paint mBitmapPaint = new Paint(Paint.DITHER_FLAG);
public boolean zoomViewActivated = false;
//These two constants specify the minimum and maximum zoom
private static float MIN_ZOOM = 1f;
private static float MAX_ZOOM = 5f;
private float scaleFactor = 1.f;
private ScaleGestureDetector detector;
//These constants specify the mode that we're in
private static int NONE = 0;
private static int DRAG = 1;
private static int ZOOM = 2;
private int mode;
//These two variables keep track of the X and Y coordinate of the finger when it first
//touches the screen
private float startX = 0f;
private float startY = 0f;
//These two variables keep track of the amount we need to translate the canvas along the X
//and the Y coordinate
private float translateX = 0f;
private float translateY = 0f;
//These two variables keep track of the amount we translated the X and Y coordinates, the last time we
//panned.
private float previousTranslateX = 0f;
private float previousTranslateY = 0f;
int currentPositionX = 0;
int currentPositionY = 0;
private class ScaleListener extends ScaleGestureDetector.SimpleOnScaleGestureListener {
@Override
public boolean onScale(ScaleGestureDetector detector) {
scaleFactor *= detector.getScaleFactor();
scaleFactor = Math.max(MIN_ZOOM, Math.min(scaleFactor, MAX_ZOOM));
return true;
}
}
public ArrayList<FingerPath> getDividedPaths(float i){
for(FingerPath p : this.paths){
Matrix scaleMatrix = new Matrix();
scaleMatrix.setScale(i, i);
p.path.transform(scaleMatrix);
}
return this.paths;
}
public ArrayList<FingerPath> getPaths() {
return paths;
}
public void dividePath(float i) {
for(FingerPath p : this.paths){
Matrix scaleMatrix = new Matrix();
scaleMatrix.setScale(i, i);
p.path.transform(scaleMatrix);
}
}
public void setPaths(ArrayList<FingerPath> paths){
this.paths = paths;
}
public void setStrokeWidth(int value){
strokeWidth = value;
}
public PaintView(Context context) {
this(context, null);
detector = new ScaleGestureDetector(getContext(), new ScaleListener());
}
public PaintView(Context context, AttributeSet attrs) {
super(context, attrs);
mPaint = new Paint();
mPaint.setAntiAlias(true);
mPaint.setDither(true);
mPaint.setColor(DEFAULT_COLOR);
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setStrokeJoin(Paint.Join.ROUND);
mPaint.setStrokeCap(Paint.Cap.ROUND);
mPaint.setXfermode(null);
mPaint.setAlpha(0xff);
mEmboss = new EmbossMaskFilter(new float[] {1, 1, 1}, 0.4f, 6, 3.5f);
mBlur = new BlurMaskFilter(5, BlurMaskFilter.Blur.NORMAL);
detector = new ScaleGestureDetector(getContext(), new ScaleListener());
}
public void init(DisplayMetrics metrics) {
int height = metrics.heightPixels;
int width = metrics.widthPixels;
mBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
mCanvas = new Canvas(mBitmap);
currentColor = DEFAULT_COLOR;
strokeWidth = BRUSH_SIZE;
}
public void normal() {
emboss = false;
blur = false;
eraser = false;
}
public void emboss() {
emboss = true;
blur = false;
eraser = false;
}
public void blur() {
emboss = false;
blur = true;
eraser = false;
}
public void eraser() {
eraser = true;
}
public void cancel(){
if(paths.size() != 0){
tempPaths.add(paths.get(paths.size()-1));
paths.remove(paths.size()-1);
invalidate();
}
}
public void redo(){
if(tempPaths.size() != 0){
paths.add(tempPaths.get(tempPaths.size()-1));
tempPaths.remove(tempPaths.size()-1);
invalidate();
}
}
public void clear() {
backgroundColor = DEFAULT_BG_COLOR;
paths.clear();
normal();
invalidate();
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.save();
//We're going to scale the X and Y coordinates by the same amount
canvas.scale(scaleFactor, scaleFactor);
//If translateX times -1 is lesser than zero, let's set it to zero. This takes care of the left bound
if((translateX * -1) < 0) {
translateX = 0;
}
//This is where we take care of the right bound. We compare translateX times -1 to (scaleFactor - 1) * displayWidth.
//If translateX is greater than that value, then we know that we've gone over the bound. So we set the value of
//translateX to (1 - scaleFactor) times the display width. Notice that the terms are interchanged; it's the same
//as doing -1 * (scaleFactor - 1) * displayWidth
else if((translateX * -1) > (scaleFactor - 1) * getWidth()) {
translateX = (1 - scaleFactor) * getWidth();
}
if(translateY * -1 < 0) {
translateY = 0;
}
//We do the exact same thing for the bottom bound, except in this case we use the height of the display
else if((translateY * -1) > (scaleFactor - 1) * getHeight()) {
translateY = (1 - scaleFactor) * getHeight();
}
//We need to divide by the scale factor here, otherwise we end up with excessive panning based on our zoom level
//because the translation amount also gets scaled according to how much we've zoomed into the canvas.
canvas.translate(translateX / scaleFactor, translateY / scaleFactor);
/* The rest of your canvas-drawing code */
mCanvas.drawColor(backgroundColor);
if(paths != null){
for (FingerPath fp : paths) {
mPaint.setColor(fp.color);
mPaint.setStrokeWidth(fp.strokeWidth);
mPaint.setMaskFilter(null);
if (fp.emboss)
mPaint.setMaskFilter(mEmboss);
else if (fp.blur)
mPaint.setMaskFilter(mBlur);
if(fp.eraser) {
mPaint.setColor(DEFAULT_BG_COLOR);
}
mCanvas.drawPath(fp.path, mPaint);
}
}
canvas.drawBitmap(mBitmap, 0, 0, mBitmapPaint);
canvas.restore();
}
private void touchStart(float x, float y) {
mPath = new SerializablePath();
FingerPath fp = new FingerPath(currentColor, emboss, blur, eraser, strokeWidth, mPath);
paths.add(fp);
tempPaths = new ArrayList<>();
mPath.reset();
mPath.moveTo(x, y);
mX = x;
mY = y;
}
private void touchMove(float x, float y) {
float dx = Math.abs(x - mX);
float dy = Math.abs(y - mY);
if (dx >= TOUCH_TOLERANCE || dy >= TOUCH_TOLERANCE) {
mPath.quadTo(mX, mY, (x + mX) / 2, (y + mY) / 2);
mX = x;
mY = y;
}
}
private void touchUp() {
mPath.lineTo(mX, mY);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
if(zoomViewActivated){
boolean dragged = false;
switch (event.getAction() & MotionEvent.ACTION_MASK) {
case MotionEvent.ACTION_DOWN:
mode = DRAG;
//We assign the current X and Y coordinate of the finger to startX and startY minus the previously translated
//amount for each coordinates This works even when we are translating the first time because the initial
//values for these two variables is zero.
startX = event.getX() - previousTranslateX;
startY = event.getY() - previousTranslateY;
break;
case MotionEvent.ACTION_MOVE:
translateX = event.getX() - startX;
translateY = event.getY() - startY;
//We cannot use startX and startY directly because we have adjusted their values using the previous translation values.
//This is why we need to add those values to startX and startY so that we can get the actual coordinates of the finger.
double distance = Math.sqrt(Math.pow(event.getX() - (startX + previousTranslateX), 2) +
Math.pow(event.getY() - (startY + previousTranslateY), 2)
);
if(distance > 0) {
dragged = true;
}
break;
case MotionEvent.ACTION_POINTER_DOWN:
mode = ZOOM;
break;
case MotionEvent.ACTION_UP:
mode = NONE;
dragged = false;
//All fingers went up, so let's save the value of translateX and translateY into previousTranslateX and
//previousTranslate
previousTranslateX = translateX;
previousTranslateY = translateY;
currentPositionX += previousTranslateX;
currentPositionY += previousTranslateY;
break;
case MotionEvent.ACTION_POINTER_UP:
mode = DRAG;
//This is not strictly necessary; we save the value of translateX and translateY into previousTranslateX
//and previousTranslateY when the second finger goes up
previousTranslateX = translateX;
previousTranslateY = translateY;
break;
}
detector.onTouchEvent(event);
//We redraw the canvas only in the following cases:
//
// o The mode is ZOOM
// OR
// o The mode is DRAG and the scale factor is not equal to 1 (meaning we have zoomed) and dragged is
// set to true (meaning the finger has actually moved)
if ((mode == DRAG && scaleFactor != 1f && dragged) || mode == ZOOM) {
invalidate();
}
}else{
float x = event.getX()*(getHeight()/scaleFactor)/getHeight()+currentPositionX;
float y = event.getY()*(getWidth()/scaleFactor)/getWidth()+currentPositionY;
switch(event.getAction()) {
case MotionEvent.ACTION_DOWN :
touchStart(x, y);
invalidate();
break;
case MotionEvent.ACTION_MOVE :
touchMove(x, y);
invalidate();
break;
case MotionEvent.ACTION_UP :
touchUp();
invalidate();
break;
}
}
return true;
}
public类PaintView扩展视图{
公共静态int笔刷大小=20;
公共静态最终整型默认颜色=COLOR.BLACK;
公共静态最终整数默认值\u BG\u COLOR=COLOR.WHITE;
专用静态最终浮动接触公差=4;
私人浮动mX,我的;
私有序列化路径mPath;
私人油漆;
私有ArrayList路径=新ArrayList();
private ArrayList tempPath=new ArrayList();
私人色彩;
private int backgroundColor=默认背景颜色;
私有内冲程宽度;
私有布尔浮雕;
私有布尔模糊;
专用布尔橡皮擦;
私人MaskFilter mEmboss;
私人MaskFilter mBlur;
私有位图mBitmap;
私人帆布mCanvas;
专用油漆mBitmapPaint=新油漆(油漆抖动标志);
公共布尔值zoomViewActivated=false;
//这两个常量指定最小和最大缩放
专用静态浮动最小缩放=1f;
专用静态浮动最大缩放=5f;
私有浮点scaleFactor=1.f;
专用电子秤检测器;
//这些常量指定了我们所处的模式
私有静态int NONE=0;
私有静态int-DRAG=1;
私有静态int-ZOOM=2;
私有int模式;
//这两个变量在第一次移动手指时跟踪手指的X和Y坐标
//触摸屏幕
专用浮点数startX=0f;
私人浮动起点=0f;
//这两个变量跟踪沿X轴平移画布所需的量
//和Y坐标
私有浮动translateX=0f;
私有浮点translateY=0f;
//这两个变量跟踪我们上次转换X和Y坐标的量
//淘金。
private float previousTranslateX=0f;
private float previousTranslateY=0f;
int currentPositionX=0;
int currentPositionY=0;
私有类ScaleListener扩展了scalegestruedetector.SimpleOnScalegestrueListener{
@凌驾
公共布尔标度(scalegestruedetector检测器){
scaleFactor*=检测器。getScaleFactor();
scaleFactor=Math.max(最小缩放,数学最小(scaleFactor,最大缩放));
返回true;
}
}
公共阵列列表GetDividedPath(浮点i){
对于(FingerPath p:this.paths){
矩阵scaleMatrix=新矩阵();
刻度矩阵。设置刻度(i,i);
p、 路径变换(scaleMatrix);
}
返回此路径;
}
公共ArrayList getpath(){
返回路径;
}
公共无效分割路径(浮动i){
对于(FingerPath p:this.paths){
矩阵scaleMatrix=新矩阵();
刻度矩阵。设置刻度(i,i);
p、 路径变换(scaleMatrix);
}
}
公共void集合路径(ArrayList路径){
这个路径=路径;
}
公共无效设置行程宽度(int值){
冲程宽度=值;
}
公共PaintView(上下文){
这个(上下文,空);
检测器=新的scalegestruedetector(getContext(),newscaleListener());
}
公共画图视图(上下文、属性集属性){
超级(上下文,attrs);
mPaint=新油漆();
mPaint.setAntiAlias(true);
mPaint.setDither(true);
mPaint.setColor(默认颜色);
mPaint.setStyle(油漆、样式、笔划);
mPaint.setStrokeJoin(油漆.连接.圆形);
mPaint.setStrokeCap(油漆盖圆形);
mPaint.setXfermode(null);
mPaint.setAlpha(0xff);
mEmboss=新的浮雕maskfilter(新的float[]{1,1,1},0.4f,6,3.5f);
mBlur=新的模糊过滤器(5,BlurMaskFilter.Blur.NORMAL);
检测器=新的scalegestruedetector(getContext(),newscaleListener());
}
公共void init(DisplayMetrics){
int height=metrics.heightPixels;
int width=metrics.widthPixels;
mBitmap=Bitmap.createBitmap(宽度、高度、Bitmap.Config.ARGB_8888);
mCanvas=新画布(mBitmap);
currentColor=默认颜色;
笔划宽度=画笔大小;
}
公共无效正常值(){
浮雕=假;
模糊=错误;
橡皮擦=假;
}
公共虚空浮雕(){
浮雕=真;
模糊=错误;
橡皮擦=假;
}
公共空间模糊(){
浮雕=假;
模糊=真;
橡皮擦=假;
}
公共无效橡皮擦(){
橡皮擦=真;
}
公开作废取消(){
if(path.size()!=0){
add(path.get(path.size()-1));
path.remove(path.size()-1);
使无效();
}
}
公共无效重做(){
if(tempPath.size()!=0){
add(tempPath.get(tempPath.size()-1));
tempPaths.remove(tempPaths.size()-1);
使无效();
}
}
公共空间清除(){
backgroundColor=默认的背景颜色;
清除路径();
正常();
使无效();
}
@凌驾
受保护的void onDraw(画布){
super.onDraw(帆布);
canvas.save();
//我们将以相同的比例缩放X和Y坐标
scale(scaleFactor,scaleFactor);
//如果translateX乘以-1小于零,我们将其设置为零。这将考虑左边界
如果((translateX*-1)<0){
translateX=0;
}
//这就是我们处理右边界的地方。我们将translateX乘以-1与(scaleFactor-1)*displayWidth进行比较。
//如果translateX大于该值,那么我们知道我们已经超过了界限。所以我们设置
//translateX to(1-scaleFactor)乘以显示宽度。请注意,术语是互换的;它是相同的
//正在执行-1*(缩放因子-1)*显示宽度
else if((translateX*-1)>(scaleFactor-1)*getWidth(){
translateX=(1-scaleFactor)*getWidth();
}
if(translateY*-1<0){