如何结合缩放动画改变View的z坐标实现阴影插值效果?
实现缩放+阴影同步插值的按钮悬停效果
当然可以实现,你需要用属性动画替代当前的补间动画——旧补间动画只修改View的绘制状态,没法联动elevation(对应你说的z坐标)的插值变化,而属性动画能直接修改View的真实属性,支持多属性同步动画,自动完成过渡插值。
下面给两种实现方案:
方案1:XML属性动画实现
- 首先创建属性动画文件(注意放在
res/animator目录下,不是anim目录):res/animator/anim_hover.xml
<?xml version="1.0" encoding="utf-8"?> <set xmlns:android="http://schemas.android.com/apk/res/android" android:interpolator="@android:anim/linear_interpolator"> <!-- 缩放X/Y轴动画 --> <objectAnimator android:duration="80" android:propertyName="scaleX" android:valueFrom="1.0" android:valueTo="1.031" android:valueType="floatType"/> <objectAnimator android:duration="80" android:propertyName="scaleY" android:valueFrom="1.0" android:valueTo="1.031" android:valueType="floatType"/> <!-- Elevation(z坐标)动画,根据需求调整起始/结束值 --> <objectAnimator android:duration="80" android:propertyName="elevation" android:valueFrom="@dimen/default_btn_elevation" android:valueTo="@dimen/hover_btn_elevation" android:valueType="floatType"/> </set>
- 在
res/values/dimens.xml中定义阴影的尺寸值:
<dimen name="default_btn_elevation">4dp</dimen> <dimen name="hover_btn_elevation">8dp</dimen>
- 给按钮添加触摸监听,触发/反向动画:
button.setOnTouchListener { v, event -> when(event.action) { MotionEvent.ACTION_DOWN -> { val animator = AnimatorInflater.loadAnimator(context, R.animator.anim_hover) animator.target = v animator.start() } MotionEvent.ACTION_UP, MotionEvent.ACTION_CANCEL -> { val animator = AnimatorInflater.loadAnimator(context, R.animator.anim_hover) animator.target = v animator.reverse() } } true }
方案2:代码动态创建属性动画
如果不想写XML,也可以直接在代码中组合动画:
button.setOnTouchListener { v, event -> when(event.action) { MotionEvent.ACTION_DOWN -> { // 缩放动画 val scaleXAnim = ObjectAnimator.ofFloat(v, "scaleX", 1f, 1.031f) val scaleYAnim = ObjectAnimator.ofFloat(v, "scaleY", 1f, 1.031f) // 阴影动画 val elevationAnim = ObjectAnimator.ofFloat(v, "elevation", resources.getDimension(R.dimen.default_btn_elevation), resources.getDimension(R.dimen.hover_btn_elevation)) // 组合动画并启动 val animSet = AnimatorSet() animSet.interpolator = LinearInterpolator() animSet.duration = 80 animSet.playTogether(scaleXAnim, scaleYAnim, elevationAnim) animSet.start() } MotionEvent.ACTION_UP, MotionEvent.ACTION_CANCEL -> { // 反向恢复动画 val scaleXAnim = ObjectAnimator.ofFloat(v, "scaleX", 1.031f, 1f) val scaleYAnim = ObjectAnimator.ofFloat(v, "scaleY", 1.031f, 1f) val elevationAnim = ObjectAnimator.ofFloat(v, "elevation", resources.getDimension(R.dimen.hover_btn_elevation), resources.getDimension(R.dimen.default_btn_elevation)) val animSet = AnimatorSet() animSet.interpolator = LinearInterpolator() animSet.duration = 80 animSet.playTogether(scaleXAnim, scaleYAnim, elevationAnim) animSet.start() } } true }
注意事项
- 属性动画会修改View的真实属性,比如缩放后点击区域会跟着变化(虽然你这个缩放比例很小,影响不大),比旧补间动画更规范。
elevation的值可以自行调整,数值越大阴影越明显,根据你的视觉需求来改。- 属性动画支持API11及以上,现在大部分项目的minSdk都满足这个要求。
内容的提问来源于stack exchange,提问作者slowcoder
相关产品推荐
相关产品推荐

