效果图:

Android动画有3类:

  1.View Animation (Tween Animation) 

  2.Drawable Animation (Frame Animation)

  2.Property Animation

其中,上述效果是用第二类属性动画做的。

  • 什么是属性动画?

  通俗的说,属性动画就是在一定的时间内,按照一定的规律来改变对象的属性(该属性对于该对象应该是从形态(大小,位置等)上可以感受到的),从而是对象展现出动画的效果。

    作用:可以定义动画来改变对象的属性( You can define an animation to change any object property over time, regardless of whether it draws to the screen or not. )

 允许定义的属性(The property animation system lets you define the following characteristics of an animation:)
     1.Duration
     2.Time Interpolation
     3.repeat count and behavior: 重复次数 和 是否回放
     4.animatior set : 一组逻辑动画,可以同时播放、顺序播放、按一定延时播放
     5.Frame refresh delay: 刷新时间,默认10ms
  • 属性动画原理

先来看一个例子:

  首先假设一个对象在屏幕的水平方向上移动,设定它的持续时间为40ms,位移是40像素长度。每10ms(属性动画默认的每帧的刷新率),它的移动距离是10像素长度。最后,在40ms的时候,这个动画结束了,而该对象停留在水平40像素的位置上(相对出发位置),这是一个使用线性插值器(linear interpolation)的例子。

先来看一下,这过程中主要的类之间是如何计算和工作的。

其中:

  ValueAnimator : 用来跟踪动画的时机(The ValueAnimator object keeps track of your animation's timing, such as how long the animation has been running, and the current value of the property that it is animating.)

  TimeInterpolation : 定义了时间插值器,根据特定的值(与动画刷新频率有关)计算出当前属性值改变的百分比(An interpolator define how specific values in an animation are calculated as a function of time.)

  TimeEvaluate:   类型估值算法,它的作用是定义了如何根据当前属性改变的百分比来计算改变后的属性值(defines how to calculate values for the property being animated)

  为了开启一个动画,首先新建一个ValueAnimator 实例并在构造的时候给它提供动画对象的属性的开始值(starting) 和 结束值(ending)的两个值和持续时间。从你调用start()方法开始,在整个动画过程,ValueAnimator 计算 了一个基于动画持续时间和动画流逝的时间的百分值(0到1之间)。这个百分值代表着动画完成的百分比,我们叫它为流逝百分比(流逝时间/总的持续时间)。在刚刚的例子中,显而易见地,在时间t=10ms的时候,流逝百分值应该是0.25(10/40)。

  当 ValueAnimator 计算完该百分比之后,就会提醒 TimeInterpolator去计算当前属性改变的百分比。属性改变的百分比是一个该时刻对应的属性的值与结束值的百分比(对应上例中就是   (某时刻X的值)/40 ),它是一个与流逝百分比相匹配的值。在上述例子中:插入百分比总是与流逝百分比一样,在t=10ms的时候,流逝百分比是0.25(10/40),插入百分比也是0.25(10/40)。

  当插入百分比计算完了之后,ValueAnimator会适当地调用TypeEvaluator基于插入百分比去计算需要改变的属性当前的具体值。对与上例在t=10ms时刻,该值是 0.25*(40-0)=10像素。

  • 重要类

Animatiors

Class Description
ValueAnimator The main timing engine for property animation that also computes the values for the property to be animated. It has all of the core functionality that calculates animation values and contains the timing details of each animation, information about whether an animation repeats, listeners that receive update events, and the ability to set custom types to evaluate. There are two pieces to animating properties: calculating the animated values and setting those values on the object and property that is being animated. ValueAnimator does not carry out the second piece, so you must listen for updates to values calculated by the ValueAnimator and modify the objects that you want to animate with your own logic. See the section about Animating with ValueAnimator for more information.
ObjectAnimator A subclass of ValueAnimator that allows you to set a target object and object property to animate. This class updates the property accordingly when it computes a new value for the animation. You want to use ObjectAnimator most of the time, because it makes the process of animating values on target objects much easier. However, you sometimes want to use ValueAnimator directly because ObjectAnimator has a few more restrictions, such as requiring specific acessor methods to be present on the target object.
AnimatorSet Provides a mechanism to group animations together so that they run in relation to one another. You can set animations to play together, sequentially, or after a specified delay. See the section about Choreographing multiple animations with Animator Sets for more information.

Evaluator:

Class/Interface Description
IntEvaluator The default evaluator to calculate values for int properties.
FloatEvaluator The default evaluator to calculate values for float properties.
ArgbEvaluator The default evaluator to calculate values for color properties that are represented as hexidecimal values.
TypeEvaluator An interface that allows you to create your own evaluator. If you are animating an object property that is not an int, float, or color, you must implement the TypeEvaluator interface to specify how to compute the object property's animated values. You can also specify a custom TypeEvaluator for int, float, and color values as well, if you want to process those types differently than the default behavior. See the section about Using a TypeEvaluator for more information on how to write a custom evaluator.

 Interpolator:

Class/Interface Description
AccelerateDecelerateInterpolator An interpolator whose rate of change starts and ends slowly but accelerates through the middle.
AccelerateInterpolator An interpolator whose rate of change starts out slowly and then accelerates.
AnticipateInterpolator An interpolator whose change starts backward then flings forward.
AnticipateOvershootInterpolator An interpolator whose change starts backward, flings forward and overshoots the target value, then finally goes back to the final value.
BounceInterpolator An interpolator whose change bounces at the end.
CycleInterpolator An interpolator whose animation repeats for a specified number of cycles.
DecelerateInterpolator An interpolator whose rate of change starts out quickly and and then decelerates.
LinearInterpolator An interpolator whose rate of change is constant.
OvershootInterpolator An interpolator whose change flings forward and overshoots the last value then comes back.
TimeInterpolator An interface that allows you to implement your own interpolator.

可以看到,我们可以使用的动画类有ValueAnimator,ObjectAnimator,AnimatorSet。

其中ObjectAnimator是ValueAnimator的子类,可以更简单的改变对象的属性来展示动画,不过也有自己的局限性(需要改变的对象的属性,该属性必须最小有一个setter方法)

下面我们用AnimatorSet和ObjectAnimator来做出文章开始之前的效果。

首先对于每个菜单的选项,我们需要做一个自定义view,这是一个普通的组合控件.

选项布局里只需一个TextView+ImageView即可。

自定义View的代码也比较简单:

 public class EnvrnoteItemView extends LinearLayout {
private TextView mTextIViewLebal;
private ImageView mImageView;
private Context mContext;
public EnvrnoteItemView(Context context) {
super(context);
} public EnvrnoteItemView(Context context, AttributeSet attrs) {
super(context, attrs);
LayoutInflater.from(context).inflate(R.layout.envrnote_item, this,true);
mContext=context;
mTextIViewLebal=(TextView) findViewById(R.id.item_lebal_id);
mImageView=(ImageView) findViewById(R.id.item_image_id);
}
public void setLebalText(String text){
mTextIViewLebal.setText(text);
invalidate();
}
public void setImage(int resId){
mImageView.setImageBitmap(BitmapFactory.decodeResource(mContext.getResources(), resId));
invalidate();
} }

剩下的就是动画的展示了:

 public class FragmentEnvrnote extends Fragment implements OnClickListener {
private final int DIS = 10;
private ImageView mItemCompose;
private EnvrnoteItemView mItemAttachment;
private EnvrnoteItemView mItemAudio;
private EnvrnoteItemView mItemCamera;
private EnvrnoteItemView mItemHandwriting;
private EnvrnoteItemView mItemLock;
private EnvrnoteItemView mItemText;
private boolean isOpened;
private ObjectAnimator mMainAnimator;
private AnimatorSet mAnimatorSet; @Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// TODO Auto-generated method stub
isOpened=false;
View view = inflater.inflate(R.layout.fragment_envrnote, null);
mItemCompose = (ImageView) view.findViewById(R.id.envrnote_compose_id);
mItemCompose.setOnClickListener(this); mItemAttachment = (EnvrnoteItemView) view
.findViewById(R.id.envrnote_attachment_id);
mItemAttachment
.setLebalText(getString(R.string.envrnote_attachment_str));
mItemAttachment.setImage(R.drawable.ic_attachment);
mItemAttachment.setOnClickListener(this); mItemAudio = (EnvrnoteItemView) view
.findViewById(R.id.envrnote_audio_id);
mItemAudio.setLebalText(getString(R.string.envrnote_audio_str));
mItemAudio.setImage(R.drawable.ic_audio);
mItemAudio.setOnClickListener(this); mItemCamera = (EnvrnoteItemView) view
.findViewById(R.id.envrnote_camera_id);
mItemCamera.setLebalText(getString(R.string.envrnote_camera_str));
mItemCamera.setImage(R.drawable.ic_camera);
mItemCamera.setOnClickListener(this); mItemHandwriting = (EnvrnoteItemView) view
.findViewById(R.id.envrnote_handwriting_id);
mItemHandwriting
.setLebalText(getString(R.string.envrnote_handwritting_str));
mItemHandwriting.setImage(R.drawable.ic_handwriting);
mItemHandwriting.setOnClickListener(this); mItemLock = (EnvrnoteItemView) view.findViewById(R.id.envrnote_lock_id);
mItemLock.setLebalText(getString(R.string.envrnote_lock_str));
mItemLock.setImage(R.drawable.ic_lock);
mItemLock.setOnClickListener(this); mItemText = (EnvrnoteItemView) view.findViewById(R.id.envrnote_text_id);
mItemText.setLebalText(getString(R.string.envrnote_text_str));
mItemText.setImage(R.drawable.ic_text);
mItemText.setOnClickListener(this);
return view;
} @Override
public void onClick(View v) {
if (v == mItemCompose) {
if (!isOpened) {
openMenu();
isOpened = true;
} else {
closeMenu();
isOpened = false;
}
}
else{
if (!isOpened) {
openMenu();
isOpened = true;
} else {
closeMenu();
isOpened = false;
}
}
} private void openMenu() {
showItem(mItemAttachment, 0, 6);
showItem(mItemAudio, 1, 6);
showItem(mItemCamera, 2, 6);
showItem(mItemHandwriting, 3, 6);
showItem(mItemLock, 4, 6);
showItem(mItemText, 5, 6);
} private void closeMenu() {
closeItem(mItemAttachment, 0, 6);
closeItem(mItemAudio, 1, 6);
closeItem(mItemCamera, 2, 6);
closeItem(mItemHandwriting, 3, 6);
closeItem(mItemLock, 4, 6);
closeItem(mItemText, 5, 6);
} private void showItem(final View view, int index, int total) {
if(view.getVisibility()!= View.VISIBLE){
view.setVisibility(View.VISIBLE);
}
int baseDistance=mItemCompose.getHeight()+DIS;
float baseY=mItemCompose.getTranslationY();
mAnimatorSet =new AnimatorSet();
mAnimatorSet.playTogether(
ObjectAnimator.ofFloat(view, "translationY", baseY-((index+1)*baseDistance)),
ObjectAnimator.ofFloat(view, "scaleX", 0f, 1.0f),
ObjectAnimator.ofFloat(view, "scaleY", 0f, 1.0f),
ObjectAnimator.ofFloat(view, "alpha", 0f, 1.0f));
mAnimatorSet.setDuration(100);
mAnimatorSet.start();
} private void closeItem(final View view, int index, int total) {
if (view.getVisibility() != View.VISIBLE) {
view.setVisibility(View.VISIBLE);
}
mAnimatorSet =new AnimatorSet();
mAnimatorSet.playTogether(
ObjectAnimator.ofFloat(view, "translationY",mItemCompose.getTranslationY()),
ObjectAnimator.ofFloat(view, "scaleX", 1.0f, 0f),
ObjectAnimator.ofFloat(view, "scaleY", 1.0f, 0f),
ObjectAnimator.ofFloat(view, "alpha", 1.0f, 0f));
mAnimatorSet.setDuration(100);
mAnimatorSet.addListener(new AnimatorListener() { @Override
public void onAnimationStart(Animator animation) {
// TODO Auto-generated method stub } @Override
public void onAnimationRepeat(Animator animation) {
// TODO Auto-generated method stub } @Override
public void onAnimationEnd(Animator animation) {
// TODO Auto-generated method stub
view.setVisibility(View.GONE);
} @Override
public void onAnimationCancel(Animator animation) {
// TODO Auto-generated method stub }
});
mAnimatorSet.start();
} }

主界面的layout:

 <?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" > <ImageView
android:id="@+id/envrnote_compose_id"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom|right"
android:background="@drawable/ic_compose"
android:padding="5dp"
android:scaleType="fitXY" /> <com.example.drawabeltest.envrnote.EnvrnoteItemView
android:id="@+id/envrnote_attachment_id"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom|right"
android:padding="5dp"
android:visibility="gone" /> <com.example.drawabeltest.envrnote.EnvrnoteItemView
android:id="@+id/envrnote_audio_id"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom|right"
android:padding="5dp"
android:visibility="gone" /> <com.example.drawabeltest.envrnote.EnvrnoteItemView
android:id="@+id/envrnote_camera_id"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom|right"
android:padding="5dp"
android:visibility="gone" /> <com.example.drawabeltest.envrnote.EnvrnoteItemView
android:id="@+id/envrnote_handwriting_id"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom|right"
android:padding="5dp"
android:visibility="gone" /> <com.example.drawabeltest.envrnote.EnvrnoteItemView
android:id="@+id/envrnote_lock_id"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom|right"
android:padding="5dp"
android:visibility="gone" /> <com.example.drawabeltest.envrnote.EnvrnoteItemView
android:id="@+id/envrnote_text_id"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom|right"
android:padding="5dp"
android:visibility="gone" /> </FrameLayout>

View Layout

可以看到,AnimaotrSet和ObjectAnimator使用起来还是比较方便的,不过前面也说过了ObjectAnimator相对ValueAnimator还是有一定局限性,所以为了更深入学习属性动画,还是需要学会如何使用ValueAnimator,另外还需要学会自定义TimeInterpolator和TypeEvaluator,而这也是我下一篇的重点。

属性动画详解一(Property Animation)的更多相关文章

  1. 属性动画详解 Interpolator TypeEvaluator

    概述 产生原因         3.0以前,android支持两种动画模式,tween animation,frame animation,在android3.0中又引入了一个新的动画系统:prope ...

  2. Android(java)学习笔记264:Android下的属性动画高级用法(Property Animation)

    1. 大家好,在上一篇文章当中,我们学习了Android属性动画的基本用法,当然也是最常用的一些用法,这些用法足以覆盖我们平时大多情况下的动画需求了.但是,正如上篇文章当中所说到的,属性动画对补间动画 ...

  3. Android中PropertyAnimation属性动画详解(一)

    在之前的文章中已经讲了帧动画frame-by-frame animation和补间动画tweened animation,其实这两种动画原理好简单,都是按照预先固定的动画模式来播放的,帧动画将一张张单 ...

  4. Android(java)学习笔记208:Android下的属性动画高级用法(Property Animation)

    1. 大家好,在上一篇文章当中,我们学习了Android属性动画的基本用法,当然也是最常用的一些用法,这些用法足以覆盖我们平时大多情况下的动画需求了.但是,正如上篇文章当中所说到的,属性动画对补间动画 ...

  5. Android开发——View动画、帧动画和属性动画详解

    0. 前言   Android动画是面试的时候经常被问到的话题.我们都知道Android动画分为三类:View动画.帧动画和属性动画. 先对这三种动画做一个概述: View动画是一种渐进式动画,通过图 ...

  6. [转]Animation 动画详解(一)——alpha、scale、translate、rotate、set的xml属性及用法

    转载:http://blog.csdn.net/harvic880925/article/details/39996643 前言:这几天做客户回访,感触很大,用户只要是留反馈信息,总是一种恨铁不成钢的 ...

  7. [转]超级强大的SVG SMIL animation动画详解

    超级强大的SVG SMIL animation动画详解 本文花费精力惊人,具有先驱前瞻性,转载规则以及申明见文末,当心予以追究.本文地址:http://www.zhangxinxu.com/wordp ...

  8. Android Animation动画详解(二): 组合动画特效

    前言 上一篇博客Android Animation动画详解(一): 补间动画 我已经为大家介绍了Android补间动画的四种形式,相信读过该博客的兄弟们一起都了解了.如果你还不了解,那点链接过去研读一 ...

  9. css 12-CSS3属性详解:动画详解

    12-CSS3属性详解:动画详解 #前言 本文主要内容: 过渡:transition 2D 转换 transform 3D 转换 transform 动画:animation #过渡:transiti ...

随机推荐

  1. stark组件(1):动态生成URL

    项目启动时自动生成URL 效果图: 知识点: Django启动前通过apps下的ready方法执行一个可以生成URL的py文件 include函数主要返回有三个元素的一个元组.第一个是url配置(ur ...

  2. __setitem__,__getitem,__delitem__的作用

    class Foo: def __init__(self, name): self.name = name def __getitem__(self, item): print('obj[key]时, ...

  3. (ADO.NET小知识点汇总)看到什么记什么

    1.数据库连接池:在同时连接数不多的情况下, 打开一个链接往数据库导1W条数据的耗时 跟 导一条数据就打开跟关闭数据库连接的耗时 两者其实相差不大,这是为什么呢?打开关闭的本身不是有很多耗时吗?这是因 ...

  4. Robotium测试架构规划及测试用例组织

    转自:http://blog.sina.com.cn/s/blog_68f262210102vrft.html 6.1 测试架构规划 由于测试用例执行的时候是在手机上执行的,所以类似于Web的把测试数 ...

  5. 学习python3之路的第一个小代码-----------9*9乘法表

    这个编写的简单,用两个循环迭代就行.下面就是我写的编码以及输出的结果 1 #!/usr/bin/env python 2 # encoding: utf-8 3 4 i = 1 5 6 while i ...

  6. Java实现对cookie的增删改查

    原文地址:http://blog.csdn.net/k21325/article/details/54377830 @RequestMapping(value="meeting/addGua ...

  7. 微信小程序--问题汇总及详解之form表单

    附上微信小程序开发文档链接:https://mp.weixin.qq.com/debug/wxadoc/dev/framework/MINA.html form表单: 当点击 <form/> ...

  8. nyoj 325

    zb的生日 时间限制:3000 ms  |  内存限制:65535 KB 难度:2   描述 今天是阴历七月初五,acm队员zb的生日.zb正在和C小加.never在武汉集训.他想给这两位兄弟买点什么 ...

  9. BZOJ2396 神奇的矩阵 【随机化 + 矩乘】

    题目链接 BZOJ2396 题解 一种快速判断两个矩阵是否相等的方法: 对于两个\(n * n\)矩阵,两边同时乘一个\(n * 1\)的随机矩阵,如果结果相等,那么有很大概率两个矩阵相等 如果左边是 ...

  10. bzoj 4465 游戏中的学问(game)

    题目描述 输入 输出 样例输入 3 1 1000000009 样例输出 2 提示 solution 令f[i][j]表示i个人围成j个圈的方案数 啥意思呢 可以把一个人塞进前面的圈里(i-1种塞法) ...