Crazy Android Note Chapter-7

第七章 图形与图像处理

Android的图形处理基础
Bitmap和BitmapFactory
继承View在Android中绘图
掌握Canvas、Paint、Path等绘图API
双缓存机制
使用Matrix对图像进行几何变换
通过drawBitpamMesh方法扭曲图像
使用不同的Shader类渲染图形
逐帧动画
补间动画
属性动画
开发自定义补间动画
SurfaceView的绘图机制
继承SurfaceView开发动画

1、Bitmap和BitmapFactory

Android应用中的图片不仅包括*.png,*.jpg,*.gif等各种格式的位图,还包括使用XML资源文件定义的各种Drawable对象。

Bitmap提供了一些静态方法来创建新的Bitmap对象:
    createBitmap(Bitmap source,int x,int y,int width,int height)
    createScaledBitmap(Bitmap src,int desWidth,int dstHeight,boolean filter)
    createBitmap(int width,int height,Bitmap.Config config)
    createBitmap(Bitmap source,int x,int y,int width,int height,Matrix m,boolean filter)
BitmapFactory是一个工具类,它提供了大量的方法从不同的数据源来解析、创建Bitmap对象:
    decodeByteArray(byte[] data,int offset,int length)
    decodeFile(String pathName)
    decodeFileDescriptor(FileDescriptor fd):用于从FileDescriptor对应的文件中解析、创建Bitmap对象
    decodeResource(Resource res,int id)
    decodeStream(InputStream is)

Android为Bitmap提供了两个方法判断它是否已回收
    boolean isRecyled()
    void recycle()

2、绘图

Canvas
Canvas代表依附于View的画布
一些画各种图形的方法
Paint
Canvas上的画笔,主要用来设置绘制风格,包括画笔颜色,画笔笔触粗细、填充风格等,
Path
Path代表任意多条直线连接而成的任意图形,当Canvas根据Path绘制时,它可以绘制出任意的图形
public void moveTo (float x, float y):设置path的开始坐标
public void lineTo (float x, float y):在设定的点和原先的点之间画一条线,如果之前没有调用moveTo(),那么原先的点默认为(0,0)
public void close ():关闭当前画的轮廓,如果当前的点不是刚开始的点,则自动在这两点之间画一天线。

之后就可以调用Canvas的drawPath(path,paint)方法沿着路径绘制图形。实际上Android还为路径绘制提供了PathEffect来定义绘制效果。
PathEffect:
    ComposePathEffect
    CornerPathEffect
    DashPathEffect
    DiscretePathEffect
    PathDashPathEffect
    SumPathEffect

通过paint.setPathEffect(PathEffect pe);给画笔设置效果

效果

画跟随Path的字体:Canvas.drawTextOnPath(DRAW_STR,path,int ,int,paint)

通知View重绘可以调用invalidate(UI线程中),在非UI线程中需要调用postInvalidate();

所谓的双缓冲技术:当程序需要在指定的View上进行绘制时,程序并不直接绘制到该View组件上,而是先绘制到内存中的一个Bitmap图片(这就是缓冲区)上,等到内存中的Bitmap绘制好后,再一次性地将Bitmap绘制到View组件上。具体实现就是,在构造方法中初始化缓冲需要的Canvas和Paint,监听触摸事件,绘制Bitmap,缓冲Bitmap绘制完成后调用invalidate,将Bitmap绘制到组件的Canvas上。

3、图形特效处理

使用Matrix控制变换
Matrix是Android提供的一个矩阵工具类,它本身并不能对图形或组件进行变换,但它可以与其他API结合来控制图形、组件的变换。
使用步骤:
    获取Matrix对象,该Matrix对象即可新创建,也可直接获取其他对象内封装的Matrix
    调用Matrix的方法进行平移、旋转、缩放、倾斜等(setTranslate(),setSkew(),setRotate(),setScale())
    将程序对Matrix所做的变换应用到指定的图形或组件(Canvas.drawBitmap(Bitmap bitmap,Matrix matrix,Paint paint))
使用drawBitmapMesh扭曲图像
public void drawBitmapMesh (Bitmap bitmap, int meshWidth, int meshHeight, float[] verts, int vertOffset, int[] colors, int colorOffset, Paint paint)
bitmap:指定需要扭曲的源位图。
meshWidth:该参数控制在横向上把该源位图划分为多少格
meshHeight:该参数控制在纵向上把该源位图划分为多少格
verts:该参数是一个长度为(meshWidth + 1) * (meshHeight + 1) * 2 的数组,它记录了扭曲后的位图各“顶点“的位置,虽然它是一个一维数组,但实际上它记录的数据是形如(x0,y0)、(x1,y1)、(x2,y2)、...、(xn,yn)格式的数据,这些数组元素控制对Bitmap位图的扭曲效果。
vertOffset:控制verts数组中从第几个数组元素开始才对bitmap进行扭曲(忽略verOffset之前数据的扭曲效果)
使用Shader填充图形
通过Paint.setShader(Shader s)方法,给画笔设置渲染效果——Android不仅可以使用颜色填充图形,也可以使用Shader对象指定的渲染效果来填充图形。Shader本身是一个抽象类,它提供了如下实现类:
BitmapShader:使用位图平铺的渲染效果。
LinearGradient:使用线性渐变来填充图形。
RadialGradient:使用圆形渐变来填充图形。
SweepGradient:使用角度渐变来填充图形。
ComposeShader:使用组合渲染效果来填充图形。

4、动画

帧(Frame)动画

AnimationDrawable

使用

<?xml version="1.0" encoding="utf-8"?>
<animation-list xmlns:android="http://schemas.android.com/apk/res/android"
                android:oneshot="false">
    <item
        android:drawable="@mipmap/ic_launcher"
        android:duration="30"/>
    <item
        android:drawable="@mipmap/ic_launcher"
        android:duration="30"/>
    <item
        android:drawable="@mipmap/ic_launcher"
        android:duration="30"/>
</animation-list>

android:oneshot:控制该动画是否循环播放,true表示不会循环播放,否则将会循环播放
也可以使用Java代码定义所有关键帧,需要先创建AnimationDrawable对象,通过addFrame()向该动画添加关键帧。

 // Load the ImageView that will host the animation and 
 // set its background to our AnimationDrawable XML resource. 
 ImageView img = (ImageView)findViewById(R.id.spinning_wheel_image);
 img.setBackgroundResource(R.drawable.spin_animation);

 // Get the background, which has been compiled to an AnimationDrawable object. 
 AnimationDrawable frameAnimation = (AnimationDrawable) img.getBackground();

 // Start the animation (looped playback by default). 
 frameAnimation.start();

AnimationDrawable提供了两个方法控制动画的播放
start();
stop();

补间(Tween)动画

补间动画就是指开发者只需指定动画开始、结束等”关键帧“,而动画变化的”中间帧“由系统计算并补齐。

Tween动画和Interpolator
AlphaAnimation:
ScaleAnimation:
TranslateAnimation:
RotateAnimation:

    一旦为补间动画指定了三个必要信息,Android就会根据动画的开始帧、结束帧、动画持续时间计算出需要在中间"补入"多少帧,并计算所有补入帧的图形。当用户浏览补间动画时,他眼中看到的依然是"帧动画"。
    为了控制在动画期间需要动态"补入"多少帧,具体在动画运行的哪些时刻补入帧,需要借助于Interpolator。
    Interpolator:差值器。根据特定算法计算出整个动画所需要动态插入帧的密度。简单来说就是控制动画的变化速度,使基本的动画效果(Alpha,Translate,Scale,Rotate)能以匀速变换、加速、减速、抛物线速度等各种速度变化。
    Interpalator是一个接口,它定义了所有Interpolator需要实现的方法:float getInterpolation(float input),开发者完全通过实现Interpolator来控制动画的变化速度。
    LinearInterpolator:动画匀速改变
    AccelerateInterpolator:动画开始的地方改变速度较慢,然后开始加速
    AccelerateDecelerateInterpolator:在动画开始、结束的地方改变速度较慢,在中间时候加速。
    CycleInterpolator:动画循环播放特定的次数,变化速度按正弦曲线改变
    DecelerateInterpolator:在动画开始的房速度较快,然后开始一减速

    这些属性可以再资源文件中定义:android:interpolator属性
    android定义了默认支持的Interpolator:@android:anim/linear_interpolator ...
自定义补间动画
自定义补间动画,需要基础Animation,重写抽象基类的appleTransformation(float interpolatedTime,Transformation t)方法,
    InterpolatedTime:代表了动画的时间进行比。不管动画实际的持续事件如何,当动画播放时,该参数总是自动从0变化到1.
    Transformation:代表了补间动画在不同时刻对图形或组件的变化程度。

属性动画

Animator:它提供创建属性的动画的基类;
ValueAnimator(继承Animator):
ObjectAnimator(继承自ValueAnimator):
AnimatorSet(继承Animator):
除此之外,属性动画还需要利用一个Evaluation(计算器),该工具类控制属性动画如何计算属性值。
IntEvaluator:
FloatEvaluator:
ArgbEvaluator:
TypeEvaluator:

ValueAnimator va = ValueAnimator.ofFloat(0f,1f);
va.setDuration(1000);
va.start()
如果希望使用ValueAnimator创建动画,还需要注册一个监听器:AnimatorUpdateListener,该监听器负责更新对象的属性值,

ObjectAnimator oa = ObjectAnimator.ofFloat(foo,"alpha",0f,1f);
oa.setDuration(1000);
oa.start();
ObjectAnimator继承了ValueAnimator,因此它可以直接将ValueAnimator在动画过程中计算出来的值应用到指定的对象的制定的属性上,即不用注册监听器了。

使用ObjectAnimator要注意的:
    要为该对象对应的属性提供setter方法,如上例中的foo对象提供setAlpha(float value);
    调用ObjectAnimator的ofInt()、ofFloat()、ofObject()工厂方法时,如果value... 参数只提供了一个值(本来需要提供开始值和结束值),那么该值会被认为是结束值,该对象应该为该属性提供一个getter方法,该getter方法的返回值被作为开始值。
    如果动画对象的View,为了能显示动画效果,可能还需要在onAnimationUpdate()事件监听方法中调用View.invalidate()方法来刷新屏幕的显示。

使用SurfaceView实现动画

View绘图机制存在如下缺陷:
    View缺乏双缓存机制。
    当程序需要更新View上的图片时,程序必须重绘View上显示的整张图片。
    新线程无法直接更新View组件

特别是在游戏相关方面,一般使用SurfaceView替代View

SurfaceView的绘图机制

    SurfaceView一般会与SurfaceHolder结合使用,SurfaceView用于向与之关联的SurfaceView上绘图,调用SurfaceView的getHolder()方法即可获取SurfaceView关联的SurfaceHolder。
    SurfaceHolder提供如下两个方法获取Canvas对象:
        Canvas lockCanvas():锁定整个SurfaceView对象,获取该SurfaceView上的Canvas
        Canvas lockCanvas(Rect dirty):锁定SurfaceView上Rect划分的区域,获取该SurfaceView上的Canvas

    当同一个SurfaceView调用上面两个方法是,两个方法返回的是同一个Canvas对象。但当程序调用第二个方法获取指定区域的Canvas时,SurfaceView将只对Rect所“圈”出来的区域进行更新,通过这种方式可以提高画面的更新速度。
    当通过LockCanvas()获取指定SurfaceView上的Canvas之后,接下来程序就可以调用Canvas进行绘图了,Canvas绘图完成后通过如下方法来释放绘图、提交所绘制的图形:
        unlockCanvasAndPost(canvas)
    Note:当调用SurfaceHolder的unlockCanvasAndPost(canvas)方法后,该方法之前所绘制的图形还处于缓冲区内,下一次lockCanvas()方法锁定的区域可能会“遮挡”它

SurfaceHolder.Callback

SurfaceView 与普通View一个重要区别:View的绘图必须在UI线程中进行,当SurfaceView就不会,因为SurfaceView的绘图是由SurfaceHolder完成的。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值