您的位置:首页 > 移动开发 > Android开发

Android动画之二:View Animation

2015-11-05 14:15 369 查看
如上一篇博客《Android动画之一:Drawable Animation》所说,android动画主要分为三大部分,上一篇博客已经讲解Drawable Animation的用法,即逐帧地显示图片,经常运用于动态显示一个进度动画,这是出现频率最高的应用场景。接下来,我们这篇文章将循序渐进,介绍View
Animation。

View Animation也是我们平时很多书籍所说的Tweened Animation(有人翻译为补间动画)。View Animation分为4大类:AlphaAnimation,RotateAnimation,ScaleAnimation,TranslateAnimation,分别对应透明度,旋转,大小,位移四种变化。

View Animation的效果由四个因素决定:1)初始状态;2)结束状态;3)持续时间;4)Interpolator。所以要定义一个View Animation,你只要定义以上四个因素,中间的过程怎么变化则有系统自动计算出来。其中前3个因素很容易理解,第四个因素Interpolator比较特别,这个单词不知道怎么翻译比较适合,很多书籍的翻译都很奇怪。Interpolator是决定动画进行过程的速度变化。比如:你将一个按钮从屏幕左侧运动到屏幕右侧。可以让它一直加速前进,或者先减速然后减速,这就是Interpolator发挥的作用,具体使用方法下面会说,先从简单的说起。

像Drawable Animation一样,定义一个View Animation可以用代码的方式,也可以用XML文件的方式。我们先来写一个最简单的例子,对一个helloworld字符串进行移动,旋转,放大,变暗,分别用代码实现和XML文件实现。先用代码实现。

首先新建工程:ViewAnimationDemo,并新建一个布局文件:animationjavacode.xml,如下:

ViewAnimationDemo\res\layout\animationjavacode.xml

[html] view
plaincopy





<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"

xmlns:tools="http://schemas.android.com/tools"

android:layout_width="match_parent"

android:layout_height="match_parent"

android:paddingBottom="@dimen/activity_vertical_margin"

android:paddingLeft="@dimen/activity_horizontal_margin"

android:paddingRight="@dimen/activity_horizontal_margin"

android:paddingTop="@dimen/activity_vertical_margin"

tools:context=".MainActivity" >

<TextView

android:id="@+id/translation"

android:layout_width="wrap_content"

android:layout_height="wrap_content"

android:layout_marginBottom="50dp"

android:text="我要移动" />

<TextView

android:id="@+id/rotate"

android:layout_width="wrap_content"

android:layout_height="wrap_content"

android:layout_below="@id/translation"

android:layout_marginBottom="50dp"

android:text="我要旋转" />

<TextView

android:id="@+id/scale"

android:layout_width="wrap_content"

android:layout_height="wrap_content"

android:layout_below="@id/rotate"

android:layout_marginBottom="50dp"

android:text="我要变大" />

<TextView

android:id="@+id/alpha"

android:layout_width="wrap_content"

android:layout_height="wrap_content"

android:layout_below="@id/scale"

android:layout_marginBottom="200dp"

android:text="我要变暗" />

<Button

android:id="@+id/fire"

android:layout_width="match_parent"

android:layout_height="50dp"

android:layout_below="@id/alpha"

android:text="运动" />

</RelativeLayout>

该XML布局文件对应的Activity如下:

ViewAnimationDemo/src/com/CSDN/viewanimationdemo/AnimCodeActivity.java

[java] view
plaincopy





public class AnimCodeActivity extends Activity {

private TextView translation;

private TextView rotate;

private TextView scale;

private TextView alpha;

private Button button;

@Override

protected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

requestWindowFeature(Window.FEATURE_NO_TITLE);

setContentView(R.layout.animationjavacode);

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

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

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

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

button = (Button) findViewById(R.id.fire);

button.setOnClickListener(new OnClickListener() {

@Override

public void onClick(View v) {

// TODO Auto-generated method stub

// 1&2: 确定起始状态,结束状态

TranslateAnimation tAnim = new TranslateAnimation(0, 400, 0, 0);//横向位移400个单位

RotateAnimation rAnima = new RotateAnimation(0, 70);//顺时针旋转70度

ScaleAnimation sAnima = new ScaleAnimation(0, 5, 0, 5);//横向放大5倍,纵向放大5倍

AlphaAnimation aAnima = new AlphaAnimation(1.0f, 0.0f);//从全不透明变为全透明

// 3: 确定持续时间

tAnim.setDuration(2000);

rAnima.setDuration(2000);

sAnima.setDuration(2000);

aAnima.setDuration(2000);

// 4: 确定Interpolator

tAnim.setInterpolator(new AccelerateDecelerateInterpolator());

// 启动动画

translation.startAnimation(tAnim);

rotate.startAnimation(rAnima);

scale.startAnimation(sAnima);

alpha.startAnimation(aAnima);

}

});

}

}

从代码中不难看到,首先确定了动画的起始状态和结束状态,然后通过setDuration(2000)设置持续时间,如果这里不设置持续时间,将默认为30mx,基本肉眼看不到。最后我们设置了一个Interpolator,此处设置效果是动画先减速进行,然后减速。默认状态时匀速进行。没错,Interpolator的基本用法就是这么容易。以上代码效果图如下:



接下来讲解怎么用xml定义动画,按照android官网的介绍,定义View Animation的xml文件格式如下:

[html] view
plaincopy





<?xml version="1.0" encoding="utf-8"?>

<set xmlns:android="http://schemas.android.com/apk/res/android"

android:interpolator="@[package:]anim/interpolator_resource"

android:shareInterpolator=["true" | "false"] >

<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>

...

</set>

</set>

该文件只能有一个根结点,可以是<set>,<alpha>,<scale>,<translate>,<rotate>,而其中,<set>结点可以包含子节点,即可以包含<set>,<alpha>,<scale>,<translate>,<rotate>,以此类推。

以上需要讲解的两个标签属性是android:interpolator和android:shareInterpolator,前者代表你所使用的interpolator,可以是系统自带,也可以是自定义。而后者代表,是否将该Interpolator共享给子节点。其它子标签的属性很容易理解,基本看属性名字就能懂,除了其中两个,android:pivotX和android:pivotY,我们知道,pivot的意思是轴心的意思,所以这两个属性定义的是此次动画变化的轴心位置,默认是左上角,当我们把它们两者都赋值为50%,则变化轴心在中心。

接下来写一个具体的例子,如下,还是在刚才的项目中进行。一般我们把定义动画的xml文件放在res/anim目录下,首先我们新建一个anim文件夹,然后新建一个xml文件,如下:

ViewAnimationDemo/res/anim/myanim.xml

[html] view
plaincopy





<set xmlns:android="http://schemas.android.com/apk/res/android"

android:shareInterpolator="false">

<scale

android:interpolator="@android:anim/accelerate_decelerate_interpolator"

android:fromXScale="1.0"

android:toXScale="1.4"

android:fromYScale="1.0"

android:toYScale="0.6"

android:pivotX="50%"

android:pivotY="50%"

android:fillAfter="false"

android:duration="1000" />

<set

android:interpolator="@android:anim/accelerate_interpolator"

android:startOffset="1000">

<scale

android:fromXScale="1.4"

android:toXScale="0.0"

android:fromYScale="0.6"

android:toYScale="0.0"

android:pivotX="50%"

android:pivotY="50%"

android:duration="400" />

<rotate

android:fromDegrees="0"

android:toDegrees="60"

android:toYScale="0.0"

android:pivotX="50%"

android:pivotY="50%"

android:duration="400" />

</set>

</set>

上述例子的效果图如下:



上面代码中,有 唯一一个根节点<set>,它有两个子节点<scale>和<set>,然后其中的子节点<set>拥有两个自己的子节点<scale>和<rotate>。讲解一下上面一些没见过的标签属性。

andoird:fillAfter:前面一个例子中,我们的动画结束后helloworld又回到了原来状态,通过设置fillAfter为true,则动画将保持结束的状态,但是,如前一篇博客《Android动画之一:Drawable
Animation》所说,View Animation的动画效果是绘制出来的,并非该组件真正移动了,这个问题我们后续会继续探讨。现在只需要知道将fillAfter设置为true之后,动画将保持结束状态,这大多应用于设计连续发生的动画。

startOffset:该属性定义动画推迟多久开始,通过这个属性的设置,我们可以设计一些前后按序发生的动画,当然,除了最后一个发生的动画,其他动画得设置fillAfter为true.

interpolator:这里我们使用了系统自带的interpolator

接下来我们定义一个布局文件,该布局文件只有一张图片,一个按钮,通过点击按钮触发图片进行动画,该布局文件比较简单,这里不列出。如何在该布局文件对应的activity代码中启动动画呢,代码如下:

ViewAnimationDemo/src/com/CSDN/viewanimationdemo/AnimaXmlActivity.java

[java] view
plaincopy





public class AnimaXmlActivity extends Activity {

private Button btn;

private ImageView img;

@Override

protected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

requestWindowFeature(Window.FEATURE_NO_TITLE);

setContentView(R.layout.activity_anima_xml);

btn = (Button)findViewById(R.id.xml_btn);

img = (ImageView)findViewById(R.id.xmlAnimImg);

btn.setOnClickListener(new OnClickListener() {

@Override

public void onClick(View v) {

// TODO Auto-generated method stub

Animation anim = AnimationUtils.loadAnimation(AnimaXmlActivity.this, R.anim.myanim);

img.startAnimation(anim);

}

});

}

}

以上代码很容易理解,我们一般使用AnimationUtils读取定义在xml文件中的动画。

到此为止,关于View Animation的基础知识基本已经覆盖了,接下来写一个具体实践的例子,实现经常见到的侧滑功能,如网易新闻这种:



这个侧滑的效果实现并不难,很多人运用了AsyncTask线程,但是结果你会发现有时会发生明显的卡顿,但是如果使用动画效果将会平滑很多。我们接下来实现这样的功能:

首先新建一个项目:SwipeWithAnim。并新建一个布局文件activity_main.xml,如下

SwipeWithAnim/res/layout/activity_main.xml

[html] view
plaincopy





<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"

xmlns:tools="http://schemas.android.com/tools"

android:layout_width="match_parent"

android:layout_height="match_parent"

tools:context=".MainActivity" >

<View

android:id="@+id/content"

android:layout_width="match_parent"

android:layout_height="match_parent"

android:background="#e23451" />

<LinearLayout

android:id="@+id/menu"

android:layout_width="400dp"

android:layout_height="match_parent"

android:layout_gravity="left"

android:layout_marginLeft="-380dp"

android:orientation="horizontal" >

<View

android:layout_width="0dp"

android:layout_height="match_parent"

android:layout_weight="1"

android:background="#eeeee1" />

<View

android:id="@+id/dragArea"

android:layout_width="20dp"

android:layout_height="match_parent"

android:background="#00000000" />

</LinearLayout>

</FrameLayout>

屏幕显示内容content,左侧隐去菜单menu,知识将20dp的透明部分放在屏幕左侧,用于触发onTouch事件。该xml文件对应的activity代码如下:

SwipeWithAnim/src/com/CSDN/swipewithanim/MainActivity.java

[java] view
plaincopy





public class MainActivity extends Activity {

private View menu;

private final static int SHOW_MENU = 1;

private final static int HIDE_MENU = -1;

private int swipe_tag = SHOW_MENU;

private int max_menu_margin = 0;

private int min_menu_margin;

private float beginX;

private float latestX;

private float diffX;

private float latestMargin;

private FrameLayout.LayoutParams lp;

/*

* (non-Javadoc)

*

* @see android.app.Activity#onCreate(android.os.Bundle)

*/

@Override

protected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

requestWindowFeature(Window.FEATURE_NO_TITLE);

setContentView(R.layout.activity_main);

menu = findViewById(R.id.menu);

lp = (FrameLayout.LayoutParams) menu.getLayoutParams();

min_menu_margin = lp.leftMargin;

menu.setOnTouchListener(new OnTouchListener() {

@Override

public boolean onTouch(View v, MotionEvent event) {

// TODO Auto-generated method stub

int action = MotionEventCompat.getActionMasked(event);

switch (action) {

case MotionEvent.ACTION_DOWN:

beginX = event.getX();

break;

case MotionEvent.ACTION_MOVE:

latestX = event.getX();

diffX = latestX - beginX;

swipe_tag = diffX > 0 ? SHOW_MENU : HIDE_MENU;

latestMargin = lp.leftMargin + diffX;

if (latestMargin > min_menu_margin

&& latestMargin < max_menu_margin) {

lp.leftMargin = (int) (latestMargin);

menu.setLayoutParams(lp);

}

break;

case MotionEvent.ACTION_UP:

TranslateAnimation tAnim;

if (swipe_tag == SHOW_MENU) {

tAnim = new TranslateAnimation(0, max_menu_margin

- latestMargin, 0, 0);

tAnim.setInterpolator(new DecelerateInterpolator());

tAnim.setDuration(800);

menu.startAnimation(tAnim);

} else {

tAnim = new TranslateAnimation(0, min_menu_margin

- latestMargin, 0, 0);

tAnim.setDuration(800);

menu.startAnimation(tAnim);

}

//在动画结束的时刻,移动menu的位置,使menu真正移动。

tAnim.setAnimationListener(new AnimationListener() {

@Override

public void onAnimationStart(Animation animation) {

// TODO Auto-generated method stub

}

@Override

public void onAnimationRepeat(Animation animation) {

// TODO Auto-generated method stub

}

@Override

public void onAnimationEnd(Animation animation) {

// TODO Auto-generated method stub

if (swipe_tag == SHOW_MENU) {

lp.leftMargin = max_menu_margin;

menu.setLayoutParams(lp);

} else {

lp.leftMargin = min_menu_margin;

menu.setLayoutParams(lp);

}

menu.clearAnimation();

}

});

break;

}

return true;

}

});

}

}

如上代码,监听menu的onTouch事件,然后根据移动距离移动menu,当手指拿起来时,启动动画,使移动继续进行到结束。我们上面提到,View Animation只是绘制出来的效果,发生动画的组件并非真正移动了,那么此处为了解决这个问题,我们监听了AnimationListener,在动画结束时,将menu的位置移动到动画结束的位置,这样就成功移动了menu。效果图如下,由于截图软件关系,看起来会卡顿,在真机上测试则完全平滑。



最后附上这篇博客前后的两份源码:
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: