Android动画实现控件形状、大小逐渐过渡
目录
一个小问题
动画实现
一个很简单的例子,现在有一个提示,需求是在下滑到它触顶时,让其去掉圆角,宽度占满
像这样子
一个小问题
这个提示的实现很简单,我们或许会想到线性布局,大概像下面这样子
但是这时候需要考虑一个问题,如果你的app支持多语言,会不会出现这种情况。
推送受限提示文字太长直接把后面的内容都挤出去了,如果你确定没问题,那线性布局确实会得到更好的性能,不然就要使用约束布局了。
效果如下
说完这个小问题,接下来进入正题
动画实现
只是做演示用,所以偷懒放在onResume了,使用一个定时器来回调用动画
public static int dp2px(float dp) {return (int)TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp, Resources.getSystem().getDisplayMetrics());
}
@Override
protected void onResume() {super.onResume();ConstraintLayout clPushTips = findViewById(R.id.cl_push_tips); //推送修复提示的布局ConstraintLayout.LayoutParams lp = (ConstraintLayout.LayoutParams)clPushTips.getLayoutParams(); //推送修复提示的布局参数int total_margin_dp = 20; //在顶部时左右margin 20dpint total_margin_px = dp2px(total_margin_dp);int total_radius_dp = 50; //在顶部时圆角 50dpint total_radius_px = dp2px(total_radius_dp);//其中两个参数为初始值和最后动画结束变成的值,传了圆角角度px//动画,最终圆角消失,左右margin设置0ValueAnimator animator_toRectangle = ValueAnimator.ofInt(total_radius_px, 0); //动画,最终圆角50dp,左右margin设置20dpValueAnimator animator_toRadius = ValueAnimator.ofInt(0, total_radius_px); animator_toRadius.setDuration(300); //动画时间300msanimator_toRectangle.setDuration(300);//动画插值器 (作用是决定如何在动画时间内,将传入的初始值参数 过渡到结束值参数) //有许多种,可以百度 ValueAnimator.setInterpolator//例如初始值100,结束值0 插值器决定是100 90 80... 或者100 90 70 40...animator_toRadius.setInterpolator(new AccelerateInterpolator()); animator_toRectangle.setInterpolator(new AccelerateInterpolator()); ValueAnimator.AnimatorUpdateListener animatorUpdateListener = new ValueAnimator.AnimatorUpdateListener() { //动画更新监听器GradientDrawable drawable = (GradientDrawable) getResources().getDrawable(R.drawable.shape_ffe1b9_bg_r50dp); //背景图片@Overridepublic void onAnimationUpdate(@NonNull ValueAnimator animation) {int radius_px = (int) animation.getAnimatedValue(); //本次动画更新,圆角角度px//当前对应margin的px值 //例如圆角从100-0 margin从40-0 那如果此次回调的圆角是50,那margin按比例是20 //(40*50/100)int cur_margin_px = total_margin_px * radius_px / total_radius_px; Log.d("123","onAnimationUpdate 当前圆角px= " + radius_px + " 当前margin_px= " + cur_margin_px);//根据动画进度值,设置过渡布局的背景圆角drawable.setCornerRadius(radius_px);clPushTips.setBackground(drawable);//根据动画进度值设置左右marginlp.setMarginStart(cur_margin_px);lp.setMarginEnd(cur_margin_px);clPushTips.setLayoutParams(lp);}};animator_toRadius.addUpdateListener(animatorUpdateListener); //添加动画更新监听器animator_toRectangle.addUpdateListener(animatorUpdateListener);new CountDownTimer(10000,1000) { //10s定时器,每秒调一次动画boolean tag = false;@Overridepublic void onTick(long millisUntilFinished) {if(tag){animator_toRadius.start();}else{animator_toRectangle.start();}tag = !tag;}@Overridepublic void onFinish() {}}.start(); }
效果如下
看一下log,现在应该对这个过程有所了解了,其实就是给初始值和结束值,然后按你需要,在一定时间内,通过某种趋势将初始值变到结束值,其中每一次变化都会调用一次onAnimationUpdate,在这个函数里面你使用此值做ui上的变化。
同时在这里我有一点要提醒一下,经过测试,如果手机性能较弱,同样从100-0,onAnimationUpdate调用次数会有差距,比较明显,会造成卡顿,相当于动画时间内能有几帧。
这个次数会受你动画时间长短影响和每次更新ui的工作量影响。例如你200ms,可能调6次,400ms可能是15次、20次,你如果在更新时进行setLayoutParams操作,和你设置背景图片的圆角比起来也会差很多。
说实话不太好用,感觉手机性能弱一些,就很容易卡顿。
在框框一顿操作之后,我还是选择用了其他方式模拟了动画效果,因为会卡,在修复提示的布局下,添加一个布局,颜色和它背景色一样,通过让它长度在MATCH_PARENT和WRAP_CONTENT间切换,实现类似动画效果
<!--可以拉伸的背景,置于推送修复提示下面,长的时候宽度满屏,让推送修复提示左右的空间被填充, 短的时候隐藏在推送修复提示之下,推送修复提示左右的空间又显示出来--><LinearLayoutandroid:id="@+id/ll_push_tips"android:layout_width="wrap_content"android:layout_height="0dp"android:background="#FFE1B9"android:gravity="center_vertical"android:orientation="horizontal"app:layout_constraintBottom_toBottomOf="@+id/cl_push_tips"app:layout_constraintLeft_toLeftOf="parent"app:layout_constraintRight_toRightOf="parent"app:layout_constraintTop_toTopOf="@+id/cl_push_tips"></LinearLayout>
这样子修复提示不动,只是让底下的一个布局变长度,流畅度会好很多,底下的布局高度和这个修复提示相同。
效果如下
先这样子,后续再看看有没有什么其他方法不卡的。