标签:
原来学过用自定义控件以及视图动画来达到这个效果。后来根据慕课网的视频,接触到了属性动画,发现其精髓之处不是一点两点。
相信大家都知道,当我们在使用视图(View)动画的时候,改变轨迹时,所触发的点击事件却没有相对应的随之轨迹而改变。确切的说,Animation改变显示的位置,不可以实现交互的效果,只是实现了显示效果。
AnimatorListeners
added
to them.ValueAnimator
provides
support for animating properties on target objects. The constructors of this class take parameters to define the target object that will be animated as well as the name of the property that will be animated. Appropriate set/get functions are then determined
internally and the animation will call these functions as necessary to animate the property.接下来我们先了解下属性动画ObjectAnimator,原理我也不会用语言去描述,通过简单的例子,看代码来了解通俗易懂。点击按钮,让图片做位移,旋转效果的实现。
先看下布局activity_main:
<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"
>
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/imageview"
android:onClick="click"
android:src="@drawable/ic_launcher"
/>
<Button
android:id="@+id/btn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Move"
android:onClick="move"
android:layout_alignParentBottom="true"
android:layout_centerHorizontal="true"
android:layout_marginBottom="58dp"
/>
</RelativeLayout>
起初我们接触视图动画的时候,一般都用TranslateAnimation,RotationAnition等等了,复杂的动画效果用AnimationSet也能实现。
这里就不具体的讲解了,下面来看下属性动画ObjectAnimator是怎么实现。
ObjectAnimator提供了ofInt、ofFloat、ofObject,这几个方法都是设置动画作用的元素、作用的属性、动画开始、结束、以及中间的任意个属性值。 当对于属性值,只设置一个的时候,会认为当然对象该属性的值为开始(getPropName反射获取),然后设置的值为终点。如果设置两个,则一个为开始、一个为结束 动画更新的过程中,会不断调用setPropName更新元素的属性,所有使用ObjectAnimator更新某个属性,必须得有getter(设置一个属性值的时候)和setter方法。
ObjectAnimator.ofFloat(image, "rotation", 0F,360F).setDuration(1000).start();
ObjectAnimator.ofFloat(image, "translationX", 0F,200F).setDuration(1000).start();
ObjectAnimator.ofFloat(image, "translationY", 0F,200F).setDuration(1000).start();
虽然三个动画效果依次写出来,但并没有执行先后顺序,偏移XY的同时也在旋转着,并发执行。
既然存在ofxxx,我们可以使用PropertyValuesHolder这个类,它可以先将动画属性和值暂时的存储起来,后一起执行,在有些时候可以使用替换掉AnimatorSet,减少代码量。
PropertyValuesHolder p1=PropertyValuesHolder.ofFloat("rotation", 0F,360F);
PropertyValuesHolder p2=PropertyValuesHolder.ofFloat("translationX", 0F,200F);
PropertyValuesHolder p3=PropertyValuesHolder.ofFloat("translationY",0F,200F);
ObjectAnimator.ofPropertyValuesHolder(image, p1,p2,p3).setDuration(1000).start();
视图动画有AnimationSet,那我们的属性动画是不是也有一个set管理呢,答案是有的,AnimatorSet.
ObjectAnimator animator1=ObjectAnimator.ofFloat(image, "rotation", 0F,360F);
ObjectAnimator animator2=ObjectAnimator.ofFloat(image, "translationX", 0F,200F);
ObjectAnimator animator3=ObjectAnimator.ofFloat(image, "translationY", 0F,200F);
AnimatorSet set=new AnimatorSet();
//可以试着调用不同方法,实现不同的动画效果
//set.playTogether(animator1,animator2,animator3);
//set.playSequentially(animator1,animator2,animator3);
set.play(animator2).with(animator3);
set.play(animator1).after(animator2);
set.setDuration(1000);
set.start();
在使用的过程中,可以试着set不同的实现顺序来看下效果,毕竟多联系下嘛。
属性动画的监听:
对于动画,一般都是一些辅助效果,比如我要删除个元素,我可能希望是个淡出的效果,但是最终还是要删掉,并不是你透明度没有了,还占着位置,所以我们需要知道动画如何结束。
public void click(View v) {
ObjectAnimator animator = ObjectAnimator.ofFloat(v, "alpha", 0f, 1f);
animator.setDuration(1000);
animator.addListener(new AnimatorListener() {
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 Toast.makeText(MainActivity.this, "点击了",
* Toast.LENGTH_SHORT) .show(); }
*
* @Override public void onAnimationCancel(Animator animation) { // TODO
* Auto-generated method stub
*
* } });
*/
animator.start();
}
这里可以看出,再我们这样使用new AnimatorListener的时候,可以对动画的开始,重复,结束,取消都可以进行监听操作,可是我们有时候只希望监听动画结束或者单一状态的时,该怎么办呢?
那你可以使用AnimatorListenerAdapter
public void click(View v) {
ObjectAnimator animator = ObjectAnimator.ofFloat(v, "alpha", 0f, 1f);
animator.setDuration(1000);
animator.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
// TODO Auto-generated method stub
super.onAnimationEnd(animation);
Toast.makeText(MainActivity.this, "点击了", Toast.LENGTH_SHORT)
.show();
}
});
animator.start();
}
AnimatorListenerAdapter继承了AnimatorListener接口,然后空实现了所有的方法,当你想使用其中一个状态监听时,可以单一的重写该方法。这样减少了一定的代码量。
属性动画的初步了解应该差不多了,相信他们在阅读完以上部分应该对属性动画有了一定的了解,或许已经感觉到了和视图动画的区别。记得大家都应该知道一种菜单效果--->
先看张效果图吧
当初接触过这个菜单的效果,是自定义控件的实现的,代码量挺复杂的,接触完属性动画,我就想是不是可以用它来完成这个效果呢?
接下来我们实现下,先看下布局,使用的FrameLayout
activity_main.xml:
<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" >
<ImageView
android:id="@+id/imageView_b"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingLeft="5dp"
android:paddingTop="5dp"
android:src="@drawable/b" />
<ImageView
android:id="@+id/imageView_c"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingLeft="5dp"
android:paddingTop="5dp"
android:src="@drawable/c" />
<ImageView
android:id="@+id/imageView_d"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingLeft="5dp"
android:paddingTop="5dp"
android:src="@drawable/d" />
<ImageView
android:id="@+id/imageView_e"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingLeft="5dp"
android:paddingTop="5dp"
android:src="@drawable/e" />
<ImageView
android:id="@+id/imageView_a"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/a" />
</FrameLayout>
布局效果图:
我们存放了5张图片,a图片为按钮图片,bcde则为卫星发射的菜单图。先把这些图片的id保存到数组中,方便我们统一的实例化。
private int[] res = { R.id.imageView_a, R.id.imageView_b, R.id.imageView_c,
R.id.imageView_d, R.id.imageView_e };
新建一个List数组来存放这些图片。
private List<ImageView> imageViewList = new ArrayList<ImageView>();
接下来,用循环遍历来实例化这些ImageView控件,把图片添加到imageViewList中,并实现这些菜单图片的监听事件。
for (int i = 0; i < res.length; i++) {
ImageView imageView = (ImageView) findViewById(res[i]);
imageView.setOnClickListener(this);
imageViewList.add(imageView);
}
重写onClick方法,因为考虑到菜单要展开和收回两种状态效果。定义一个标志位,初始化为true.
private Boolean flag = true;// 用于判断展开 ,回收
点击事件方法:
@Override
public void onClick(View v) {
// TODO Auto-generated method stub
switch (v.getId()) {
case R.id.imageView_a:
if (flag) {
startAnim();
} else {
closeAnim();
}
break;
default:
Toast.makeText(MainActivity.this, "click" + v.getId(),
Toast.LENGTH_SHORT).show();
break;
}
}
一个展开动画效果的方法startAnim()和一个回收效果的方法closeAnim()。其实两者实现的动画效果是相反方向的。
startAnim()
private void startAnim() {
// TODO Auto-generated method stub
for (int i = 1; i < res.length; i++) {
ObjectAnimator animator = ObjectAnimator.ofFloat(
imageViewList.get(i),
"translationY",
0F,
(float) (300 * (Math.sin(Math.PI / 2 / (res.length - 2)
* (i - 1)))));
ObjectAnimator animator1 = ObjectAnimator.ofFloat(
imageViewList.get(i),
"translationX",
0F,
(float) (300 * (Math.cos(Math.PI / 2 / (res.length - 2)
* (i - 1)))));
AnimatorSet set = new AnimatorSet();
set.playTogether(animator, animator1);
set.setDuration(300);
set.start();
set.setInterpolator(new BounceInterpolator());
flag = false;
}
}
实现了位移的偏移X,Y。其实没有多少复杂的,for循环遍历展开效果,只是在偏移的路径需要一些数学知识。这里我固定了偏移的半径,也就是水平或者纵向的最大偏移量为300.
标签:
原文地址:http://blog.csdn.net/u012896330/article/details/52291263