android绘图canvas的sava、restore、rotate以及若干问题,canvas绘图的理解

20150228修正更新:

        其他部分没问题,关键在于最后一点,旋转的不是画布,是画布的坐标系。将最后2图红色区域理解为坐标系即可。

感谢xiaweicheng指正。


-----------------------原文------------------------------


        不知道是我的理解能力有问题还是怎么回事,网上有很多举例说明的,我都看不懂,理解不能,而楼下还有很多的人说一目了然浅显易懂。纠结了N久,决定自己来理解一番,做个记录。


        所有的操作都是通过canvas来实现的,这个的意思是说,平移、旋转、放大缩小的操作,都是直接操作canvas来实现。canvas就是一般理解的画布。导致理解错误的根源就是这里。

        


经常看到这样的代码:

//前期定义  Canvas canvas 
	    canvas.drawRect(100, 100, 150, 150, p1);   // p1 是红色画笔
	    canvas.rotate(30);  
	    canvas.drawRect(200, 200, 250, 250, p2);   // p2 是蓝色画笔


        按照我一般的理解,先画了一个红色rect(100,100,150,150),然后旋转canvas 30度,再画一个蓝色rect(200,200,250,250)。

        那么,由于是对canvas的操作,旋转了画布,第一个rect应该旋转了,然后画第二个rect,第二个rect应该没旋转,预估效果应该是下左图(也就是第一个方块旋转,第二个方块不旋转)。而实际效果确是下右图(第一个不旋转,第二个旋转)。


还有一个问题,图上方块的位置和代码里面的貌似也不一样,这个后面再说


        然后试验了几次之后我发现,画图的canvas和显示没关系,而这个canvas又只影响你画图。

        意思就是,画图,实际上是画在这个canvas定义的范围和规则下,但是显示在屏幕上,如下图:



蓝色的是手机屏幕边框,红色的是一个全屏的canvas




        当canvas旋转30度之后,其实成了这个样子,但是你绘制的图形还是只能在canvas内,并且坐标是按照canvas旋转之前来的,也就是左上角坐标仍然是(0,0),右下角的坐标仍然是(width,height)。

        也就是说,你按照(100,100,150,150)画出来的rect需要旋转30度之后,才是你看到的效果,这就是上面那个坐标看上去好像不对的原因。


忘记个重要的事情,这个canvas只是定义了画图的规则,范围,但是其实图是画在屏幕上的,所以最开始那个2个rect谁该旋转的问题就解决了。



        那么save和restore的问题也就好解决了,save只是save了当前这个canvas的状态,和已经画出来的图形无关,restore就是取出最近一次save的canvas的状态,仍然不影响图形。




        还有一个涉及到的问题就是,如下面旋转canvas之后的图,那么这个时候画在canvas之外的图形会怎么处理保存,试验了之后发现,由显示控件截取。

        也就是说如果我画在一个bitmap上,那么这个bitmap的边界就是能显示的最大范围

发布了85 篇原创文章 · 获赞 2 · 访问量 43万+
展开阅读全文

安卓canvas上如何选中并清除之前画的某一笔画(橡皮功能)

06-12

* 使用drawPath(Path,Paint)方法画的,所有Path存放在一个arraylist之中。 * 现在想在onTouchEvent方法中实现橡皮功能,手指触摸选中笔画并清除之。 * 有使用Paint的setStrokeWidth方法,因此画出来的笔画有粗细。 如何判断手指触点坐标是否在形成的笔画图形之内? 最核心问题在于**怎么确定笔画形成的那块区域**? 注:橡皮擦清除整条独立的笔画,类似于Google keep里的绘图那样,不是用背景色绘制触摸轨迹 下面是代码 用于储存path和画笔类型的 fingerPath类: ``` import android.graphics.Path; public class FingerPath { public int color; public boolean emboss; public boolean blur; public int strokeWidth; public Path path; public FingerPath(int color, boolean emboss, boolean blur, int strokeWidth, Path path) { this.color = color; this.emboss = emboss; this.blur = blur; this.strokeWidth = strokeWidth; this.path = path; } } ``` 自定义view类: ``` import android.content.Context; import android.graphics.Bitmap; import android.graphics.BlurMaskFilter; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.EmbossMaskFilter; import android.graphics.MaskFilter; import android.graphics.Paint; import android.graphics.Path; import android.util.AttributeSet; import android.util.DisplayMetrics; import android.view.MotionEvent; import android.view.View; import java.util.ArrayList; public class PaintView extends View { public static int BRUSH_SIZE = 20; public static final int DEFAULT_COLOR = Color.RED; public static final int DEFAULT_BG_COLOR = Color.WHITE; private static final float TOUCH_TOLERANCE = 4; private float mX, mY; private Path mPath; private Paint mPaint; private ArrayList<FingerPath> paths = new ArrayList<>(); private int currentColor; private int backgroundColor = DEFAULT_BG_COLOR; private int strokeWidth; private boolean emboss; private boolean blur; private MaskFilter mEmboss; private MaskFilter mBlur; private Bitmap mBitmap; private Canvas mCanvas; private Paint mBitmapPaint = new Paint(Paint.DITHER_FLAG); public PaintView(Context context) { this(context, null); } 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); } 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; } public void emboss() { emboss = true; blur = false; } public void blur() { emboss = false; blur = true; } public void clear() { backgroundColor = DEFAULT_BG_COLOR; paths.clear(); normal(); invalidate(); } public void undo() { int size=paths.size(); paths.remove(size-1); invalidate(); } @Override protected void onDraw(Canvas canvas) { canvas.save(); mCanvas.drawColor(backgroundColor); 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); mCanvas.drawPath(fp.path, mPaint); } canvas.drawBitmap(mBitmap, 0, 0, mBitmapPaint); canvas.restore(); } private void touchStart(float x, float y) { mPath = new Path(); FingerPath fp = new FingerPath(currentColor, emboss, blur, strokeWidth, mPath); paths.add(fp); mPath.reset(); mPath.moveTo(x, y); mX = x; mY = y; System.out.println("按下时list长度"+paths.size()); } 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) { float x = event.getX(); float y = event.getY(); 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; } } ``` 问答

没有更多推荐了,返回首页

©️2019 CSDN 皮肤主题: 大白 设计师: CSDN官方博客

分享到微信朋友圈

×

扫一扫,手机浏览