前言:
好久没来写文章了,一方面是因为自己懒了,另外一个是因为最近工作比较忙,没有闲时间(其实主要还是因为懒)。话说八月多换了一个新工作,在之前的公司,主要是横向发展,了解了很多技术。在现在的公司主要是纵向发展,更加深入的探索。之前是广而不精,没有深入学习。在现在的公司呢,能够更加深入的学习技术。就拿最近的工作来说吧,动画很多,刚开始只是知道Android动画分为属性动画、帧动画和补间动画。但是,他们之间有什么具体的区别就不是特别清楚了。尤其是属性动画和补间动画的区别,我能说自己被补间动画坑惨了吗?(其实,还是自己学艺不精,需要继续努力了。)好了,废话说多了,还是来看看今天的文章吧。
首先
要学习Android动画,我们以一个例子来深入学习,毕竟都是要把功能实现出来,写一些大而空没有实用性的东西,不但是敷衍别人,更是对自己的不负责任。如下图所示:(原谅我自恋一下,放了自己的图像照)
- 分析
1、要实现从大图到小图的过度,我们需要怎么实现呢?又需要用哪些技术呢?首先图片缩小了,那么就需要缩放动画;其次,图片向右移动和向上移动了,那么就需要位移动画。背景动画是用Lottie实现的,中间的圆形变叉也是用的lottie监听帧率变化,这个不在今天的讨论和实现范围内。今天主要实现布局从大到小的缩放和移动。
2、 那么缩放和移动可以用哪些动画实现呢?可能有朋友说是用补间动画实现了,那么如果布局上面有点击事件呢?点击事件的位置在原位置还是新位置呢?答案是还在原来的位置,补间动画只能实现view的变化,而不能改变点击事件的位置,如果只是做一个页面展示用,那么补间动画完全够用了。但是,如果想改变点击事件的位置,必须要用属性动画。我能说我在这里被坑了好久吗?好了,现在来看看我们如何实现控件的缩放和移动。
其次
/**
* 动画
*/
public static void scanAnimation(Context context, FrameLayout mLytAll) {
WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
DisplayMetrics dm = new DisplayMetrics();
wm.getDefaultDisplay().getMetrics(dm);
final int width = dm.widthPixels;// 屏幕宽度(像素)
int height = dm.heightPixels;// 屏幕高度(像素)
ObjectAnimator translationX = ObjectAnimator.ofFloat(mLytAll,
"translationX", 1,
(DensityUtils.px2dp(context, width) - 36));
ObjectAnimator translationY = ObjectAnimator.ofFloat(mLytAll,
"translationY", 1,
-(DensityUtils.px2dp(context, height) - 23));
ObjectAnimator scaleX = ObjectAnimator.ofFloat(mLytAll,
"scaleX", (94 / DensityUtils.px2dp(context, width)));
ObjectAnimator scaleY = ObjectAnimator.ofFloat(mLytAll,
"scaleY", (166 / DensityUtils.px2dp(context, height)));
AnimatorSet animatorSet = new AnimatorSet(); //组合动画
animatorSet.playTogether(translationX, translationY, scaleX, scaleY); //设置动画
animatorSet.setDuration(350);
animatorSet.start();
}
- 分析:
1、这里要移动,那么就需要屏幕的宽和高,然后进行等比缩放,因为上面和右边有一定的距离,那么就要计算右边距和上边距;
2、这里因为是X轴和Y轴进行移动并且缩放,所以我这边用了组合动画,AnimatorSet;
3、X轴的移动动画用了ObjectAnimator然后设置X轴移动和Y轴移动,。假设,距离右边距是36dp,上边距是23。那么X轴的移动就是,从1到屏幕宽转换成dp值后减去右边距(36dp)。Y轴的移动就是屏幕的高转换为dp后减去上边距(23dp),然后加上一个负号(-),为什么呢,因为Y轴向上为负,向下为正;
4、现在来看缩放动画,假设缩放后的小屏幕高是166dp,宽是94dp,那么就是缩放后的屏幕宽(94dp)除以屏幕宽转换成dp值之后的比率了。当然,这样做显然是不太严谨的(为什么说不严谨呢,后面会讲)。Y轴的缩放呢就是缩放后小图的高(166dp)除以屏幕高转换成dp之后的值。
5、完成后用AnimatorSet设置X轴Y轴的平移和缩放动画同步进行,然后设置动画的执行时长至此就完成了动画从大屏幕到小屏幕的移动和缩放。
再次
之前在分析的时候说上面直接写死宽高值之后进行缩放不严谨,那么我们来分析一下。因为Android手机屏幕分辨率很多,如果在大屏幕手机上面可能不会出现太大的问题,那么在屏幕分辨率比较低的手机上呢?有可能显示不全或者按钮挤压到一起去了。那么,就要动态计算小屏幕的宽高了。比如说UI给了我们的UI图的宽高比是360640,那么我们如何来动态计算屏幕的宽高比呢?那么,X轴的缩放比率就不应该是94/DensityUtils.px2dp(context, width))了,而应该是(94(DensityUtils.px2dp(context, width))/360))/DensityUtils.px2dp(context, width)),就是说我们应该计算UI图的宽和手机实际的宽的比率乘以94再除以屏幕的宽,这才是我们需要缩放的宽。那么高呢?当然是一样的了166 / DensityUtils.px2dp(context, height)),这样写当然也是有问题的了,而应该是(166*(DensityUtils.px2dp(context, height))/640))/DensityUtils.px2dp(context, height)),这样才是更加严谨的写法。
/**
* 动画
*/
public static void scanAnimation(Context context, FrameLayout mLytAll) {
WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
DisplayMetrics dm = new DisplayMetrics();
wm.getDefaultDisplay().getMetrics(dm);
final int width = dm.widthPixels;// 屏幕宽度(像素)
int height = dm.heightPixels;// 屏幕高度(像素)
ObjectAnimator translationX = ObjectAnimator.ofFloat(mLytAll,
"translationX", 1,
(DensityUtils.px2dp(context, width) - 36));
ObjectAnimator translationY = ObjectAnimator.ofFloat(mLytAll,
"translationY", 1,
-(DensityUtils.px2dp(context, height) - 23));
ObjectAnimator scaleX = ObjectAnimator.ofFloat(mLytAll,
"scaleX", ((94(DensityUtils.px2dp(context, width)/360)) / DensityUtils.px2dp(context, width)));
ObjectAnimator scaleY = ObjectAnimator.ofFloat(mLytAll,
"scaleY", ((166(DensityUtils.px2dp(context, height)/640)) / DensityUtils.px2dp(context, height)));
AnimatorSet animatorSet = new AnimatorSet(); //组合动画
animatorSet.playTogether(translationX, translationY, scaleX, scaleY); //设置动画
animatorSet.setDuration(350);
animatorSet.start();
}
最后
好了,今天就写到这里,如果有不正确的还希望各位指正,小可这里不胜感激,还有就是,能用属性动画实现的,尽量不要用补间动画来做,因为你会碰到各种坑。