转载请注明出处,谢谢~~
目录
上一篇博客我们讲解了Android属性动画的一些基础使用,主要是使用属性动画的几种加载方式,有ObjectAnimator方式,ValueAnimator方式,AnimatorSet方式以及AnimatorInflater方式。这篇博客我们将继续介绍一下Android一些其他执行属性动画的方式,和一些需要注意的点,本文可以看做是对上一篇的一个补充说明,也可以当做是对属性动画使用的进阶。
这篇文章将要说明以下几个问题:
在Android API 12之后,View增加了一个方法,View.animate(),使用这个方法,就可以直接对这个view执行属性动画,我们看下代码:
@SuppressLint("NewApi")
private void findView() {
tv_rotate = (TextView) findViewById(R.id.tv_rotate);
iv_view = (ImageView) findViewById(R.id.iv_view);
tv_rotate.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
//API 12
iv_view.animate()
.rotationX(180.f)
.alpha(0.5f)
.scaleX(0.5f)
.scaleY(0.5f)
.setDuration(2000)
.setInterpolator(new LinearInterpolator())
.setListener(new /*API 11*/AnimatorListenerAdapter() {
@Override
public void onAnimationStart(Animator animation) {}
@Override
public void onAnimationRepeat(Animator animation) {}
@Override
public void onAnimationEnd(Animator animation) {
Log.e(TAG, "animation end");
// API 11
iv_view.setAlpha(1.0f);
iv_view.setRotationX(0.f);
}
@Override
public void onAnimationCancel(Animator animation) {
onAnimationEnd(animation);
}
})
//API 16
.withStartAction(new Runnable() {
@Override
public void run() {
Log.e(TAG, "with start action");
}
})
//API 16
.withEndAction(new Runnable() {
@Override
public void run() {
Log.e(TAG, "with end action");
//API 11
iv_view.setScaleX(1.0f);
iv_view.setScaleY(1.f);
}
})
.start();
// .setStartDelay(5000)//设置延迟时间
}
});
}
在代码里,需要的API等级我都标示的很清楚,有一些方法甚至是在API16中才加入的。我们看下效果:
然后看下我们打的log是不是执行了
03-24 22:35:24.737: E/View.animate(964): with start action 03-24 22:35:26.757: E/View.animate(964): animation end 03-24 22:35:26.757: E/View.animate(964): with end action
可以看到我们的log都执行了,设置了这些属性之后我么就可以在动画执行前或是执行后做一些操作了。
Evaluator是求值器,评估器,也有人叫他估值器,是计算一定变化范围内,某一个时段的一个值,这个值在变化两端之间(包含首尾)。我们接下来举几个例子,从最简单的开始:
public static void startValue2(final View view){
ValueAnimator anim = new ValueAnimator();
anim.setDuration(1000);
anim.setObjectValues(0.0f,360.0f);
anim.setEvaluator(new FloatEvaluator());
anim.start();
anim.addUpdateListener(new AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
view.setRotationX((Float)animation.getAnimatedValue());
}
});
}
这是通过ValueAnimator使用估值器的一个例子。效果很简单,也是以前实现的一个效果,只是实现的方式不同:
使用估值器的例子还可以看我以前的一个自定义控件,它是单独的把估值器抽出去,然后用估值器的值来设置一些属性的变化,有点类似属性动画的思想。传送门–自定义控件之背景变换
还有一种估值器是自定义估值器,这种方式说起来也不算复杂,只是你自己根据需求定义一个估值器来改变你想改变的属性
public static void startValue(final View view){
ValueAnimator valueAnimator = new ValueAnimator();
valueAnimator.setDuration(2000);
valueAnimator.setObjectValues(new PointF(0, 0));
valueAnimator.setInterpolator(new LinearInterpolator());
valueAnimator.setEvaluator(new TypeEvaluator<PointF>(){
// fraction = t / duration 取值范围0-1,就是0-2000取值包含头尾除以2000.
@Override
public PointF evaluate(float fraction, PointF startValue,PointF endValue){
PointF point = new PointF();
point.x = 200 * fraction * 3;
point.y = 0.5f * 200 * (fraction * 3) * (fraction * 3);
return point;
}
});
valueAnimator.start();
valueAnimator.addUpdateListener(new AnimatorUpdateListener(){
@Override
public void onAnimationUpdate(ValueAnimator animation){
PointF point = (PointF) animation.getAnimatedValue();
view.setX(point.x);
view.setY(point.y);
}
});
}
这段代码是我从别人那要的,非原创,嘿嘿,懒得写这块了,因为没有合适的需求。效果图我就不上了,这块只谈如何使用吧。
这是时间插值器,怎么理解呢?就是你的动画的变化规则与速率,例如前边代码里见过的LinearInterpolator,这是线性插值器,也就是你的动画的变化是匀速的,就像你做一个位移动画,前后位移的速度都一样,是匀速运动,而如果你设置了AccelerateInterpolator,这就是加速插值器,你的位移就是加速的,非匀速运动。Android给提供了一些插值器,个人认为,完全够用了。对于这些插值器你可能不是太了解,本文就做一个一一对应。
AccelerateDecelerateInterpolator:
AnticipateOvershootInterpolator:
这些图都是来自网络的搜集,有些只是描述了一个大致的走向,我么可以根据走向选择我们需要的插值器。
这个是对界面布局的一些操作,我们看看代码:
/** * 根据传递过来的动画类型返回相应的动画,或者返回自定义动画 * @param transition * @param anim * @param type * @return */
@SuppressLint("NewApi")
public static Animator getTransition(LayoutTransition transition ,Animator anim , int type){
Animator animator = null;
if (anim == null) {
if (type == LayoutTransition.APPEARING) {
animator = transition.getAnimator(LayoutTransition.APPEARING);
}else if (type == LayoutTransition.CHANGE_APPEARING) {
animator = transition.getAnimator(LayoutTransition.CHANGE_APPEARING);
}else if (type == LayoutTransition.DISAPPEARING) {
animator = transition.getAnimator(LayoutTransition.DISAPPEARING);
}else if (type == LayoutTransition.CHANGE_DISAPPEARING) {
animator = transition.getAnimator(LayoutTransition.CHANGE_DISAPPEARING);
}else {
animator = transition.getAnimator(LayoutTransition.CHANGING);
}
}else{
animator = anim;
}
return animator;
}
我们看几个属性:
- LayoutTransition.APPEARING - view出现时,view本身的动画
- LayoutTransition.CHANGE_APPEARING - view出现时,其他view的动画(如果对其他view的布局有影响的话)
- LayoutTransition.DISAPPEARING - view消失时,view本身的动画
- LayoutTransition.CHANGE_DISAPPEARING - view消失时,其他view的动画(如果对其他view的布局有影响的话)
更加具体是使用去看代码里的调用吧,写的都很明白。这些动画可以用默认的,也可以完全自定义。
1.前边说过了,属性动画是根据view的一些属性来执行动画,那么它要改变这些属性,必须要有对应的方法,例如改变背景,要有setBackgroundColor方法,我么就可以对backgroundColor执行动画。那么如果碰到了没有set方法的,怎么办,或者set方法不是改变对应属性而是设置最大最小值(典型的button),我们来看看这样的情况该怎么解决。
第一种方式,定义Wrapper,装饰我们要控制的view。
public class ViewWrapper {
private View mView;
public ViewWrapper(View view) {
mView = view;
}
public int getWidth() {
return mView.getLayoutParams().width;
}
public void setWidth(int width) {
mView.getLayoutParams().width = width;
mView.requestLayout();
}
public View getView(){
return mView;
}
}
我们重写下这个view的get和set方法,这样就完成了对该属性的改变。
看下效果:
第二种方式,监听器改变任意属性。
/** * 通过ValueAnimator和一个估值器来改变button的宽度。由于button的setWidth不是设置当前宽度,而是设置最小和最大宽度(继承自TextView),所以我们要 * 用view.getLayoutParams().width方式来改变宽度,然后强制重绘界面。 * @param view * @param start * @param end */
@SuppressLint("NewApi")
public static void changeWidthByValue(final View view,final int start, final int end){
ValueAnimator valueAnimator = ValueAnimator.ofFloat(0, 1);
valueAnimator.addUpdateListener(new AnimatorUpdateListener() {
private IntEvaluator mEvaluator = new IntEvaluator();
@Override
public void onAnimationUpdate(ValueAnimator animator) {
float currentValue = (Float)animator.getAnimatedValue();
view.getLayoutParams().width = mEvaluator.evaluate(currentValue, start, end);
view.requestLayout();
}
});
valueAnimator.setDuration(2000).start();
}
这是通过监听器来改变button对应是属性,其中也加入了估值器,这种方式与上一种方式实现的效果相同,但实现方式不同,最终思想确实统一的,看下效果:
【注意】
在上面不知道你有没有注意到,我们不管用哪种方式,都调用了requestLayout()方法,这是因为我们需要通过重绘界面(重测)来实现对属性值的改变。在一些不会自己更新的属性里,我们都需要调用强制重绘方法来实现对属性的改变,这也是自定义空间中经常用到的方式。
2.对动画执行结束后的一些操作——监听器
有时候我们执行动画,需要在执行后让view消失掉,这就需要对动画执行后的一些监听,前边都用到了一些监听器,现在再说两种。
/** * 通过一个包装类来实现对button的改变,set方法里需要重绘界面,动画执行完毕后可以让view消失掉 * @param view * @param from * @param to */
public static void startAnim(final ViewWrapper view,int from , int to , boolean isDisappear){
ObjectAnimator animator = ObjectAnimator.ofInt(view, "width", from, to);
animator.setDuration(2000);
if (isDisappear) {
animator.addListener(new AnimatorListener() {
@Override
public void onAnimationStart(Animator animation) {
// do nothing
}
@Override
public void onAnimationRepeat(Animator animation) {
// do nothing
}
@Override
public void onAnimationEnd(Animator animation) {
ViewGroup parent = (ViewGroup) view.getView().getParent();
if (parent != null)
parent.removeView(view.getView());
}
@Override
public void onAnimationCancel(Animator animation) {
onAnimationEnd(animation);
}
});
}
animator.start();
}
然后我们发现,这个监听器需要复写的方法也太多了,有没有类似我们学适配器的时候,那种可以少写几个方法,或者自己选择想复写哪个的监听器呢?答案是肯定的:
/** * 动画执行完毕后可以让view消失掉,使用一个较为简单的监听器 * @param view * @param from * @param to */
public static void startAnimAndDisappear(final View view,float from , float to){
ObjectAnimator animator = ObjectAnimator.ofFloat(view, "alpha", from, to);
animator.setDuration(2000);
animator.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
ViewGroup parent = (ViewGroup) view.getParent();
if (parent != null) {
parent.removeView(view);
}
}
});
animator.start();
}
这个监听器就自由的多了,我们可以根据需求选择复写哪个方法。同样看下效果:
关于Android属性动画的介绍就告一段落了,感觉使用上应该说的很详细,至于更多的扩展,只有读者自己慢慢发掘了,多想,多试验,总能做出好东西来的。大家一起加油~!
Demo下载地址