第六章 一张白纸好作画—Canvas画布(5)

6.4.8区域 android.graphics.Region与Region.Op

在Canvas的绘画时,我们可能碰到止需要显示半个矩形,或者显示一部分图片,那么我们就要用到Canvas的设置区域的方法,有clipRect(Rect rect,Region.Op op)、clipRegion(Region region)这两个方法。Region表示的是一个区域和Rect不同的是,它可以表示的一个不规则的样子,可以是椭圆、多边形等等,当然Region也可以表示一个矩形,而Rect仅仅是矩形。

同样Region的boolean contains(int x, int y) 成员可以判断一个点是否在该区域内。

Region.Op是多个区域叠加效果的参数。

public enum Op {

DIFFERENCE(0),//DIFFERENCE 第一个中不同于第二个的部分显示出来

INTERSECT(1),//INTERSECT 取两者交集,默认的方式

UNION(2),//UNION 取全集

XOR(3),//XOR 补集,就是全集的减去交集的剩余部分显示

REVERSE_DIFFERENCE(4),//第二个不同于第一个的部分显示

REPLACE(5);//REPLACE 显示第二个的

}

   

下面我们来解读下SDK中的ApiDemos(com.example.android.apis.graphics.Region.java)这个示例。效果图如图6-4所示。

图6-4 Region的示例

 

它主要是将两个Rect放在同一个Region中,根据不同的Region.Op来制作出的效果图,有颜色的区域为有效区,不同的颜色表示合并后产生的不同Rect。

核心代码如下:

// 定义两个Rect(矩形)

mRect1.set(10, 10, 100, 80);

mRect2.set(50, 50, 130, 110);

// 定义一个Region,用来保存两个Rect的集合

Region rgn = new Region();

// 将mRect1添加进Region中

rgn.set(mRect1);

// 将mRect2添加进Region中,注意这里的第二个参数,他就是要传进去得效果的标示,详细参见上面的Region.Op说明。

rgn.op(mRect2, op);

// Region的迭代器,可以讲一个Region分解成不同的Rect,通过iter.next(Rect r)方法来把每个矩形提取出来。

RegionIterator iter = new RegionIterator(rgn);

Rect r = new Rect ();

while (iter.next(r)) {

canvas.drawRect(r, mPaint);

}

6.4.9千姿百态矩阵变换 android.graphics.Matrix

对前面的基础知识有所了解后,我们就可以来看android.graphics.Matrix类,该类表示一个转换矩阵,它确定如何将一个坐标空间的点映射到另一个坐标空间。通过设置Matrix对象的属性并将其应用于Canvas对象或Bitmap对象,我们可以对该对象执行各种图形转换。这些转换函数包括平移(x和y重新定位)、旋转、缩放和倾斜,达到很炫的效果。

matrix 对象被视为具有如下内容的 3 x 3 的矩阵:

在传统的转换矩阵中,u、v和w属性具有其它功能。Matrix类只能在二维空间中操作,因此始终假定属性值u和v为0.0,属性值w为1.0。换句话说,矩阵的有效值如下:

您可以获取和设置 Matrix 对象的全部六个其它属性的值:a、b、c、d、tx 和 ty。

Matrix类支持四种主要的转换函数类型:平移、缩放、旋转和倾斜。对于这些函数中的三种,有特定的方法,如表6-8中所述。

方法

矩阵值

显示结果

说明

Translate(tx,ty)

平移(置换),

将图像向右移动tx像素,向下移动ty 像素。

scale(sx, sy)

缩放,

调整图像的大小,方法是将每个像素的位置在 x 轴方向上乘以sx并在 y 轴方向上乘以sy。

rotate(q)

旋转,

将图像旋转一个以弧度为单位的角度q。

Skew(skx, sky)

倾斜,

以平行于X轴或Y轴的方向逐渐滑动图像。skx 值充当乘数,控制沿x 轴滑动的距离;sky控制沿y轴滑动的距离。

表6-8 Matrix类支持的四种主要的转换函数

 

下面的代码简单的实现了图片的倒影镜像

Matrix mMatrix = new Matrix();

mMatrix.setScale(1.0f, -1.0f);

canvas.drawBitmap(mBitmap, mMatrix, null);

 

上述的四种操作的方法,每种操作方法都有三种接口setXX、preXX、postXX。setXX将使整个matrix的值为设置的值。preXX是将新的变换矩阵左乘原来的矩阵,而postXX是将新的变换矩阵右乘原来的变换矩阵。

 

经验分享:

在组合matrix中preTranslate、setTranslate、postTranslate是有很大区别的。抽象的说pre方法是向前“生长”,post方法是向后“生长”,下面还是通过2个例子来说明:

matrix.preScale(0.5f, 1);

matrix.preTranslate(10, 0);

matrix.postScale(0.7f, 1);

matrix.postTranslate(15, 0);

则坐标变换经过的4个变换过程依次是:

translate(10, 0) -> scale(0.5f, 1) -> scale(0.7f, 1) -> translate(15, 0),

所以对matrix方法的调用顺序是很重要的,不同的顺序往往会产生不同的变换效果。pre方法的调用顺序和post方法的互不影响,即以下的方法调用和前者在真实坐标变换顺序里是一致的。

matrix.postScale(0.7f, 1);

matrix.preScale(0.5f, 1); 

matrix.preTranslate(10, 0);

matrix.postTranslate(15, 0);

而matrix的set方法则会对先前的pre和post操作进行刷除,而后再设置它的值,比如下列的方法调用:

matrix.preScale(0.5f, 1);

matrix.postTranslate(10, 0);

matrix.setScale(1, 0.6f);

matrix.postScale(0.7f, 1);

matrix.preTranslate(15, 0);

其坐标变换顺序是translate(15, 0) -> scale(1, 0.6f) -> scale(0.7f, 1).

另外可以注意这个方法Matrix.mapRect(RectF rect);对RectF矩形进行变换。

 

矩阵一般应用在变换view的时候,那么很多时候我们将需要将一些点或矩形,进行转换,Android的Matrix为我们提供了很方便的方法来进行计算。下面我们来看个例子:

float[] p1 = {1000f,100f};

float[] p2 = {1000f,100f};

 

// 下面是一个正向的过程

// 原始变换矩阵

Matrix m1 = new Matrix();

// m1的逆矩阵

Matrix m2 = new Matrix();

Log.d("test111 ", ""+p1[0]+","+p1[1]);

m1.postTranslate(100, 300);

m1.postScale(0.6f, 0.3f);

m1.postRotate(45.f);

// 这个过程是将p1{1000f,100f}这个点通过了m1的转换,变成了一个新的点p1,这时候p1已经变成了转换后的点了。

m1.mapPoints(p1);

Log.d("test222 ", ""+m1.toString());

Log.d("test333 ", ""+p1[0]+","+p1[1]);

 

// 下面是一个逆向的过程

// 将p1经过转换的点,赋值给p2

p2 = p1;

Log.d("test444 ", ""+p2[0]+","+p2[1]);

// 这里将m1进行了逆向,然后存放在m2里

boolean temp = m1.invert(m2);

Log.d("test555 ", ""+m2.toString());

Log.d("test666 ", ""+temp);

// 这里转换过的点可以理解为转换后的点,通过逆向矩阵m2得到最原始的点的位子,并存放在p2里。

m2.mapPoints(p2);

Log.d("test777 ", ""+p2[0]+","+p2[1]);

 

图6-5显示了运行的结果。

图6-5 矩阵逆向例子的结果

 

经验分享:

通过上面的例子我们可以看到,你可能在一个时候只需要用到一部分,及矩阵正向的逻辑,或矩阵逆向的逻辑。需要注意的是下面2个方法:

1)m1.mapPoints(p1);//这个过程是将p1{1000f,100f}这个点通过了m1的转换,变成了一个新的点p1,这时候p1已经变成了转换后的float数组了。

2)m1.invert(m2);//这里将m1进行了逆向,然后存放在m2里

 

©️2020 CSDN 皮肤主题: 大白 设计师:CSDN官方博客 返回首页