读书简记3-《Android开发艺术探索》第六、第七章

第六章 Android的drawable

6.1Drawable简介

1.drawable概念

Drawable有很多种,它们都表示一种图像的概念,但是它们又不全是图片,通过颜色也可以构造出各式各样的图像的效果。在实际开发中,Drawable常被用来作为View的背景使用。Drawable一般都是通过XML来定义的,当然我们也可以通过代码来创建具体的Drawable对象。
在Android的设计中,Drawable是一个抽象类,它是所有Drawable对象的基类,每个具体的Drawable都是它的子类,比如ShapeDrawable、BitmapDrawabl

2.drawble的宽高
rawable的内部宽/高这个参数比较重要,通过getIntrinsicWidth和getIntrinsicHeight这两个方法可以获取到它们。但是并不是所有的Drawable都有内部宽/高,比如一张图片所形成的Drawable,它的内部宽/高就是图片的宽/高,但是一个颜色所形成的Drawable,它就没有内部宽/高的概念。另外需要注意的是,Drawable的内部宽/高不等同于它的大小,一般来说,Drawable是没有大小概念的,当用作View的背景时,Drawable会被拉伸至View的同等大小。

6.2 Drawable的分类

1.drawable的种类

drawable的种类繁多,常见的有BitmapDrawable、ShapeDrawable、LayerDrawable以及StateListDrawable

2.BitmapDrawable和NinePatchDrawable

表示的就是一张图片。在实际开发中,我们可以直接引用原始的图片即可,但是也可以通过XML的方式来描述它,通过XML来描述的BitmapDrawable可以设置更多的效果

NinePatchDrawable,它表示的是一张.9格式的图片,.9图片可以自动地根据所需的宽/高进行相应的缩放并保证不失真,之所以把它和BitmapDrawable放在一起介绍是因为它们都表示一张图片。和BitmapDrawable一样,在实际使用中直接引用图片即可,但是也可以通过XML来描述

3.shapeDrawable
ShapeDrawable是一种很常见的Drawable,可以理解为通过颜色来构造的图形,它既可以是纯色的图形,也可以是具有渐变效果的图形

4.LayerDrawable
LayerDrawable对应的XML标签是,它表示一种层次化的Drawable集合,通过将不同的Drawable放置在不同的层上面从而达到一种叠加后的效果
一个layer-list中可以包含多个item,每个item表示一个Drawable。Item的结构也比较简单,比较常用的属性有android:top、android:bottom、android:left和android:right,它们分别表示Drawable相对于View的上下左右的偏移量,单位为像素

5.StateListDrawable
StateListDrawable对应于标签,它也是表示Drawable集合,每个Drawable都对应着View的一种状态,这样系统就会根据View的状态来选择合适的Drawable。StateListDrawable主要用于设置可单击的View的背景,最常见的是Button.

6.LevelListDrawable
LevelListDrawable对应于标签,它同样表示一个Drawable集合,集合中的每个Drawable都有一个等级(level)的概念。根据不同的等级,LevelListDrawable会切换为对应的Drawable

7.TransitionDrawable
TransitionDrawable对应于标签,它用于实现两个Drawable之间的淡入淡出效果

8.InsetDrawable
InsetDrawable对应于标签,它可以将其他Drawable内嵌到自己当中,并可以在四周留出一定的间距。当一个View希望自己的背景比自己的实际区域小的时候,可以采用InsetDrawable来实现,同时我们知道,通过LayerDrawable也可以实现这种效果。

9.ScaleDrawable
ScaleDrawable对应于标签,它可以根据自己的等级(level)将指定的Drawable缩放到一定比例

10.ClipDrawable
ClipDrawable对应于标签,它可以根据自己当前的等级(level)来裁剪另一个Drawable,裁剪方向可以通过android:clipOrientation和android:gravity这两个属性来共同控制

6.3 自定义Drawable

1.Drawable的使用

Drawable的使用范围很单一,一个是作为ImageView中的图像来显示,另外一个就是作为View的背景,大多数情况下Drawable都是以View的背景这种形式出现的。

2.Drawable的工作原理

Drawable的工作原理很简单,其核心就是draw方法。在第5章中,我们分析了View的工作原理,我们知道系统会调用Drawable的draw方法来绘制View的背景,从这一点我们明白,可以通过重写Drawable的draw方法来自定义Drawable。

2.自定义Drawable

通常我们没有必要去自定义Drawable,这是因为自定义的Drawable无法在XML中使用,这就降低了自定义Drawable的使用范围

补充:

自定义Drawable时,需要继承Drawable类。draw、setAlpha、setColorFilter和getOpacity这几个方法都是必须要实现的。另外getIntrinsicWidth和getIntrinsicHeight这两个方法需要注意一下,当自定义的Drawable有固有大小时最好重写这两个方法,因为它会影响到View的wrap_content布局

第七章 Android动画深入分析

7.1View动画

1.Android动画简述

Android的动画可以分为三种:View动画、帧动画和属性动画。

view动画通过对场景里的对象不断做图像变换(平移、缩放、旋转、透明度)从而产生动画效果,它是一种渐近式动画,并且View动画支持自定义。帧动画通过顺序播放一系列图像从而产生动画效果,可以简单理解为图片切换动画,很显然,如果图片过多过大就会导致OOM。属性动画通过动态地改变对象的属性从而达到动画效果,属性动画为API 11的新特性,在低版本无法直接使用属性动画

2.View动画的种类

View动画的作用对象是View,它支持4种动画效果,分别是平移动画、缩放动画、旋转动画和透明度动画。View动画的四种变换效果对应着Animation的四个子类:TranslateAnimation、ScaleAnimation、RotateAnimation和AlphaAnimation。这四种动画既可以通过XML来定义,也可以通过代码来动态创建,对于View动画来说,建议采用XML来定义动画,这是因为XML格式的动画可读性更好

3.View动画的定义

要使用View动画,首先要创建动画的XML文件,这个文件的路径为:res/anim/filename. xml。

<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
    android:shareInterpolator="true">
    <alpha
        android:fromAlpha="float"
        android:toAlpha="float" />
    <scale
        android:fromXScale="float"
        android:toXScale="float"
        android:fromYScale="float"
        android:toYScale="float"
        android:pivotX="float"
        android:pivotY="float" />
    <translate
        android:fromXDelta="float"
        android:toXDelta="float"
        android:fromYDelta="float"
        android:toYDelta="float" />
    <rotate
        android:fromDegrees="float"
        android:toDegrees="float"
        android:pivotX="float"
        android:pivotY="float" />
</set>

android:shareInterpolator 表示集合中的动画是否和集合共享同一个插值器。如果集合不指定插值器,那么子动画就需要单独指定所需的插值器或者使用默认值

下面是各个具体的动画类,还有他们的值

  • 标签标示平移动画,对应TranslateAnimation类,它可以使一个View在水平和竖直方向完成平移的动画效果

    android:fromXDelta——表示x的起始值,比如0;

    android:toXDelta——表示x的结束值,比如100;

  • 标签表示缩放动画,对应ScaleAnimation,它可以使View具有放大或者缩小的动画效果。(默认情况下轴点是View的中心点,这个时候在水平方向进行缩放的话会导致View向左右两个方向同时进行缩放,但是如果把轴点设为View的右边界,那么View就只会向左边进行缩放)

    android:fromXScale——水平方向缩放的起始值,比如0.5;

    android:toXScale——水平方向缩放的结束值,比如1.2;

    android:pivotX——缩放的轴点的x坐标,它会影响缩放的效果;

  • 标签表示旋转动画,对于RotateAnimation,它可以使View具有旋转的动画效果

    (在旋转动画中,轴点扮演着旋转轴的角色,即View是围绕着轴点进行旋转的,默认情况下轴点为View的中心点)

    android:fromDegrees——旋转开始的角度,比如0;

    android:toDegrees——旋转结束的角度,比如180;

  • 标签表示透明度动画,对应AlphaAnimation,它可以改变View的透明度\

    android:fromAlpha——表示透明度的起始值,比如0.1;·

    android:toAlpha——表示透明度的结束值,比如1。

其他常用属性补充:

android:duration——动画的持续时间;

android:fillAfter——动画结束以后View是否停留在结束位置,true表示View停留在结束位置,false则不停留

4.View动画的使用

利用上面定义的anim文件,开启动画的代码

 Button mButton = (Button) findViewById(R.id.button1);    
 Animation animation = AnimationUtils.loadAnimation(this, R.anim.animation_    test);    // 从资源中加载动画
 mButton.startAnimation(animation); // 开启动画

除了使用xml,还可以直接在代码中创建

AlphaAnimation alphaAnimation = new AlphaAnimation(0, 1);  // 透明度变化  alphaAnimation.setDuration(300);    // 设置动画持续时间
mButton.startAnimation(alphaAnimation); // 开启动画

通过Animation的setAnimationListener方法可以给View动画添加过程监听,接口如下所示:

 public static interface AnimationListener {            
        void onAnimationStart(Animation animation);            
        void onAnimationEnd(Animation animation);            
        void onAnimationRepeat(Animation animation);        }

5.自定义动画

自定义动画其实比较复杂,因为涉及到矩阵变换的很多细节。派生一种新动画只需要继承Animation这个抽象类,然后重写它的initialize和applyTransformation方法,在initialize方法中做一些初始化工作,在applyTransformation中进行相应的矩阵变换即可,很多时候需要采用Camera来简化矩阵变换的过程

补充:

自定义动画涉及到的类 Camera 和 Matrix

6.帧动画 - AnimationDrawable

帧动画是顺序播放一组预先定义好的图片,类似于电影播放。不同于View动画,系统提供了另外一个类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="@drawable/image1" android:duration="500" />
    <item android:drawable="@drawable/image2" android:duration="500" />
    <item android:drawable="@drawable/image3" android:duration="500" />
</animation-list>

android:onetrue–表示动画是否只播放一遍

下面的item就是每一帧动画

下面是将当前帧动画作为View的背景来使用的代码:

Button mButton = (Button)findViewById(R.id.button1);        mButton.setBackgroundResource(R.drawable.frame_animation);  // 正常当作drawable来使用      
AnimationDrawable drawable = (AnimationDrawable) mButton.getBackground();   // 从背景中获取动画
drawable.start(); // 开启动画

注意:

帧动画需要定义在drawable中,而不是anim目录中

帧动画的使用比较简单,但是比较容易引起OOM,所以在使用帧动画时应尽量避免使用过多尺寸较大的图

7.2 View动画的特殊使用场景

1.View动画的特殊使用

View动画还可以在一些特殊的场景下使用,比如在ViewGroup中可以控制子元素的出场效果,在Activity中可以实现不同Activity之间的切换效果。

2.LayoutAnimation简介和使用:

LayoutAnimation作用于ViewGroup,为ViewGroup指定一个动画,这样当它的子元素出场时都会具有这种动画效果。这种效果常常被用在ListView上

LayoutAnimation的使用:

  • (1)定义LayoutAnimatio

    <?xml version="1.0" encoding="utf-8"?>
    <layoutAnimation xmlns:android="http://schemas.android.com/apk/res/android"
        android:delay="0.5"
        android:animationOrder="normal"
        android:animation="@anim/animationone">
    </layoutAnimation>
    

    android:delay 表示子元素开始动画的时间延迟,比如子元素入场动画的时间周期为300ms,那么0.5表示每个子元素都需要延迟150ms才能播放入场动画

    android:animationOrder 表示子元素动画的顺序,有三种选项:normal、reverse和random,其中normal表示顺序显示,即排在前面的子元素先开始播放入场动画;reverse表示逆向显示,即排在后面的子元素先开始播放入场动画;random则是随机播放入场动画。

    android:animation 为子元素指定具体的入场动画

  • (2) 实现子元素的入场动画

  • (3)为ViewGroup指定android:layoutAnimation属android:layoutAnimation = “@anim/ anim_layout”。

除了在XML中指定LayoutAnimation外,还可以通过LayoutAnimationController来实现,具体代码如下所示。

 ListView listView = (ListView) layout.findViewById(R.id.list);        Animation animation = AnimationUtils.loadAnimation(this, R.anim.anim_        item);    // 加载需要使用的子元素入场动画    
LayoutAnimationController controller = new LayoutAnimationController        (animation); // 获取controller       
controller.setDelay(0.5f);  // 设置动画延迟      
controller.setOrder(LayoutAnimationController.ORDER_NORMAL); // 设置播放顺序   
listView.setLayoutAnimation(controller);  // 为viewgroup设置layout-anim

3.Activity的切换效果

Activity有默认的切换效果,但是这个效果我们是可以自定义的,主要用到overridePending-=Transition(int enterAnim, int exitAnim)这个方法,这个方法必须在startActivity(Intent)或者finish()之后被调用才能生效 (对应开启和关闭)

  public void overridePendingTransition(int enterAnim, int exitAnim)

enterAnim——Activity被打开时,所需的动画资源id;·

exitAnim——Activity被暂停时,所需的动画资

注意:

overridePendingTransition这个方法必须位于startActivity或者finish的后面,否则动画效果将不起作用。

4.Fragment的切换效果

Fragment也可以添加切换动画,我们可以通过FragmentTransaction中的setCustom-Animations() 方法来添加切换动画。这个切换动画需要使用View动画

 public FragmentTransaction setCustomAnimations(@AnimatorRes @AnimRes int enter,
            @AnimatorRes @AnimRes int exit)

7.3 属性动画

1.属性动画简介

属性动画是API 11新加入的特性,和View动画不同,它对作用对象进行了扩展,属性动画可以对任何对象做动画,甚至还可以没有对象。除了作用对象进行了扩展以外,属性动画的效果也得到了加强,不再像View动画那样只能支持四种简单的变换。属性动画中有ValueAnimator、ObjectAnimator和AnimatorSet等概念,通过它们可以实现绚丽的动画。

属性动画可以对任意对象的属性进行动画而不仅仅是View,动画默认时间间隔300ms,默认帧率10ms/帧。其可以达到的效果是:在一个时间间隔内完成对象从一个属性值到另一个属性值的改变。

2.属性动画的使用

比较常用的几个动画类是:ValueAnimatorObjectAnimatorAnimatorSet,其中ObjectAnimator继承自ValueAnimator,AnimatorSet是动画集合,可以定义一组动画

使用例子:

  • (1) 改变一个对象(myObject)的translationY属性,让其沿着Y轴向上平移一段距离:它的高度。

    ObjectAnimator.ofFloat(myObject, "translationY", -myObject.getHeight()).        start();
    
  • (2)改变一个对象的背景色属性,典型的情形是改变View的背景色

            ValueAnimator colorAnim = ObjectAnimator.ofInt(this, "backgroundColor",        /Red/0xFFFF8080, /Blue/0xFF8080FF);     // 改变属性   
    colorAnim.setDuration(3000);        
    colorAnim.setEvaluator(new ArgbEvaluator());        colorAnim.setRepeatCount(ValueAnimator.INFINITE);        colorAnim.setRepeatMode(ValueAnimator.REVERSE);        
    colorAnim.start();
    
  • (3)动画集合,5秒内对View的旋转、平移、缩放和透明度都进行了改变

            AnimatorSet set = new AnimatorSet();        
    set.playTogether(  
     ObjectAnimator.ofFloat(myView, "rotationX", 0, 360),            ObjectAnimator.ofFloat(myView, "rotationY", 0, 180),            ObjectAnimator.ofFloat(myView, "rotation", 0, -90),            ObjectAnimator.ofFloat(myView, "translationX", 0, 90),            ObjectAnimator.ofFloat(myView, "translationY", 0, 90),            ObjectAnimator.ofFloat(myView, "scaleX", 1, 1.5f),            ObjectAnimator.ofFloat(myView, "scaleY", 1, 0.5f),            ObjectAnimator.ofFloat(myView, "alpha", 1, 0.25f, 1)        );        set.setDuration(51000).start();
    

除此之前,还可以通过xml来定义

在XML中可以定义ValueAnimator、Object-Animator以及AnimatorSet。

  • 标签对应AnimatorSet

    android:ordering属性有两个可选值:“together”和“sequentially”,其中“together”表示动画集合中的子动画同时播放,“sequentially”则表示动画集合中的子动画按照前后顺序依次播放,android:ordering属性的默认值是“together”。

  • 标签对应ValueAnimator

    animator的属性和objectAnimator基本一样,它只是比少了一个android: property Name属性而已

  • 则对应ObjectAnimator

    android:propertyName——表示属性动画的作用对象的属性的名称;

    android:duration——表示动画的时长;

    android:valueFrom——表示属性的起始值;

    android:valueTo——表示属性的结束值;

    android:startOffset——表示动画的延迟时间,当动画开始后,需要延迟多少毫秒才会真正播放此动画;· android:repeatCount——表示动画的重复次数,默认值为0,-1表示哦无限循环

    android:repeatMode——表示动画的重复模式;有两个选项:“repeat”和“reverse”,分别表示连续重复和逆向重复

    android:valueType——表示android:propertyName所指定的属性的类型,有“intType”和“floatType”两个可选项,分别表示属性的类型为整型和浮点型。另外,如果android:propertyName所指定的属性表示的是颜色,那么不需要指定android:valueType,系统会自动对颜色类型的属性做处理。

使用xml文件定义属性动画后的使用:

        AnimatorSet set = (AnimatorSet) AnimatorInflater.loadAnimator(myContext,        R.anim.property_animator);        
        set.setTarget(mButton);  // 设置目标对象      
        set.start();  // 开启动画

实际开发中建议采用代码来实现属性动画,这是因为通过代码来实现比较简单。更重要的是,很多时候一个属性的起始值是无法提前确定的

3.理解插值器和估值器

TimeInterpolator中文翻译为时间插值器,它的作用是根据时间流逝的百分比来计算出当前属性值改变的百分比。系统预置的有LinearInterpolator(线性插值器:匀速动画)、AccelerateDecelerateInterpolator(加速减速插值器:动画两头慢中间快)和Decelerate-Interpolator(减速插值器:动画越来越慢)等

TypeEvaluator的中文翻译为类型估值算法,也叫估值器,它的作用是根据当前属性改变的百分比来计算改变后的属性值系统预置的有IntEvaluator(针对整型属性)、FloatEvaluator(针对浮点型属性)和ArgbEvaluator(针对Color属性)。

属性动画中的插值器(Interpolator)和估值器(TypeEvaluator)很重要,它们是实现非匀速动画的重要手段

4.自定义插值器和估值器

自定义插值器需要实现Interpolator或者TimeInter-polator,自定义估值算法需要实现TypeEvaluator。另外就是如果要对其他类型(非int、float、Color)做动画,那么必须要自定义类型估值算法。

5.属性动画的监听器

属性动画提供了监听器用于监听动画的播放过程,主要有如下两个接口:AnimatorUpdateListener和AnimatorListener。

AnimatorListener是Animator的内部接口,系统还提供了AnimatorListenerAdapter这个类,它是Animator-Listener的适配器类,这样我们就可以有选择地实现下面的4个方法了

    public static interface AnimatorListener {

        default void onAnimationStart(Animator animation, boolean isReverse) {
            onAnimationStart(animation);
        }

        default void onAnimationEnd(Animator animation, boolean isReverse) {
            onAnimationEnd(animation);
        }

    
        void onAnimationStart(Animator animation);


        void onAnimationEnd(Animator animation);

  
        void onAnimationCancel(Animator animation);

      
  
        void onAnimationRepeat(Animator animation);
    }

下面再看一下AnimatorUpdateListener的定义:

        public static interface AnimatorUpdateListener {            
        void onAnimationUpdate(ValueAnimator animation);        }

AnimatorUpdateListener比较特殊,它会监听整个动画过程,动画是由许多帧组成的,每播放一帧,onAnimationUpdate就会被调用一次

6.属性动画的原理

属性动画要求动画作用的对象提供该属性的get和set方法,属性动画根据外界传递的该属性的初始值和最终值,以动画的效果多次去调用set方法,每次传递给set方法的值都不一样,确切来说是随着时间的推移,所传递的值越来越接近最终值。

假设我们对object的属性abc做动画,需满足以下条件:

(1)object必须要提供setAbc方法,如果动画的时候没有传递初始值,那么还要提供getAbc方法,因为系统要去取abc属性的初始值(如果这条不满足,程序直接Crash)。

(2)object的setAbc对属性abc所做的改变必须能够通过某种方法反映出来,比如会带来UI的改变之类的(如果这条不满足,动画无效果但不会Crash)。

7.属性动画不生效的解决方式

  • 给你的对象加上get和set方法,如果你有权限的话;
  • 用一个类来包装原始对象,间接为其提供get和set方法;·
  • 采用ValueAnimator,监听动画过程,自己实现属性的改变。

8.属性动画工作原理分析

属性动画需要运行在有Looper的线程中。内部会通过反射去调用get和set方法

7.4 使用动画的注意事项

1.需要注意的问题

  • 1.OOM问题 这个问题主要出现在帧动画中,当图片数量较多且图片较大时就极易出现OOM
  • 2.内存泄露 在属性动画中有一类无限循环的动画,这类动画需要在Activity退出时及时停止,否则将导致Activity无法释放从而造成内存泄露
  • 3.兼容性问题
  • 4.View动画的问题 view动画是对View的影像做动画,并不是真正地改变View的状态,因此有时候会出现动画完成后View无法隐藏的现象,即setVisibility(View.GONE)失效了,这个时候只要调用view.clearAnimation()清除View动画即可解决此问题。
  • 5.不要使用px 在进行动画的过程中,要尽量使用dp,使用px会导致在不同的设备上有不同的效果。
  • 6.动画元素的交互 将view移动(平移)后,在Android 3.0以前的系统上,不管是View动画还是属性动画,新位置均无法触发单击事件,同时,老位置仍然可以触发单击事件。尽管View已经在视觉上不存在了,将View移回原位置以后,原位置的单击事件继续生效。从3.0开始,属性动画的单击事件触发位置为移动后的位置,但是View动画仍然在原位置
  • 7.硬件加速 使用动画的过程中,建议开启硬件加速,这样会提高动画的流畅性。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值