zoukankan      html  css  js  c++  java
  • 讲解Canvas中的一些重要方法

    Canvas所提供的各种方法根据功能来看大致可以分为几类:

    第一是以drawXXX为主的绘制方法;

    第二是以clipXXX为主的裁剪方法;

    第三是以scale、skew、translate和rotate组成的Canvas变换方法;

    最后一类则是以saveXXX和restoreXXX构成的画布锁定和还原;

    还有一些其他的就不归类了。

     如果用到的不能硬件加速的方法,请务必关闭硬件加速,否则可能会产生不正确的效果。

    一、clipRect(…)

    clipRect是将当前画布裁剪为一个矩形,以后画的图像仅仅只能在画在这块矩形区域中,超出的部分完全看不到的。可以说裁剪后的区域是我们以后画图的画布,而原来的画布已经不存在了。

    下面的代码在画布没裁剪的时候绘制了蓝色,裁剪后绘制了红色。可见裁剪后的画布就仅剩下红色区域了。

        @Override
        protected void onDraw(Canvas canvas) {
            super.onDraw(canvas);
            canvas.drawColor(Color.BLUE);  
            canvas.clipRect(0, 0, 500, 500);  
            canvas.drawColor(Color.RED);  
        } 

    上面我们用到了:

    clipRect(int left, int top, int right, int bottom)  

    相似的还有:

    clipRect(float left, float top, float right, float bottom)  

    一个int一个float。除此之外还有两个与之对应的方法,传入rect或rectf即可。

    clipRect(Rect rect)  
    clipRect(RectF rect) 

    Rect和RectF是类似的,只不过RectF中涉及计算的时候数值类型均为float型,两者均表示一块规则矩形。

    二、用Rect实现多区域裁剪

    2.1 intersect

    先来看一段代码:

        @Override
        protected void onDraw(Canvas canvas) {
            super.onDraw(canvas);
            canvas.drawColor(Color.BLUE);  
            
            Rect rect = new Rect(0, 0, 500, 500);  
              
            rect.intersect(250, 250, 750, 750); 
            
            canvas.clipRect(rect);
            canvas.drawColor(Color.RED);  
        }  

    结果:

    PS:黄色线框为后期加上的辅助线非程序生成

    其实intersect的作用跟我们之前学到的图形混合模式有点类似,它会取两个区域的相交区域作为最终区域,上面我们的第一个区域是在实例化Rect时确定的(0, 0, 500, 500),第二个区域是调用intersect方法时指定的(250, 250, 750, 750),这两个区域对应上图的两个黄色线框,两者相交的地方则为最终的红色区域。

    它的计算方法是,先得到左上方块区域的右下角坐标,再得到下方方框的左上角坐标,两个点,四个坐标构成一个新的rect,然后再进行裁剪。 

    2.2 union

    union方法与intersect相反,取的是相交区域最远的左上端点作为新区域的左上端点,而取最远的右下端点作为新区域的右下端点,比如:

        @Override
        protected void onDraw(Canvas canvas) {
            super.onDraw(canvas);
            canvas.drawColor(Color.BLUE);  
            
            Rect rect = new Rect(0, 0, 500, 500);  
              
            rect.union(250, 250, 750, 750);  
            
            canvas.clipRect(rect);
            canvas.drawColor(Color.RED);  
        } 

    计算方式:先得到左上矩阵的左上角,再得到下方矩阵的右下角,两个点,四个坐标构成一个新的rect。

    三、clipPath(Path path)  

     我们可以利用该方法从Canvas中“挖”取一块不规则的画布:

        @Override
        protected void onDraw(Canvas canvas) {
            super.onDraw(canvas);
            mPaint.setColor(Color.RED);
            mPaint.setStyle(Paint.Style.STROKE);
    
            // 实例化路径
            mPath = new Path();
            // 移动起点至[50,50]
            mPath.moveTo(50, 50);  
            mPath.lineTo(75, 23);  
            mPath.lineTo(150, 100);  
            mPath.lineTo(80, 110);  
            // 闭合路径
            mPath.close();  
            
            // 在原始画布上绘制蓝色
            canvas.drawColor(Color.BLUE);  
            // 按照路径进行裁剪
            canvas.clipPath(mPath);  
            // 在裁剪后剩余的画布上绘制红色
            canvas.drawColor(Color.RED);  
        }

    四、Region.Op

    4.1 引入

    回顾Canvas中有关裁剪的方法,你会发现有一大堆带有Region.Op参数的重载方法:

    clipPath(Path path, Region.Op op)  
    clipRect(Rect rect, Region.Op op)  
    clipRect(RectF rect, Region.Op op)  
    clipRect(float left, float top, float right, float bottom, Region.Op op)  
    clipRegion(Region region, Region.Op op)  

    要明白这些方法的Region.Op参数那么首先要了解Region为何物。Region的意思是“区域”,在Android里呢它同样表示的是一块封闭的区域,Region中的方法都非常的简单,我们重点来瞧瞧Region.Op,Op是Region的一个枚举类,里面呢有六个枚举常量:

    那么Region.Op其实就是个组合模式。

    4.2 用代码进行测试

    我这里给出一个代码框架,大家可以自行替换粗体文字来看效果。记得关闭硬件加速哦~

    public class CanvasView extends View {  
        private Region mRegionA, mRegionB;// 区域A和区域B对象  
        private Paint mPaint;// 绘制边框的Paint  
      
        public CanvasView(Context context, AttributeSet attrs) {  
            super(context, attrs);  
      
            // 实例化画笔并设置属性  
            mPaint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.DITHER_FLAG);  
            mPaint.setStyle(Paint.Style.STROKE);  
            mPaint.setColor(Color.WHITE);  
            mPaint.setStrokeWidth(2);  
      
            // 实例化区域A和区域B  
            mRegionA = new Region(100, 100, 300, 300);  
            mRegionB = new Region(200, 200, 400, 400);  
        }  
      
        @Override  
        protected void onDraw(Canvas canvas) {  
            // 填充颜色  
            canvas.drawColor(Color.BLUE);  
      
            canvas.save();  
      
            // 裁剪区域A  
            canvas.clipRegion(mRegionA);  
      
            // 再通过组合方式裁剪区域B  
            canvas.clipRegion(mRegionB, Region.Op.DIFFERENCE);  
      
            // 填充颜色  
            canvas.drawColor(Color.RED);  
      
            canvas.restore();  
      
            // 绘制框框帮助我们观察  
            canvas.drawRect(100, 100, 300, 300, mPaint);  
            canvas.drawRect(200, 200, 400, 400, mPaint);  
        }  
    }  

    以下是各种组合模式的效果:

    DIFFERENCE

    最终区域为第一个区域与第二个区域不同的区域。

    INTERSECT

    最终区域为第一个区域与第二个区域相交的区域。

    REPLACE

    最终区域为第二个区域。

    REVERSE_DIFFERENCE

    最终区域为第二个区域与第一个区域不同的区域。

    UNION

    最终区域为第一个区域加第二个区域。

    XOR

    最终区域为第一个区域加第二个区域并减去两者相交的区域。

     

    4.3 Region

    上面用的是region进行演示的,其余的rect什么的自己去试试,都是一样的。

    有些童鞋会问那么Region和Rect有什么区别呢?首先最重要的一点,Region表示的是一个区域,而Rect表示的是一个矩形,这是最根本的区别之一。其次,Region有个很特别的地方是它不受Canvas的变换影响Canvas的local不会直接影响到Region自身

    我们来看一个simple你就会明白:

        @Override
        protected void onDraw(Canvas canvas) {
            super.onDraw(canvas);
            // 因为用到了clipRegion,所以要关闭硬件加速
            setLayerType(View.LAYER_TYPE_SOFTWARE, null);  
            
            // 设置画笔
            mPaint.setStyle(Paint.Style.STROKE);
            mPaint.setColor(Color.GREEN);  
            mPaint.setStrokeWidth(10); 
    
            // 实例化矩形对象
            Rect rect = new Rect(0, 0, 200, 200);
    
            // 实例化区域对象
            Region region = new Region(200, 200, 400, 400);
    
            canvas.save();
    
            // 裁剪矩形
            canvas.clipRect(rect);
            canvas.drawColor(Color.RED);
    
            canvas.restore();
    
            canvas.save();
    
            // 裁剪区域
            canvas.clipRegion(region);
            canvas.drawColor(Color.BLUE);
    
            canvas.restore();
    
            // 为画布绘制一个边框便于观察
            canvas.drawRect(0, 0, canvas.getWidth(), canvas.getHeight(), mPaint);
        }

    效果图中很清楚的表示了画布当前是和屏幕一样大小的,rect区域是红色的,region区域是蓝色的,两个矩形一样大。当我们按比例缩小画布,来看看会发生什么改变呢?

    // 缩放画布  
    canvas.scale(0.75F, 0.75F); 

    完整代码:

    @Override
        protected void onDraw(Canvas canvas) {
            super.onDraw(canvas);
            // 因为用到了clipRegion,所以要关闭硬件加速
            setLayerType(View.LAYER_TYPE_SOFTWARE, null);  
            
            // 缩放画布  
            canvas.scale(0.75F, 0.75F); 
            
            // 设置画笔
            mPaint.setStyle(Paint.Style.STROKE);
            mPaint.setColor(Color.GREEN);  
            mPaint.setStrokeWidth(10); 
    
            // 实例化矩形对象
            Rect rect = new Rect(0, 0, 200, 200);
    
            // 实例化区域对象
            Region region = new Region(200, 200, 400, 400);
    
            canvas.save();
    
            // 裁剪矩形
            canvas.clipRect(rect);
            canvas.drawColor(Color.RED);
    
            canvas.restore();
    
            canvas.save();
    
            // 裁剪区域
            canvas.clipRegion(region);
            canvas.drawColor(Color.BLUE);
    
            canvas.restore();
    
            // 为画布绘制一个边框便于观察
            canvas.drawRect(0, 0, canvas.getWidth(), canvas.getHeight(), mPaint);
        }

    画布缩小后,矩形区域也跟着缩小了,但是region却没有缩小,这就表示region不会随着画布的缩放而改变。

    五、saveXXX和restoreXXX

    5.1 引入“层”的概念

    saveXXX和restoreXXX这两个方法一个是保存画布,一个是释放画布。其实满抽象的,下面我给出一段代码:

        @Override
        protected void onDraw(Canvas canvas) {
            super.onDraw(canvas);
     
            mPaint.setColor(Color.RED);
            canvas.drawRect(50, 50, 250, 250, mPaint);
            
            // 保存画布
            canvas.save();
    
            mPaint.setColor(Color.BLUE);
            canvas.drawRect(100, 100, 200, 200, mPaint);
            
            // 还原画布
            canvas.restore();
            
            mPaint.setColor(Color.GREEN);
            canvas.drawCircle(150, 150, 40, mPaint);
        }

    这段代码中先绘制了一个一个红色矩形,然后保存了画布,再绘制一个蓝色矩形,接着释放画布,最后绘制了绿色的圆。简单来说就是画了矩形,矩形,圆形。而save()和restore()区域内的代码你可以简单理解为一个PS图层。

    当然上面仅仅是简单的类比,android的绘制机制和ps的有一些本质差异,ps中各种图层都是在画板上的,你可以调整上下顺序;而android中是用栈来处理的,而且一旦你调用了drawxxx方法,图像就被绘制到屏幕上了,你再想改变就不可能了。也就是说drawxxx是一个提交操作,类似于ps中的保存,你做图的时候可以各种设置,一旦你保存了某一个图层,那么就没办法再改了。

    5.2 用代码来解释“层”

        @Override
        protected void onDraw(Canvas canvas) {
            super.onDraw(canvas);
            
            mPaint.setColor(Color.RED);
            canvas.drawRect(50, 50, 250, 250, mPaint);// 保存画布
            canvas.save();
            
            mPaint.setColor(Color.BLUE);
            canvas.drawRect(100, 100, 200, 200, mPaint);
            
            // 还原画布
            canvas.restore();
            
            mPaint.setColor(Color.GREEN);
            canvas.drawCircle(150, 150, 40, mPaint);
        }

    代码很简单,直接上截图:

    这里你看不到任何有关于层的特性,只看到了先画的图形会被放在最下面,后画出的图像会叠放在上层。那么我们修改一下代码加上一个旋转的效果,看看会如何。

        @Override
        protected void onDraw(Canvas canvas) {
            super.onDraw(canvas);
        
            mPaint.setColor(Color.RED);
            canvas.drawRect(50, 50, 250, 250, mPaint);
            
            
            // 保存画布
            canvas.save();
            // 旋转画布
            canvas.rotate(30); 
                    
            mPaint.setColor(Color.BLUE);
            canvas.drawRect(100, 100, 200, 200, mPaint);
            
            // 还原画布
            canvas.restore();
            
            mPaint.setColor(Color.GREEN);
            canvas.drawCircle(150, 150, 40, mPaint);
        }

    我们在save()后添加了旋转,绘制后释放了画布。你会发现旋转的只有蓝色的矩形,之后绘制的绿色圆形没有旋转。这就说明蓝色的矩形被我们放在了一个新的层上,在那里进行的操作是不会影响到之后的图层的。一旦你释放了画布,之后的操作就在别的层上了,所以我们的绿色圆形没有发生任何变化。

    至此结合上一节对Canvas的一些原理阐述我们该对它有个全新的认识,之前我们一直称其为画布,其实更准确地说Canvas是一个容器,如果把Canvas理解成画板,那么我们的“层”就像张张夹在画板上的透明的纸,而这些纸对应到Android则是一个个封装在Canvas中的Bitmap。

    六、saveLayerXXX

    6.1 save()和saveLayer(…)

    除了save()方法Canvas还给我们提供了一系列的saveLayerXXX方法给我们保存画布,与save()方法不同的是,saveLayerXXX方法会将所有的操作存到一个新的Bitmap中而不影响当前Canvas的Bitmap,而save()方法则是在当前的Bitmap中进行操作,并且只能针对Bitmap的形变和裁剪进行操作,saveLayerXXX方法则无所不能,当然两者还有很多的不同,我们稍作讲解。虽然save和saveLayerXXX方法有着很大的区别但是在一般应用上两者能实现的功能是差不多,我们可以用代码来进比较:

    save()

        @Override
        protected void onDraw(Canvas canvas) {
            super.onDraw(canvas);
        
            mPaint.setColor(Color.RED);
            canvas.drawRect(50, 50, 250, 250, mPaint);
            
            
            // 保存画布
            canvas.save();
            // 旋转画布
            canvas.rotate(30); 
                    
            mPaint.setColor(Color.BLUE);
            canvas.drawRect(100, 100, 200, 200, mPaint);
            
            // 还原画布
            canvas.restore();
            
            mPaint.setColor(Color.GREEN);
            canvas.drawCircle(150, 150, 40, mPaint);
        }

    saveLayer(…)

    @Override
        protected void onDraw(Canvas canvas) {
            super.onDraw(canvas);
        
            mPaint.setColor(Color.RED);
            canvas.drawRect(50, 50, 250, 250, mPaint);
            
            
            // 保存画布
            canvas.saveLayer(0, 0, 250, 250, null, Canvas.ALL_SAVE_FLAG);
            // 旋转画布
            canvas.rotate(30); 
                    
            mPaint.setColor(Color.BLUE);
            canvas.drawRect(100, 100, 200, 200, mPaint);
            
            // 还原画布
            canvas.restore();
            
            mPaint.setColor(Color.GREEN);
            canvas.drawCircle(150, 150, 40, mPaint);
        }

      其实二者的效果是一样的,但为什么我们的蓝色矩形的一个角不见了呢?这是因为saveLayer保存的是一个区域,而这个区域小于蓝色矩形显示的区域,所以就进行了裁切。类比于PS,ps中的每个图层的大小都是等于图片大小的,但是这里我们可以单独设置每个层的大小。下面我们来引入saveLayer保存图层的原理。

      saveLayerXXX方法会将操作保存到一个新的Bitmap中,而这个Bitmap的大小取决于我们传入的参数大小,Bitmap是个相当危险的对象,很多朋友在操作Bitmap时不太理解其原理经常导致OOM,在saveLayer时我们会依据传入的参数获取一个相同大小的Bitmap,虽然这个Bitmap是空的但是其会占用一定的内存空间,我们希望尽可能小地保存该保存的区域,而saveLayer则提供了这样的功能,顺带提一下,onDraw方法传入的Canvas对象的Bitmap在Android没引入HW之前理论上是无限大的,实际上其依然是根据你的图像来不断计算的,而在引入HW之后,该Bitmap受到限制,具体多大大家可以尝试画一个超长的path运行下,你就可以在Logcat中看到warning。

    6.2 saveLayerAlpha(…)

    除了saveLayer,Canvas还提供了一个saveLayerAlpha方法,顾名思义,该方法可以在我们保存画布时设置画布的透明度:

    @Override
        protected void onDraw(Canvas canvas) {
            super.onDraw(canvas);
        
            mPaint.setColor(Color.RED);
            canvas.drawRect(50, 50, 250, 250, mPaint);
            
            
            // 保存画布
            canvas.saveLayerAlpha(0, 0, 250, 250, 100, Canvas.ALL_SAVE_FLAG);
            // 旋转画布
            canvas.rotate(30); 
                    
            mPaint.setColor(Color.BLUE);
            canvas.drawRect(100, 100, 200, 200, mPaint);
            
            // 还原画布
            canvas.restore();
            
            mPaint.setColor(Color.GREEN);
            canvas.drawCircle(150, 150, 40, mPaint);
        }

    6.3 flag的值

    我们在用saveLayerAlphasaveLayer方法时都用到了一个flag值Canvas.ALL_SAVE_FLAG,还有别的常量值可以使用么?我们还发现

    save (int saveFlags)

    也需要传入一个flag,这个flag是什么呢?

    这六个常量值分别标识了我们在调用restore方法后还原什么。

    六个标识位除了CLIP_SAVE_FLAG、MATRIX_SAVE_FLAG和ALL_SAVE_FLAGsavesaveLayerXXX方法都通用外,其余三个只能使saveLayerXXX方法有效。也就是说

    CLIP_SAVE_FLAG

    MATRIX_SAVE_FLAG

    ALL_SAVE_FLAG

    可以被save(int flag)和saveLayerXXX(…,int flag)使用,其余的仅仅适用于saveLayerXXX方法。

    ALL_SAVE_FLAG  很简单也是我们新手级常用的标识保存所有

    CLIP_SAVE_FLAG  是裁剪标识位;

    MATRIX_SAVE_FLAG  是变换的标识位;

    CLIP_TO_LAYER_SAVE_FLAG、FULL_COLOR_LAYER_SAVE_FLAG和HAS_ALPHA_LAYER_SAVE_FLAG只对saveLayer和saveLayerAlpha有效。

    CLIP_TO_LAYER_SAVE_FLAG  表示对当前图层执行裁剪操作需要对齐图层边界;

    FULL_COLOR_LAYER_SAVE_FLAG  表示当前图层的色彩模式至少需要是8位色,

    HAS_ALPHA_LAYER_SAVE_FLAG  表示在当前图层中将需要使用逐像素Alpha混合模式,关于色彩深度和Alpha混合大家可以参考维基百科

    平时使用大家可以直接ALL_SAVE_FLAG就行,感兴趣的可以了解下别的标志位的作用。

    七、restoreToCount(int saveCount)

    所有的save、saveLayer和saveLayerAlpha方法都有一个int型的返回值,该返回值作为一个标识给与了一个你当前保存操作的唯一ID编号,我们可以利用restoreToCount(int saveCount)方法来指定在还原的时候还原哪一个保存操作。

    7.1 restoreToCount和save

    @Override
        protected void onDraw(Canvas canvas) {
            super.onDraw(canvas);
    
            // 保存画布,并且得到id01
            int saveID1 = canvas.save(Canvas.MATRIX_SAVE_FLAG);
            // 旋转画布
            canvas.rotate(30);
            // 画红色的矩形
            mPaint.setColor(Color.RED);
            canvas.drawRect(50, 50, 250, 250, mPaint);
    
            // 建立新的层,得到id02
            int saveID2 = canvas.save(Canvas.MATRIX_SAVE_FLAG);
            // 画蓝色的矩形
            mPaint.setColor(Color.BLUE);
            canvas.drawRect(100, 100, 200, 200, mPaint);
    
            // 还原画布01
            canvas.restoreToCount(saveID1);
            
            // 画绿色圆形
            mPaint.setColor(Color.GREEN);
            canvas.drawCircle(150, 150, 40, mPaint);
    
            
        }

    上面的效果可以告诉我们,虽然我们在绘制蓝色矩形前新建了一个图层,但蓝色矩形还是随着红色矩形旋转了。而我们调用canvas.restoreToCount(saveID1)后绘制的绿色圆形却出现了正常的位置上,没有进行旋转。

    下面我们用栈来解释一下:

    前面我们曾说过save和saveLayerXXX方法有着本质的区别,saveLayerXXX方法会将所有操作在一个新的Bitmap中进行,而save则是依靠stack栈来进行。

    Canvas会默认保存一个底层的空间给我们绘制一些东西,当我们没有调用save方法时所有的绘图操作都在这个Default Stack ID中进行,每当我们调用一次save就会往Stack中存入一个ID,将其后所有的操作都在这个ID所指向的空间进行直到我们调用restore方法还原操作,上面代码我们save了2次且仅仅restore了ID1,所以我们之后绘制的绿色圆形是在ID2的栈中,而蓝色的矩形还是在ID1的栈中。

    当我们调用canvas.restore()后标志着上一个save操作的结束或者说回滚了,下面我们整理下代码来看看不同的效果:

        @Override
        protected void onDraw(Canvas canvas) {
            super.onDraw(canvas);
    
            // 保存画布,并且得到id01
            int saveID1 = canvas.save(Canvas.MATRIX_SAVE_FLAG);
            // 旋转画布
            canvas.rotate(30);
            // 画红色的矩形
            mPaint.setColor(Color.RED);
            canvas.drawRect(50, 50, 250, 250, mPaint);
            
            // 还原画布01
            canvas.restoreToCount(saveID1);
            
            // 建立新的层,得到id02
            int saveID2 = canvas.save(Canvas.MATRIX_SAVE_FLAG);
            
            // 画蓝色的矩形
            mPaint.setColor(Color.BLUE);
            canvas.drawRect(100, 100, 200, 200, mPaint);
    
            // 还原画布02
            canvas.restoreToCount(saveID2);
            
            // 画绿色圆形
            mPaint.setColor(Color.GREEN);
            canvas.drawCircle(150, 150, 40, mPaint);
    
            
        }

    可以明显的发现蓝色矩形这时候和红色矩形处于不同的层上了,没有随着红色矩形进行旋转。

    7.2 getSaveCount()

    前面说了,每当我们调用restore还原Canvas,对应的save栈空间就会从Stack中弹出去,Canvas提供了getSaveCount()方法来为我们提供查询当前栈中有多少save的空间。这个方法没啥可说的,上代码:

    @Override
        protected void onDraw(Canvas canvas) {
            super.onDraw(canvas);
    
            System.out.println(canvas.getSaveCount()); 
            
            // 保存画布,并且得到id01
            int saveID1 = canvas.save(Canvas.MATRIX_SAVE_FLAG);
            System.out.println(canvas.getSaveCount()); 
            // 旋转画布
            canvas.rotate(30);
            // 画红色的矩形
            mPaint.setColor(Color.RED);
            canvas.drawRect(50, 50, 250, 250, mPaint);
            
            // 还原画布01
            canvas.restoreToCount(saveID1);

    八、变换

    说起变换,无非就几种:平移、旋转、缩放和错切,而我们的Canvas也继承了变换的精髓,同样提供了这几种相应的方法,像translate(float dx, float dy)方法平移画布用了无数次,这里再次强调,translate方法会改变画布的原点坐标,原点坐标对变换的影响弥足轻重

    8.1 scale(float sx, float sy)

        @Override
        protected void onDraw(Canvas canvas) {
            super.onDraw(canvas);
    
            Bitmap bitmap = BitmapFactory.decodeResource(mContext.getResources(), R.drawable.kale);
            // 缩放比率为1时表示不缩放
            canvas.scale(1.0F, 1.0F);  
            canvas.drawBitmap(bitmap, 0, 0, null);  
        }

    上面的缩放比为1,也就是不做任何改变。我们来改动下缩放比:

    canvas.scale(0.8F, 0.35F); 

    8.2scale(float sx, float sy, float px, float py)

    scale(float sx, float sy)缩放也很好理解,但是它有一个重载方法scale(float sx, float sy, float px, float py),后两个参数用于指定缩放的中心点,前两个参数用于指定横纵向的缩放比率值在0-1之间为缩小。

        @Override
        protected void onDraw(Canvas canvas) {
            super.onDraw(canvas);
    
            Bitmap bitmap = BitmapFactory.decodeResource(mContext.getResources(), R.drawable.kale);
            canvas.scale(0.8F, 0.35F, bitmap.getWidth(), 0);  
            canvas.drawBitmap(bitmap, 0, 0, null);  
        }

    我们发现图像往右边移动了一点。这是因为我们把缩放中心进行的改变,大家尝试改变下参数,多多理解理解。

    8.3 rotate(float degrees)和rotate(float degrees, float px, float py)

    rotate(float degrees)和重载方法rotate(float degrees, float px, float py)它们的作用是旋转画布。这个在上面已经多次用到了,就不再赘述了。

    8.4 skew(float sx, float sy)

    kew(float sx, float sy)是错切方法,两个参数与scale类似表示横纵向的错切比率。所谓错切就是把图片分割为不同的格子,类似于棋盘。然后移动上面的各个交点的位置,让图片进行变换的效果。

        @Override
        protected void onDraw(Canvas canvas) {
            super.onDraw(canvas);
    
            Bitmap bitmap = BitmapFactory.decodeResource(mContext.getResources(), R.drawable.kale);
            canvas.skew(0.5F, 0F);  
            canvas.drawBitmap(bitmap, 0, 0, null);  
        }

    效果:

    8.5 setMatrix(matrix)

    看名字就知道了,通过矩阵来进行变换。有关矩阵的一些知识,我在之前的文章中已经介绍过了,如果不了解计算原理,可以去参考下文的第七节:

    详解Paint的setShader(Shader shader)

        @Override
        protected void onDraw(Canvas canvas) {
            super.onDraw(canvas);
    
            Matrix matrix = new Matrix();  
            matrix.setScale(0.8F, 0.35F);  
            matrix.postTranslate(100, 100);  
            canvas.setMatrix(matrix);  
            
            Bitmap bitmap = BitmapFactory.decodeResource(mContext.getResources(), R.drawable.kale);
            canvas.drawBitmap(bitmap, 0, 0, null);  
        }

    本文大部分内容参考自:

    http://blog.csdn.net/aigestudio/article/details/41960507

    http://blog.csdn.net/aigestudio/article/details/42677973

    http://blog.csdn.net/airk000/article/details/38925059

    我记录在此,作为学习笔记。

    From AigeStudio(http://blog.csdn.net/aigestudio)Power by Aige 尊重原作者,感谢作者的无私分享!

  • 相关阅读:
    【HDOJ5971】Wrestling Match(二分图,并查集)
    【HDOJ5978】To begin or not to begin(概率)
    【HDOJ5979】Convex(三角函数)
    【HDOJ5980】Find Small A(签到)
    【HDOJ5949】Relative atomic mass(签到)
    【HDOJ5948】Thickest Burger(签到)
    【HDOJ6228】Tree(树)
    【HDOJ6227】Rabbits(贪心)
    147.命题逻辑
    146.离散数学
  • 原文地址:https://www.cnblogs.com/tianzhijiexian/p/4300988.html
Copyright © 2011-2022 走看看