自定义控件开发入门与实战

2.3插值器

  • AccelerateInterpolator:在动画开始的地方速率改变比较慢,然后开始加速

  • DecelerateInterpolator:减速插值器,在动画开始的一瞬间加速到最大值

  • LinearInterpolator:线性插值器,也称匀速插值器,速率保持恒定

  • BoubceInterpolator:弹跳插值器,模拟了控件自由落地后回弹的效果

  • AnticipateInterpolator:初始偏移插值器,表示在动画开始的时候向前偏移一段距离,然后应用动画,在旋转动画回先向反方向旋转,对于缩放和透明度渐类推。该插值器有一个构造函数(public AnticipateInterpolator(float tension)),tension表示张力,默认为2,张力越大,初始的偏移量越大,而且数越快。使用无参构造函数时,默认张力是2

  • OvershootInterpolator:结束偏移插值器,如上

  • AnticipateOvershootInterpolator:是AnticipateInterpolator和AnticipateInterpolator的结合体。

  • CycleInterpolator:循环插值器,表示动画循环播放特定的次数,速率沿正弦曲线改变。其构造函数传入的参数表示循环次数

属性动画

3.1 ValueAnimator的基本使用

3.1.1 概述

补间动画和逐帧动画统称为视图动画,这两个动画只能对派生自View的控件实例起作用,属性动画则是作用于控件属性的,他能单独改变控件某一个属性的值,比如颜色。所以属性动画是能够通过改变某一属性值来做动画的。而且补间动画并不能改变空间内部的属性值。(书p69有一个栗子)

3.1.2ValueAnimator的简单使用

  • 这个动画针对的是值,不会对控件执行任何操作,我们可以给他设定从哪那个值到哪个值,通过监听这些值的渐变过程来改变自己

  • ValueAnimator 没有跟任何控件相关联,只对值进行动画运算,而不是针对控件,写个栗子解释一下

    1
    2
    3
    ValueAnimator animator = ValueAnimator.ofInt(0, 400);
    animator.setDuration(1000);
    animator.start();
    • 属性动画的使用栗子,可以改变控件的属性的。给实例设置UpdateListener,然后在里面通过控件实例的layout()方法改变控件的属性值。需注意的layout()方法的改变是永久的
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    public class ValueAnimDemoActivity extends Activity {
    private TextView tv;
    private Button btn;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.translate_anim_activity);

    tv = (TextView) findViewById(R.id.tv);

    btn = (Button) findViewById(R.id.btn);
    btn.setOnClickListener(new View.OnClickListener() {
    public void onClick(View v) {
    doAnimation();
    }
    });

    tv.setOnClickListener(new View.OnClickListener() {
    public void onClick(View v) {
    Toast.makeText(ValueAnimDemoActivity.this, "clicked me", Toast.LENGTH_SHORT).show();
    }
    });
    }

    private void doAnimation(){
    ValueAnimator animator = ValueAnimator.ofInt(0,400);
    animator.setDuration(1000);

    animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
    public void onAnimationUpdate(ValueAnimator animation) {
    int curValue = (Integer)animation.getAnimatedValue();
    //如果前面调用的是ofFloat()方法,那么这里就转化为float型
    tv.layout(curValue,curValue,curValue+tv.getWidth(),curValue+tv.getHeight());
    }
    });
    animator.start();
    }

    }
  • ValueAnimator的常用函数有ofInt()和ofFloat()。这两个函数的参数类型均是可变长参数,可传入任何的数量的值,但是传进去的数值越多,动画变化就越复杂。比如ofInt(2, 90, 45)就表示从数字2变化到90再变化到数字45。 通过getAnimatedValue()函数可以得到当前运动点的值,该函数返回的是一个object对象,转化为Integer型还是float型取决于调用的是ofInt()还是ofFloat()

  • 1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    /**
    * 设置动画时长,单位是毫秒
    */
    ValueAnimator setDuration(long duration);

    /**
    * 获取ValueAnimator在运动时当前运动点的值
    */
    Object getAnimatedValue();
    /**
    * 开始动画
    */
    void start();
    /**
    * 设置循环次数,设置为INFINITE表示无限循环
    * 设置为零则表示不循环
    */
    void setRepeatCount(int value);
    /**
    * 设置循环模式
    * value的取值有RESTART 和 REVERSE
    * RESTART表示正序重新开始
    * REVERSE表示倒序重新开始
    */
    void setRepeatMode(int value);
    /**
    * 取消动画
    */
    void cancel();

3.1.3ValueAnimator监听器

  • 其共有两个监听器

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    /**
    * 监听器一:监听动画过程中值的实时变化
    */

    public static interface AnimatorUpdateListener{
    void onAnimationUpdate(ValueAnimator animation);
    }
    /**
    * 监听器二:监听动画变化时的四个状态
    */

    public static interface AnimatorListener{
    //当动画开始时
    void onAnimationStart(Animator animation);
    //当动画结束时
    void onAnimationEnd(Animator animation);
    //当动画取消时
    void onAnimationCancel(Animtor animation);
    //当动画重复时
    void onAnimationRepeat(Animator animation);
    }

    这里的监听器一主要用于监听过程中值的实时变化,监听器二主要监听Animation的4个状态.

    对于移除监听器,提供全部移除和指定移除(需注意的一点是,移除listener后,并没有取消动画效果,所以动画会继续)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    /**
    * 移除AnimatorUpdateListener
    */

    void removeUpdateListener(AnimatorUpdateListener listener);
    void removeAllUpdateListener();

    /**
    * 移除AnimatorListener
    */

    void removeListener(AnimatorUpdateListener listener);
    void removeAllListener();
  • 一些不常用的函数

    1
    2
    3
    4
    5
    6
    7
    8
    9
    /**
    * 延时多久开始,单位是毫秒
    */
    public void setStartDelay(long startDelay)

    /**
    * 完全克隆一个ValueAnimator实例,包括它所有的设置以及所有对监听器代码的处理
    */
    public ValueAnimator clone()

3.1.4 弹跳加载中效果

实现效果步骤需要考虑的点

  • 控件应该派生自ImageView类,方便更改它的源文件类容
  • 想要实现上下移动,可以先利用valueAnimator实时产生一个0~100的数据值,然后让当前的图片的位置实时向上移动valueAnimator的动态值得高度即可。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
public class LoadingImageView extends ImageView {
private int mTop;
//当前动画图片索引
private int mCurImgIndex = 0;
//动画图片总张数
private static int mImgCount = 3;

public LoadingImageView(Context context) {
super(context);
init();
}

public LoadingImageView(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}

public LoadingImageView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init();
}

@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
super.onLayout(changed, left, top, right, bottom);

mTop = top;
}

private void init() {
ValueAnimator valueAnimator = ValueAnimator.ofInt(0, 100, 0);
valueAnimator.setRepeatMode(ValueAnimator.RESTART);
valueAnimator.setRepeatCount(ValueAnimator.INFINITE);
valueAnimator.setDuration(2000);
valueAnimator.setInterpolator(new AccelerateDecelerateInterpolator());

valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
public void onAnimationUpdate(ValueAnimator animation) {
Integer dx = (Integer) animation.getAnimatedValue();
//在这里通过这个计算出新的坐标,然后设置上去
setTop(mTop - dx);
}
});

valueAnimator.addListener(new Animator.AnimatorListener() {
public void onAnimationStart(Animator animation) {
setImageDrawable(getResources().getDrawable(R.drawable.pic_1));
}

public void onAnimationRepeat(Animator animation) {
mCurImgIndex++;
switch (mCurImgIndex % mImgCount) {
case 0:
setImageDrawable(getResources().getDrawable(R.drawable.pic_1));
break;
case 1:
setImageDrawable(getResources().getDrawable(R.drawable.pic_2));
break;
case 2:
setImageDrawable(getResources().getDrawable(R.drawable.pic_3));
break;
}
}

public void onAnimationEnd(Animator animation) {

}

public void onAnimationCancel(Animator animation) {

}
});


valueAnimator.start();
}
}

3.2自定义插值器与Evaluator

3.2.1自定义插值器

插值器就是用来控制动画的区间如何被计算的

在ValueAnimator中使用插值器,直接调用ValueAnimator。setInterpolator(TimeInterpolator value)函数

  • 系统的插值器的结构层次是

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    Interpolator extends TimeInterpolator 

    //用LinearInterpolator举栗子
    LinearInterpolator extends Interpolator


    public interface TimeInterpolator{
    //input的范围是0~1表示动画进度(动画进度)
    //返回值表示的当前实际想要显示的进度(数值进度)
    float getInterpolation(float input);
    }

    用LinearInterpolator的核心代码做一个栗子

    1
    2
    3
    4
    5
    6
    7
    public class LinearInterpolator implements Interpolator {
    ...

    public float getInterpolation(float input){
    return input;
    }
    }

    在这个栗子里面,直接返回的input,就是以当前的动画进度作为动画的数值进度,这样的话,动画的数值进度就与时间进度相同,所以它是匀速的。

3.2.2Evaluator

一、概述

ofInt(1, 400) (定义动画数值期间) -> 插值器(返回当前数值进度,如0.2) -> Evaluator(根据数值进度计算当前值) -> 监听器返回(在AnimatorUpdateListener中返回)

从定义动画的数值区间到在AnimatorUpdatel iscne得到当前动画所对应数值的整个过程的4个步骤的具体含义如下

  1. ofInt(0,400); 表示指定动画的数值区间,从0运动 到400.

  2. 插值器:在动画开始后,通过插值器会返回当前动画进度所对应的数值进度,但这个数值进度是以小数表示的,如0.2。

  3. Evaluator: 我们通过监听器拿到的是当前动画所对应的具体数值,而不是用小数表示的数值。那么必须有一个地方会根据当前的数值进度将其转换为对应的数值,这个地方就是Evaluator. Evaluator 用于将从插值器返回的数值进度转换成对应的数值。

  4. 监听器返回:我们通过在AnimatorUpdateListener 监听器中使用animation.getAnimatedValue()函数拿到Evaluator中返回的数值。
    讲了这么多,Evaluator 其实就是- 个转换器,它能把小数进度转换成对应的数值位置。

二、各种Evaluator

在传入的时用的ofInt()还是ofFlaot()会对应不同的Evaluator,因为,需要返回整形合浮点型,如果不分开使用,则会出现,强转错误。所以ofInt()函数对应的就是IntEvaluator,ofFloat()类似。使用Evaluator时,使用animator.setEvaluator()函数来实现即可。在不主动提供的情况下,系统会默认提供的。

看一下IntEvaluator的内部时怎么实现的

1
2
3
4
5
6
   public class IntEvaluator implements TypeEvaluator<Integer> {
public Integer evaluate(float fraction, Integer startValue, Integer endValue) {
int startInt = startValue;
return (int)(startInt + fraction * (endValue - startInt));
}
}
  • fraction参数就是插值器中的返回值,表示当前动画的数值进度,以百分制的小数表示

  • startValue和endValue分别对应ofInt(int start, int end)函数中的start和end的数值。假设动画的ofInt(100, 400)数值进度为20%,则fraction为0.2,startValue为100,endValue为400

  • 返回值就是当前数值进度所对应的具体数值,这个数值就是我们在AnimatoeUpdateListener监听器通过fetAnimatedValue()得到的数

  • 当我们自定义Evaluator得时候,需要指定一个泛型,泛型事什么就由你用的ofInt()还是ofFloat()决定

    1
    2
    3
    4
    5
    6
    public class myIntEvaluator implements TypeEvaluator<Integer> {
    @Override
    p public Integer evaluate(float fraction, Integer startValue, Integer endValue{
    //TODO...
    }
    }

结论: 既可以通过重写插值器改变数值进度来改变数值位置,也可以通过改变Evaluator中数值进度所对应的具体数值来改变数值位置

  • 关于ArgbEvaluator

    • 使用ArgbEvaluator:他是用来实现颜色值过渡转换的,使用方法与IntEvaluator和FloatEvaluator没什么差别

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      ValueAnimator animator = ValueAnimator.ofInt(0xffffff000xff0000ff);
      animator.setDuration(3000);
      animator.setEvaluator(new ArgbEvaluator());

      animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
      public void onAnimationUpdate(ValueAnimator animation) {
      Integer curValue = (Integer) animation.getAnimatedValue();
      tv.setBackgroundColor(curValue);
      }
      });
      animatorstart();

      需要注意的是,这里必须使用ofInt()函数来定义颜色的取值范围,并且必须包括A、R、G、B四个值

  • 关于ArgbEvaluator的实现原理

    原理一共分为三个部分:

    1. 根据startValue求出A、R、G、B中各个色彩的初始值
    2. 根据endValue求出A、R、G、B中各个色彩的结束值
    3. 根据当前动画的百分比进度求出相对应的数值
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    public Object evaluate(float fraction, Object startValue, Object endValue) {
    int startInt = (Integer) startValue;
    float startA = ((startInt >> 24) & 0xff) / 255.0f;
    float startR = ((startInt >> 16) & 0xff) / 255.0f;
    float startG = ((startInt >> 8) & 0xff) / 255.0f;
    float startB = ( startInt & 0xff) / 255.0f;

    int endInt = (Integer) endValue;
    float endA = ((endInt >> 24) & 0xff) / 255.0f;
    float endR = ((endInt >> 16) & 0xff) / 255.0f;
    float endG = ((endInt >> 8) & 0xff) / 255.0f;
    float endB = ( endInt & 0xff) / 255.0f;

    // convert from sRGB to linear
    startR = (float) Math.pow(startR, 2.2);
    startG = (float) Math.pow(startG, 2.2);
    startB = (float) Math.pow(startB, 2.2);

    endR = (float) Math.pow(endR, 2.2);
    endG = (float) Math.pow(endG, 2.2);
    endB = (float) Math.pow(endB, 2.2);

    // compute the interpolated color in linear space
    float a = startA + fraction * (endA - startA);
    float r = startR + fraction * (endR - startR);
    float g = startG + fraction * (endG - startG);
    float b = startB + fraction * (endB - startB);

    // convert back to sRGB in the [0..255] range
    a = a * 255.0f;
    r = (float) Math.pow(r, 1.0 / 2.2) * 255.0f;
    g = (float) Math.pow(g, 1.0 / 2.2) * 255.0f;
    b = (float) Math.pow(b, 1.0 / 2.2) * 255.0f;

    return Math.round(a) << 24 | Math.round(r) << 16 | Math.round(g) << 8 | Math.round(b);
    }

3.3ValueAnimator进阶 ——ofObject

3.3.1概述

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
%第三问的求解
clc
C = 0.85;
r = 2.5;
A = pi * r * r;
N = 0.0000001;
V1 = 108.616080206793;
Volume_high_left = 114.758288413902;
b = zeros(1,2);
num = 1;


syms m_after v_after P_after p_after

for w = 0.1:0.1:50

m_forward = 0.85 * 108.6109;
v_forward = 108.6109;
P_forward = 100;
p_forward = 0.85;
for n = 0.082:N:1
Dt = 2 * N * pi / w ;
if(P_forward > 100)
Dp = P_forward - 100;
Dm = C * A * sqrt(p_forward*2*Dp) * Dt;
else
Dm = 0;

end

Dv = -pi * r^2 * w * (2.413 * sin(2*n*pi) + (2.676e-6)*cos(2*n*pi))*Dt;

m_after = m_forward - Dm;
v_after = v_forward + Dv;
p_after = m_after/v_after;
P_after = (tan((log(p_after)+0.252939679498367)/0.1523) - 0.23432)/0.0044061 ;

m_forward = m_after;
v_forward = v_after;
p_forward = p_after;
P_forward = P_after;


if (abs(P_after-100)<=0.01 && n >= 0.5)
disp(P_after)
v_out = V1 - Volume_high_left + (-2.413*cos(2*n*pi) + 2.676e-6*sin(2*n*pi) + 2.413)*pi*r^2;
%写进excel w 和 v_out

nn = strcat('A',num2str(num));
xlswrite('result.xlsx',w,1,nn);
nn = strcat('B',num2str(num));
xlswrite('result.xlsx',v_out,1,nn);
num = num + 1;
break;
end
end
end
%用于拟合弹性模量
origin_data3 = xlsread('result.xlsx','Sheet1');
mx = origin_data3(:,4);
my = origin_data3(:,5);