标签:
这几天看到一个小动画,觉得有点意思,就自己实现来看看,先看效果图
OK,这个效果基本功能就是,一个图片,从顶部掉下来,完后弹几下,再停止,实现起来还是比较简单的,不过也走了点小弯路,这里记录下。
有段时间做自定义控件比较多,有点中毒了,看到任何效果第一个先想到自定义控件,所以一开始我是用自定义控件嵌套自己用动画计算距离来实现,后来发现没必要,但基本思路是一致的,这里先看看自定义控件嵌套动画如何实现。
首先自定义一个ViewGroup, 看一下里面用到的几个变量
private int mWidth; // 屏幕宽度 private int mHeight; // 屏幕高度 private boolean mFirst = true; // 是否第一次执行 private int mTotalRound; // 总次数 private int mCurRound; // 当前次数 private long mTime; // 动画时间都有注释,主要注意下次数,其中图片落下或弹起,都算一次动画
完后实现一个加入图片的方法
private void init() { ImageView view = new ImageView(mContext); view.setBackgroundResource(R.mipmap.jump); view.setScaleType(ImageView.ScaleType.FIT_XY); addView(view); }
接下来,实现onMeasure方法,获取屏幕宽高,这个在后面动画中有用
@Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { mWidth = MeasureSpec.getSize(widthMeasureSpec); //获取ViewGroup宽度 mHeight = MeasureSpec.getSize(heightMeasureSpec); //获取ViewGroup高度 setMeasuredDimension(mWidth, mHeight); //设置ViewGroup的宽高 if (mFirst) { mFirst = false; beginAnimation(); } }
这里mFirst是一个布尔变量,初始为true,这里就是动画只执行一次,不管进入onMeasure方法多少次,beginAnimation就是我们要进行的动画。
实现动画前,我们先来仔细观察一下这个动画,这个动画看着挺简单,但是有几个细节还是要注意:
1. 动画弹起的高度越来越小,我这里是第一次弹起屏幕的高度的1/2,第二次弹起1/4,第三次弹起1/8,以此类推
2. 我们将图片的一次落下或弹起看成一次动画,动画的时间越来越短,假设第一次落下动画需要1秒,那第一次弹起就需要1/2秒,第二次落下也是1/2秒,第二次弹起则需要1/4秒,以此类推
3. 下落的时候,速度越来越快,弹起的时候,速度越来越慢
了解了这些细节后,我就可以用layout方法来动态改变ImageView的位置,从而实现动画的效果,layout方法很简单
public void layout(int l, int t, int r, int b)传入left, top, right, bottom的值来绝对位置,left和right我们不用关注,因为图片是上下移动,所以left恒为0,而right恒为屏幕的宽度,而top和bottom是实时变化的,按顺序看下top和bottom的值变化,假设屏幕高度为mHeight
第一次动画,图片从屏幕的顶部下落到屏幕的底部,top的变化为从-mHeight到0,bottom的变化从0到mHeight
第二次动画,图片从屏幕的底部弹起到图片的底部正好在屏幕高度的1/2处,top的变化从0到-mHeight/2,bottom的变化从mHeight到mHeight/2
第三次动画,图片底部从屏幕高度的1/2处下落到屏幕的底部,top的变化从-mHeight/2到0,bottom的变化从mHeight/2到mHeight
以此类推,后面的动画都是类似的,这些都清楚后,直接来看动画如何实现
private void beginAnimation() { final int height; if (mCurRound % 2 == 0) { // 向下 height = (int) (-mHeight * Math.pow(0.5, mCurRound / 2)); } else { // 向上 height = (int) (-mHeight * Math.pow(0.5, (mCurRound + 1) / 2)); } Animation translateAnimation = new Animation() { @Override protected void applyTransformation(float interpolatedTime, Transformation t) { if (mCurRound % 2 == 0) { getChildAt(0).layout(0, (int) (height * (1 - interpolatedTime)), mWidth, mHeight + height - (int) (interpolatedTime * height)); } else { getChildAt(0).layout(0, (int) (height * interpolatedTime), mWidth, mHeight + (int) (height * interpolatedTime)); } } }; if (mCurRound % 2 == 0) { translateAnimation.setInterpolator(new AccelerateInterpolator()); translateAnimation.setDuration((long)(mTime * Math.pow(0.5, mCurRound / 2))); } else { translateAnimation.setInterpolator(new DecelerateInterpolator()); translateAnimation.setDuration((long)(mTime * Math.pow(0.5, (mCurRound + 1) / 2))); } startAnimation(translateAnimation); translateAnimation.setAnimationListener(new Animation.AnimationListener() { @Override public void onAnimationStart(Animation animation) { } @Override public void onAnimationEnd(Animation animation) { mCurRound++; if (mCurRound < mTotalRound) { beginAnimation(); } } @Override public void onAnimationRepeat(Animation animation) { } }); }mCurRound就是当前动画的次数,从0开始计数,取模2为0,也就是偶数,也就是下落,取模2为1,也就是奇数,也就是弹起,后面的applyTransformation方法,有一个参数interpolatedTime,在动画执行的过程中,会不断调用applyTransformation这个方法,而interpolatedTime的值从0变化1,我们可以根据这个值,来动态计算当前的高度,而计算方法参考上面的每次动画的top和bottom的值变化范围,自己在纸上画画就知道了。后面的
if (mCurRound % 2 == 0) { translateAnimation.setInterpolator(new AccelerateInterpolator()); translateAnimation.setDuration((long)(mTime * Math.pow(0.5, mCurRound / 2))); } else { translateAnimation.setInterpolator(new DecelerateInterpolator()); translateAnimation.setDuration((long)(mTime * Math.pow(0.5, (mCurRound + 1) / 2))); }就是之前分析的,动画下落和弹起的速率变化不一样,动画时间也越来越短。
最后就是在动画执行结束的时候,判断下当前动画执行了多少个了,如果没执行完就继续执行下一个动画,这里是一个递归的调用。代码实现起来还是蛮简单的,关键是前面的分析过程,和细节的注意,下面贴出完整代码
public class JumpActivity extends Activity { private int totalRound = 10; private int time = 2000; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(new BounceView(this, totalRound, time)); } }
public class BounceView extends ViewGroup { private Context mContext; private int mWidth; // 屏幕宽度 private int mHeight; // 屏幕高度 private boolean mFirst = true; // 是否第一次执行 private int mTotalRound; // 总次数 private int mCurRound; // 当前次数 private long mTime; // 动画时间 public BounceView(Context context) { super(context); init(); } public BounceView(Context context, AttributeSet attrs) { super(context, attrs); init(); } public BounceView(Context context, int roundNum, long time) { super(context); mContext = context; mTime = time; mTotalRound = roundNum; init(); } private void init() { ImageView view = new ImageView(mContext); view.setBackgroundResource(R.mipmap.jump); view.setScaleType(ImageView.ScaleType.FIT_XY); addView(view); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { mWidth = MeasureSpec.getSize(widthMeasureSpec); //获取ViewGroup宽度 mHeight = MeasureSpec.getSize(heightMeasureSpec); //获取ViewGroup高度 setMeasuredDimension(mWidth, mHeight); //设置ViewGroup的宽高 if (mFirst) { mFirst = false; beginAnimation(); } } @Override protected void onLayout(boolean changed, int left, int top, int right, int bottom) { } private void beginAnimation() { final int height; if (mCurRound % 2 == 0) { // 向下 height = (int) (-mHeight * Math.pow(0.5, mCurRound / 2)); } else { // 向上 height = (int) (-mHeight * Math.pow(0.5, (mCurRound + 1) / 2)); } Animation translateAnimation = new Animation() { @Override protected void applyTransformation(float interpolatedTime, Transformation t) { if (mCurRound % 2 == 0) { getChildAt(0).layout(0, (int) (height * (1 - interpolatedTime)), mWidth, mHeight + height - (int) (interpolatedTime * height)); } else { getChildAt(0).layout(0, (int) (height * interpolatedTime), mWidth, mHeight + (int) (height * interpolatedTime)); } } }; if (mCurRound % 2 == 0) { translateAnimation.setInterpolator(new AccelerateInterpolator()); translateAnimation.setDuration((long)(mTime * Math.pow(0.5, mCurRound / 2))); } else { translateAnimation.setInterpolator(new DecelerateInterpolator()); translateAnimation.setDuration((long)(mTime * Math.pow(0.5, (mCurRound + 1) / 2))); } startAnimation(translateAnimation); translateAnimation.setAnimationListener(new Animation.AnimationListener() { @Override public void onAnimationStart(Animation animation) { } @Override public void onAnimationEnd(Animation animation) { mCurRound++; if (mCurRound < mTotalRound) { beginAnimation(); } } @Override public void onAnimationRepeat(Animation animation) { } }); } }这是第一种方法,我们自定义了控件,而且还在applyTransformation中做了计算,后来想了下,其实没必要,不使用自定义控件,不自己去计算,也可以实现,思路其实差不多,唯一的区别就是我们可以使用属性动画,直接用ObjectAnimator的ofFloat方法来移动图片就行了,这里因为思路都是一致的,我就直接贴代码了:
先声明一个布局文件jump_layout.xml:
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="match_parent" android:layout_height="match_parent"> <ImageView android:id="@+id/move" android:layout_width="match_parent" android:layout_height="match_parent" android:src="@mipmap/jump" android:scaleType="fitXY" /> </LinearLayout>完后声明Java类JumpActivity.java:
public class JumpActivity extends Activity { private ImageView view; private int mHeight; private int totalRound = 10; private int curRound = 0; private int time = 2000; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); //setContentView(new BounceView(this, totalRound, time)); setContentView(R.layout.jump_layout); view = (ImageView) findViewById(R.id.move); } @Override public void onWindowFocusChanged(boolean hasFocus) { super.onWindowFocusChanged(hasFocus); if (hasFocus) { Rect outRect = new Rect(); getWindow().findViewById(Window.ID_ANDROID_CONTENT).getDrawingRect(outRect); mHeight = outRect.height(); beginTransAnimation(); } } private void beginTransAnimation() { final int height; if (curRound % 2 == 0) { // 向下 height = (int) (-mHeight * Math.pow(0.5, curRound / 2)); } else { // 向上 height = (int) (-mHeight * Math.pow(0.5, (curRound + 1) / 2)); } float from = 0; float to = 0; if (curRound % 2 == 0) { from = height; to = 0; } else { from = 0; to = height; } ObjectAnimator animator = ObjectAnimator.ofFloat(view, "translationY", from, to); animator.setInterpolator(new AccelerateInterpolator()); animator.addListener(new Animator.AnimatorListener() { @Override public void onAnimationStart(Animator animation) { } @Override public void onAnimationEnd(Animator animation) { curRound++; if (curRound <= totalRound) { beginTransAnimation(); } } @Override public void onAnimationCancel(Animator animation) { } @Override public void onAnimationRepeat(Animator animation) { } }); if (curRound % 2 == 0) { animator.setInterpolator(new AccelerateInterpolator()); animator.setDuration((long) (time * Math.pow(0.5, curRound / 2))).start(); } else { animator.setInterpolator(new DecelerateInterpolator()); animator.setDuration((long) (time * Math.pow(0.5, (curRound + 1) / 2))).start(); } } }
标签:
原文地址:http://blog.csdn.net/gesanri/article/details/51339455