Android_Banner.jpg
简介
本节中我们介绍下给RecyclerView中的Item添加动画。
添加的动画,分为,在打开列表时有Item的展示动画,当滑动的时候没有动画
和打开列表滑动时有动画两种
实现过程
实现一个列表
效果如下
Screenshot_2020-09-01-17-03-35-349_com.dashingqi.module.recyclerview.png
接下来我们就要操作这个列表中的Item,让其产生动画
布局的实现代码
main_activity.xml 的布局文件
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools">
name="viewModel"
type="com.dashingqi.module.recyclerview.RvAnimationViewModel" />
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".RvAnimationActivity">
android:id="@+id/animRv"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager" />
MainActivity.kt中的代码
lass RvAnimationActivity : AppCompatActivity() {
private val animationDownToUp by lazy {
AnimationUtils.loadAnimation(this, R.anim.item_anim_down_to_up)
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val dataBinding = DataBindingUtil.setContentView(
this,
R.layout.activity_rv_animation
)
//获取到ViewModel的实例
val viewModel = ViewModelProvider(this)[RvAnimationViewModel::class.java]
//绑定ViewModel
dataBinding.viewModel = viewModel
//设置适配器
var adapter = RvAnimationAdapter(viewModel.items, animRv)
animRv.adapter = adapter
// 为Rv中的Item添加装饰器
animRv.addItemDecoration(object : RecyclerView.ItemDecoration() {
override fun getItemOffsets(
outRect: Rect,
view: View,
parent: RecyclerView,
state: RecyclerView.State
) {
val childPosition = parent.getChildAdapterPosition(view)
if (childPosition != 0) {
outRect.top = DensityUtils.dip2pxInt(parent.context, 16f)
}
}
})
}
}
ViewModel中的代码
class RvAnimationViewModel : ViewModel() {
val items = ObservableArrayList()
val itemBinding = ItemBinding.of(BR.item, R.layout.item_anim_view)
init {
for (index in 0 until 80) {
items.add("Item${index}")
}
}
}
Adapter中的代码
class RvAnimationAdapter(var datas: ArrayList, var recyclerView: RecyclerView) :
RecyclerView.Adapter() {
class MyViewHolder(var dataBinding: ItemAnimViewBinding) :
RecyclerView.ViewHolder(dataBinding.root) {
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyViewHolder {
val dataBinding = DataBindingUtil.inflate(
LayoutInflater.from(parent.context),
R.layout.item_anim_view,
parent,
false
)
return MyViewHolder(dataBinding)
}
override fun getItemCount(): Int {
return datas.size
}
override fun onBindViewHolder(holder: MyViewHolder, position: Int) {
val itemDataBinding = holder.dataBinding as ItemAnimViewBinding
itemDataBinding.item = datas[position]
itemDataBinding.executePendingBindings()
}
}
item的布局文件
xmlns:app="http://schemas.android.com/apk/res-auto">
name="item"
type="String" />
android:layout_width="match_parent"
android:layout_height="wrap_content">
android:layout_width="match_parent"
android:layout_height="240dp"
android:background="@android:color/holo_red_dark"
android:text="@{item}"
android:gravity="center"
android:textColor="#ffffff"
android:textSize="20sp"
android:textStyle="bold"
app:layout_constraintTop_toTopOf="parent" />
实现进入列表时的动画
实现效果如下
list_start.gif
该动画是从右侧平移到屏幕中,所以我们的平移动画的X轴的起点从 100%开始,终止点为0 ,y不变
android:duration="500">
android:fromXDelta="100%p"
android:fromYDelta="0"
android:toXDelta="0"
android:toYDelta="0" />
android:fromAlpha="0"
android:toAlpha="100" />
接着借助LayoutAnimationController 给 RV的layoutAnimation设置动画数据
val viewModel = ViewModelProvider(this)[RvAnimationViewModel::class.java]
dataBinding.viewModel = viewModel
var adapter = RvAnimationAdapter(viewModel.items, animRv)
animRv.adapter = adapter
var animation = AnimationUtils.loadAnimation(this, R.anim.item_anim_translate)
val layoutAnimationController = LayoutAnimationController(animation)
//设置顺序
layoutAnimationController.order = LayoutAnimationController.ORDER_NORMAL
animRv.layoutAnimation = layoutAnimationController
滑动的时候带有动画
效果如下
list_scroll.gif
上图中的效果,是在ItemView可见的时候执行动画,我们的切入点也就是这个时机,正好Adapter中有提供一个方法onViewAttachedToWindow()
onViewAttachedToWindow() 是当Adapter创建好的View依附在Window的时候调用的,所以这个方法是一个时机,在这个方法中,为每一个ItemView设置动画。
同时我们还需要为Rv设置滑动的监听事件,来记录滑动的方向,这样在onViewAttachedToWindow()方法中设置不同的动画
所以Adapter中的代码变更如下
class RvAnimationAdapter(var datas: ArrayList, var recyclerView: RecyclerView) :
RecyclerView.Adapter() {
/**
* 用来记录当前是向上滑动的
*/
var isScrollUp = false
/**
* 用来记录当前是向下滑动的
*/
var isScrollDown = false
/**
* 动画
*/
private val animation by lazy {
AnimationUtils.loadAnimation(recyclerView.context, R.anim.item_anim_translate)
}
private val animationUpToDown by lazy {
AnimationUtils.loadAnimation(recyclerView.context, R.anim.item_anim_up_to_down)
}
private val animationDownToUp by lazy {
AnimationUtils.loadAnimation(recyclerView.context, R.anim.item_anim_down_to_up)
}
init {
//为RV添加滑动事件的监听
recyclerView.addOnScrollListener(object : RecyclerView.OnScrollListener() {
override fun onScrollStateChanged(recyclerView: RecyclerView, newState: Int) {
super.onScrollStateChanged(recyclerView, newState)
}
override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
super.onScrolled(recyclerView, dx, dy)
isScrollUp = dy > 0
isScrollDown = dy < 0
}
})
}
class MyViewHolder(var dataBinding: ItemAnimViewBinding) :
RecyclerView.ViewHolder(dataBinding.root) {
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyViewHolder {
val dataBinding = DataBindingUtil.inflate(
LayoutInflater.from(parent.context),
R.layout.item_anim_view,
parent,
false
)
return MyViewHolder(dataBinding)
}
override fun getItemCount(): Int {
return datas.size
}
override fun onBindViewHolder(holder: MyViewHolder, position: Int) {
val itemDataBinding = holder.dataBinding as ItemAnimViewBinding
itemDataBinding.item = datas[position]
itemDataBinding.executePendingBindings()
}
/**
* 当创建好的View依附到Window上时回调的
*/
override fun onViewAttachedToWindow(holder: MyViewHolder) {
super.onViewAttachedToWindow(holder)
for (index in 0 until recyclerView.childCount) {
//获取到Item
val itemView = recyclerView.getChildAt(index)
//清除每一个Item上的动画
itemView?.clearAnimation()
}
// 当向上滑动的时候
if (isScrollUp) {
holder.itemView.startAnimation(animationDownToUp)
}
//当向下滑动的时候
if (isScrollDown) {
holder.itemView.startAnimation(animationUpToDown)
}
}
}
对应的动画文件
android:duration="200">
android:fromYDelta="100%"
android:toYDelta="0" />
android:fromAlpha="0"
android:toAlpha="1" />
android:duration="200"
>
android:fromYDelta="-100%"
android:toYDelta="0" />
android:fromAlpha="0"
android:toAlpha="1" />