本章主要介绍了Android的图形、图像处理,按照下面思维导图的思路来梳理本章书的内容。重点学习使用简单图片方法;Android丰富的绘图API,包括Canvas、Paint、Path等类;各种图形特效,利用Matrix对图形进行几何变换;最后是Android的动画支持。
【知识点】
·Bitmap和BitmapFactory:
Bitmap代表一张位图,BitmapDrawable里封装的图片就是一个Bitmap对象。开发者为了把一个Bitmap对象包装成BitmapDrawable对象,可调用BitmapDrawable构造器:
// 把一个Bitmap对象包装成BitmapDrawable对象
BitmapDrawable drawable = new BitmapDrawable(bitmap);
如果需要获取BitmapDrawable所包装的Bitmap对象,则可以调用BitmapDrawable的getBitmap()方法,如下面的代码所示:
//获取一个BitmapDrawable所包装的Bitmap对象
Bitmap bitmap=drawable.getBitmap();
BitmapFactory是一个工具类,它用于提供大量的方法,这些方法可用于从不同的数据源来解析、创建Bitmap对象,BitmapFactory包含如下方法:
手机系统的内存比较小,如果系统不停地去解析、创建Bitmap对象,可能由于前面创建Bitmap所占用的内存还没有回收,从而导致程序运行时引发OutOfMemory错误。Android为Bitmap提供了两个方法来判断它是否已回收,以及强制Bitmap回收自己:
boolean isRecycled():返回该Bitmap是否已被回收。
void recycle():强制一个Bitmap对象立即回收自己。
除此之外,如果Android应用需要访问其它存储路径(比如SD卡中)里的图片,都需要借助于BitmapFactory来解析、创建Bitmap对象。
·绘图:
Android的绘图应该继承View组件,并重写它的onDraw(Canvas canvas)方法即可。重写onDraw(Canvas canvas)方法时涉及一个绘图APICanvas,Canvas代表了“依附”于指定View的画布,它提供了一些方法绘制各种图形。
Canvas还提供了如下方法进行变换:
Canvas提供的上面的方法还涉及一个API:Paint,Paint代表了Canvas上的画笔,因此Paint类主要用于设置绘制风格,包括画笔颜色、画笔笔触粗细、填充风格等。
在Canvas提供的绘制方法中还用到了一个API:Path,Path代表任意多条直线连接而成的任意图形,当Canvas根据Path绘制时,它可以绘制出任意的形状。
·使用Matrix控制图像或组件变换步骤:
①获取Matrix对象,该Matrix对象既可创建,也可直接获取其他对象内封装的Matrix;
②调用Matrix的方法进行平移、旋转、缩放、倾斜等;
③将程序对Matrix所做的变换应用到指定图像或组件。
·双缓冲机制:
每次View组件上的图形状态数据发生了改变,都应该通知View组件重写onDraw(Canvas canvas)方法重绘组件。通知View重绘可调用invalidate(在UI线程中)或postInvalidate(非UI线程中)。当程序需要在指定View上进行绘制时,程序并不直接绘制到View组件上,而是先绘制到一个内存中的Bitmap图片(这就是缓冲)上,等到内存中的Bitmap绘制好之后,再一次性地将Bitmap绘制到View组件上。
·逐帧动画:
逐帧(Frame)是最容易理解的动画,它要求开发者把动画过程的每张静态图片都收集起来,然后由Android来控制依次显示这些静态图片。逐帧动画通常也是采用XML资源文件进行定义的。定义逐帧动画非常简单,只要在<animation-list.../>元素中使用<item.../>子元素定义动画的全部帧,并指定各帧的持续时间即可。
一旦程序获取了AnimationDrawable对象之后,接下来接可用ImageView把AnimationDrawable显示出来----习惯上把AnimationDrawable设成ImageView的背景即可。需要指出的是:AnimationDrawable代表的动画默认是不播放的,必须在程序中启动动画播放才可以。AnimationDrawable提供了入戏两个方法来开始、停止动画:
start():开始播放动画
stop():停止播放动画
·补间(Tween)动画:
补间动画就是指开发者只需要指定动画开始、动画结束“关键帧”,而动画变化的“中间帧”由系统计算并补齐。对于补间动画而言,开发者无需“逐一”定义动画过程中的每一帧,他只要定义动画开始、结束的关键帧,并指定动画的持续时间即可。补间动画所定义的开始帧、结束帧其实只是一些简单的变化,比如图形大小的缩放、旋转角度的改变等,Android使用Animation代表抽象的动画类,它包括如下几个子类:
AlphaAnimation:透明度改变的动画,其中透明度可从0变化到1。创建时指定开始透明度、结束透明度和持续时间。
ScaleAnimation:大小缩放的动画。创建时指定开始缩放比、结束缩放比、持续时间,还要指定“缩放中心”坐标。
TranslateAnimation:位移变化的动画,只要设置开始位置、结束位置和持续时间即可。
RotateAnimation:旋转动画。创建时指定动画开始旋转角度、结束旋转角度、持续时间,还要指定“旋转中心”坐标。
一旦为补间动画指定了3个必要信息,Android就会根据动画的开始帧、结束帧、动画持续时间计算出需要在中间“补入”多少帧,并计算所有补入帧的图形。当用户浏览补间动画时,他眼中看到的依然是“逐帧动画”。
为了控制在动画期间需要“补入”多少帧,具体在动画运行的哪些时刻补入帧,需要借助于Interpolator。Interpolator根据特定的算法计算出整个动画所需要动态插入帧的密度和位置。简单的说就是Interpolator负责控制动画的变化速度,这就使得基本的动画效果(Alpha、Scale、Translate、Rotate)能以匀速、加速、减速、抛物线速度等各种速度变化。
Android为Interpolator提供了如下几个实现类,分别用于实现不同动画变化速度:
LinearInterpolator:动画以均匀的速度变化。
AccelerateInterpolator:在动画开始的地方改变速度较慢,然后开始加速。
AccelerateDecelerateInterpolator:在动画开始、结束的地方改变速度较慢,在中间的时候加速。
CycleInterpolator:动画循环播放特定的次数,变化速度按正弦曲线变化。
DecelerateInterpolator:在动画开始的地方改变速度较快,然后开始减速。
为了在动画资源文件中指定补间动画所使用的Interpolator,定义补间动画的<set.../>元素支持一个android:interpolator属性,该属性可指定默认的Interpolator。例如:
@android:anim/linear_interpolator
@android:anim/accelerate_interpolator
@android:anim/accelerate_decelerate_interpolator
一旦在程序中通过AnimationUtils得到代表补间动画的Animation之后,接下来就可以调用View的startAnimation(Animation anim)方法开始对View执行动画了。
·属性动画:
属性动画需要定义如下几方面的属性:
动画持续时间,通过android:duration属性指定。
动画差值方式,通过android:interpolator属性指定。
动画重复次数,通过android:repeatCount属性指定。
重复行为,通过android:repeatMode属性指定。
动画集,在属性动画资源文件中通过<set.../>元素来组合,该元素的android:ordering属性指定该组动画是按次序播放,还是同时播放。
帧刷新频率。
使用属性动画的步骤:
①创建ValueAnimator或ObjectAnimator对象
②根据需要为Animator对象设置属性
③如果需要监听Animator的动画开始事件、动画结束事件、动画重复事件、动画值改变事件,并根据事件提供响应处理代码,应该为Animator对象设置事件监听器
④如果有多个动画需要按次序或同时播放,应使用AnimatorSet组合这些动画
⑤调用Animator对象的start()方法启动动画。
·使用SurfaceView实现动画:
SurfaceView一般会与SurfaceHolder结合使用,SurfaceHolder用于向与之关联的SurfaceView上绘图,调用SurfaceView的getHolder()方法即可获取SurfaceView关联的SurfaceHolder。SurfaceHolder提供了如下方法来获取Canvas对象:
Canvas lockCanvas():锁定整个SurfaceView对象,获取其上的Canvas。
Canvas lockCanvas(Rect dirty):锁定SurfaceView上Rect划分的区域,获取其上的Canvas。
获取指定了SurfaceView上的Canvas之后,程序就可以调用Canvas绘图,绘图完成后通过如下方法来释放绘图,提交所绘制的图形:
unlockCanvasAndPost(canvas);
需要指出的是,当调用SurfaceHolder的unlockCanvasAndPost方法之后,该方法之前所绘制的图形还在缓冲中,下一次的lockCanvas()方法锁定的区域可能会“遮挡”它。
【问题】
·问:书中有处代码用到了BlurMaskFilter、EmbossMaskFilter等,问BlurMaskFilter?EmbossMaskFilter?
答:MaskFilter类可以为Paint分配边缘效果。对MaskFilter的扩展可以对一个Paint边缘的alpha通道应用转换。Android包含了下面几种MaskFilter:
BlurMaskFilter
指定了一个模糊的样式和半径来处理Paint的边缘,实现喷涂效果,构造方法如下:
public BlurMaskFilter(float radius, BlurMaskFilter.Blur
style)
其中,Radius:喷涂半径,Style:喷涂类型。
EmbossMaskFilter
指定了光源的方向和环境光强度来添加浮雕效果。构造方法如下:
public EmbossMaskFilter(float[]
direction,float ambient,float specular,float blurRadius)
其中,direction:表示光源从x、y、z照射曲线的方向,ambient:表示周围光源的数量,specular:表示光照的反射系数,blurRadius:在光照前喷涂的范围。
要应用一个MaskFilter,可以使用setMaskFilter方法,并传递给它一个MaskFilter对象。下面的例子是对一个已经存在的Paint应用一个EmbossMaskFilter:
// 设置光源的方向
float[] direction = new float[]{ 1, 1, 1 };
//设置环境光亮度
float light = 0.4f;
// 选择要应用的反射等级
float specular = 6;
// 向mask应用一定级别的模糊
float blur = 3.5f;
EmbossMaskFilter emboss=new EmbossMaskFilter(direction,light,specular,blur);
// 应用mask
myPaint.setMaskFilter(emboss);
来源: <http://yueguc.iteye.com/blog/782525#>
SDK中包含的FingerPaint API demo是说明如何使用MaskFilter的一个非常好的例子。它展示了这两种filter的效果。