保留CardView圆角状态样式并添加ShineAnimation的方法
问题背景
我原本使用如下CardView布局:
<androidx.cardview.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android" xmlns:card_view="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:id="@+id/cardRoot" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginVertical="8dp" card_view:cardCornerRadius="16dp" card_view:contentPaddingLeft="15dp" card_view:contentPaddingRight="15dp" > <Text...> />
并在Adapter中通过updateStyle方法手动处理点击时的样式切换:
private fun updateStyle(isSelected: Boolean) { if (isSelected) { binding.title.setTextColor( ContextCompat.getColor( binding.root.context, R.color.blue, ), ) binding.cardRoot.setBackgroundColor( ContextCompat.getColor( binding.root.context, R.color.blue_disabled, ), ) } else { binding.title.setTextColor( ContextCompat.getColor( binding.root.context, R.color.dark_grey, ), ) binding.cardRoot.setBackgroundColor( ContextCompat.getColor( binding.root.context, R.color.grey, ), ) } }
当前实现运行正常,但现在需要添加**ShineAnimation(扫光动画)**效果。尝试用Button等其他View实现时,出现圆角与背景重叠的问题,参考相关方案后仍未解决。请问如何在保留原有功能(最好继续使用CardView)的前提下添加该动画效果?
解决方案
核心思路
CardView的圆角是通过裁剪实现的,直接在它上层加动画会导致动画内容超出圆角范围。所以需要把动画层放在CardView内部,让动画层继承CardView的圆角裁剪属性,避免重叠问题。
步骤1:修改CardView布局
在CardView内部添加一个承载扫光动画的View,铺满整个CardView,同时设置android:clipToOutline="true"继承圆角裁剪:
<androidx.cardview.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android" xmlns:card_view="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:id="@+id/cardRoot" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginVertical="8dp" card_view:cardCornerRadius="16dp" card_view:contentPaddingLeft="15dp" card_view:contentPaddingRight="15dp" android:clipChildren="true" <!-- 确保子View被裁剪 --> > <!-- 扫光动画层 --> <View android:id="@+id/shineLayer" android:layout_width="match_parent" android:layout_height="match_parent" android:background="@drawable/shine_gradient" android:clipToOutline="true" <!-- 继承CardView的圆角 --> android:visibility="gone" /> <!-- 默认隐藏 --> <!-- 原有的文本View --> <TextView android:id="@+id/title" android:layout_width="wrap_content" android:layout_height="wrap_content" android:paddingVertical="12dp" tools:text="示例文本" /> </androidx.cardview.widget.CardView>
步骤2:创建扫光渐变Drawable
在res/drawable下创建shine_gradient.xml,定义透明到高亮再到透明的线性渐变:
<shape xmlns:android="http://schemas.android.com/apk/res/android"> <gradient android:angle="0" android:endColor="@android:color/transparent" android:centerColor="#80FFFFFF" android:startColor="@android:color/transparent" /> </shape>
步骤3:实现ShineAnimation逻辑
在Adapter中添加动画控制方法,同时保留原有样式切换逻辑:
// 初始化扫光动画 private fun initShineAnimation() { val shineLayer = binding.shineLayer val anim = TranslateAnimation( Animation.RELATIVE_TO_SELF, -1.0f, Animation.RELATIVE_TO_SELF, 1.0f, Animation.RELATIVE_TO_SELF, 0.0f, Animation.RELATIVE_TO_SELF, 0.0f ) anim.duration = 1000 // 动画时长 anim.fillAfter = false anim.interpolator = AccelerateDecelerateInterpolator() shineLayer.animation = anim } // 点击触发动画+样式切换 fun onCardClicked(isSelected: Boolean) { updateStyle(isSelected) // 显示并播放扫光动画 binding.shineLayer.visibility = View.VISIBLE binding.shineLayer.animation.start() // 动画结束后隐藏扫光层 binding.shineLayer.animation.setAnimationListener(object : Animation.AnimationListener { override fun onAnimationStart(animation: Animation?) {} override fun onAnimationEnd(animation: Animation?) { binding.shineLayer.visibility = View.GONE } override fun onAnimationRepeat(animation: Animation?) {} }) } // 保留原有样式切换方法,修正CardView背景设置方式 private fun updateStyle(isSelected: Boolean) { if (isSelected) { binding.title.setTextColor( ContextCompat.getColor(binding.root.context, R.color.blue) ) binding.cardRoot.setCardBackgroundColor( ContextCompat.getColor(binding.root.context, R.color.blue_disabled) ) } else { binding.title.setTextColor( ContextCompat.getColor(binding.root.context, R.color.dark_grey) ) binding.cardRoot.setCardBackgroundColor( ContextCompat.getColor(binding.root.context, R.color.grey) ) } }
注意:这里把
setBackgroundColor替换为CardView专属的setCardBackgroundColor,避免直接设置背景破坏圆角结构。
步骤4:绑定点击事件
在Adapter的onBindViewHolder中初始化动画并设置点击逻辑:
override fun onBindViewHolder(holder: ViewHolder, position: Int) { val item = getItem(position) holder.binding.title.text = item.title // 初始化动画 holder.initShineAnimation() // 设置初始选中状态 holder.updateStyle(item.isSelected) // 绑定点击事件 holder.binding.cardRoot.setOnClickListener { item.isSelected = !item.isSelected holder.onCardClicked(item.isSelected) notifyItemChanged(position) } }
关键注意点
- 扫光层必须设置
android:clipToOutline="true",否则动画内容会超出CardView圆角。 - CardView要开启
android:clipChildren="true",确保子View被裁剪。 - 始终使用
setCardBackgroundColor修改CardView背景,不要直接调用setBackgroundColor。
内容的提问来源于stack exchange,提问作者StuartDTO
相关产品推荐
相关产品推荐

