我正在编写一个 Android 应用程序,用户必须使用
ChipGroup
和 Chips
选择一个选项。
一切工作正常,只是有点笨拙,因为除了选择 Chip
时的默认波纹之外没有动画。
我已阅读Material Design 3 文档并且发现此视频展示了我想要实现的漂亮动画,但我不知道如何实现。
我已经尝试过:
启用
android:animateLayoutChanges="true"
但这只是添加和删除
Chip
的动画,而不是检查和取消检查的动画。
使用
TransitionManager.beginDelayedTransition(chipGroup);
这在chipGroup上工作得很好,但是
Chip
的内容(刻度线出现和文本重新缩放)没有动画。
如果我做错了什么,请告诉我,这也是我用来添加和选择这些的方法
Chips
:
ChipAdapter adapter = new ChipAdapter(getContext());
for(int i = 0; i < adapter.getCount(); i++){
View chip = adapter.getView(i, chipGroup, chipGroup);
if(chip instanceof Chip) {
chip.setId(i);
chip.setOnClickListener(v -> {
for(int p = 0; p < chipGroup.getChildCount(); p++){
chipGroup.getChildAt(p).setSelected(false);
}
chip.setSelected(true);
});
chipGroup.addView(chip);
}
}
更新: 附加 Jetpack Compose 答案。
据我所知,没有嵌入方法可以简单地启用此动画,但我找到了两种方法来模仿链接视频中显示的动画。
此选项的工作原理是启用包含芯片的 ChipGroup 的
animateLayoutChanges
选项
android:animateLayoutChanges="true"
并为您的
ChipGroup
添加以下代码:
for (view in chipGroup.children) {
val chip = view as Chip
chip.setOnCheckedChangeListener { buttonView, _ ->
val index = chipGroup.indexOfChild(buttonView)
chipGroup.removeView(buttonView)
chipGroup.addView(buttonView, index)
}
}
每当芯片的选择状态发生变化时,此代码将自动删除芯片并立即将其添加回到ChipGroup。
对于此选项,请将以下 自定义芯片类 (Kotlin) 添加到您的项目中,并将您的芯片更改为
CheckAnimationChip
而不是 com.google.android.material.chip.Chip
的实例:
import android.animation.ObjectAnimator
import android.content.Context
import android.util.AttributeSet
import androidx.core.animation.doOnEnd
import com.google.android.material.chip.Chip
import com.google.android.material.chip.ChipDrawable
private const val CHIP_ICON_SIZE_PROPERTY_NAME = "chipIconSize"
// A value of '0f' would be interpreted as 'use the default size' by the ChipDrawable, so use a slightly larger value.
private const val INVISIBLE_CHIP_ICON_SIZE = 0.00001f
/**
* Custom Chip class which will animate transition between the [isChecked] states.
*/
class CheckAnimationChip @JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null,
defStyleAttr: Int = com.google.android.material.R.attr.chipStyle
) : Chip(context, attrs, defStyleAttr) {
private var onCheckedChangeListener: OnCheckedChangeListener? = null
private var _chipDrawable: ChipDrawable
private var defaultCheckedIconSize: Float
private var currentlyScalingDown = false
var animationDuration = 200L
init {
// Set default values for this category of chip.
isCheckable = true
isCheckedIconVisible = true
_chipDrawable = chipDrawable as ChipDrawable
defaultCheckedIconSize = _chipDrawable.chipIconSize
super.setOnCheckedChangeListener { buttonView, isChecked ->
if (currentlyScalingDown) {
// Block the changes caused by the scaling-down animation.
return@setOnCheckedChangeListener
}
onCheckedChangeListener?.onCheckedChanged(buttonView, isChecked)
if (isChecked) {
scaleCheckedIconUp()
} else if (!isChecked) {
scaleCheckedIconDown()
}
}
}
/**
* Scale the size of the Checked-Icon from invisible to its default size.
*/
private fun scaleCheckedIconUp() {
ObjectAnimator.ofFloat(_chipDrawable, CHIP_ICON_SIZE_PROPERTY_NAME,
INVISIBLE_CHIP_ICON_SIZE, defaultCheckedIconSize)
.apply {
duration = animationDuration
start()
doOnEnd {
_chipDrawable.chipIconSize = defaultCheckedIconSize
}
}
}
/**
* Scale the size of the Checked-Icon from its default size down to invisible. To achieve this, the
* [isChecked] property needs to be manipulated. It is set to be true till the animation has ended.
*/
private fun scaleCheckedIconDown() {
currentlyScalingDown = true
isChecked = true
ObjectAnimator.ofFloat(_chipDrawable, CHIP_ICON_SIZE_PROPERTY_NAME,
defaultCheckedIconSize, INVISIBLE_CHIP_ICON_SIZE)
.apply {
duration = animationDuration
start()
doOnEnd {
isChecked = false
currentlyScalingDown = false
_chipDrawable.chipIconSize = defaultCheckedIconSize
}
}
}
override fun setOnCheckedChangeListener(listener: OnCheckedChangeListener?) {
onCheckedChangeListener = listener
}
}
此类使用ObjectAnimator
更改芯片图标的大小。因此,它访问芯片的
ChipDrawable
并使用动画器更改 chipIconSize
属性。
在 Jetpack Compose 中,您可以使用
animateConentSize()
修饰符:
FilterChip(
selected = selected,
onClick = { /* Handle Click */ },
leadingIcon = {
Box(
Modifier.animateContentSize(keyframes { durationMillis = 200 })
) {
if (selected) {
Icon(
imageVector = Icons.Default.Done,
contentDescription = null,
modifier = Modifier.size(FilterChipDefaults.IconSize)
)
}
}
},
label = { /* Text */ }
)
这里重要的部分是始终有一个用于
Box
的可组合项(此处为 leadingIcon
),如果选择了芯片,则它保存复选图标,如果没有选择,则为空。然后可以使用 animateContentSize()
修改器平滑地设置该可组合项的动画效果。
使用内置的布局过渡,您可以实现非常令人满意的动画像这样。
首先,将
animateLayoutChanges="true"
添加到 XML 中的 ChipGroup。 这很重要,因为它实际上会向您的视图添加一个 LayoutTransition,否则您将得到一个空指针异常。
现在在您的 Activity 或 Fragment 中,您可以为 ChipGroup 设置转换类型:
ChipGroup filterChipGroup = view.findViewById(R.id.chip_group);
filterChipGroup.getLayoutTransition().enableTransitionType(LayoutTransition.CHANGING);
您可以使用任何类型的 ViewGroup 来执行此操作。 (例如 LinearLayout、ConstraintLayout ...)