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里 |