
简介
- 本节中我们介绍下给RecyclerView中的Item添加动画。
- 添加的动画,分为,在打开列表时有Item的展示动画,当滑动的时候没有动画
和打开列表滑动时有动画两种
实现过程
实现一个列表
-
效果如下
Screenshot_2020-09-01-17-03-35-349_com.dashingqi.module.recyclerview.png
- 接下来我们就要操作这个列表中的Item,让其产生动画
布局的实现代码
- main_activity.xml 的布局文件
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools">
<data>
<variable
name="viewModel"
type="com.dashingqi.module.recyclerview.RvAnimationViewModel" />
</data>
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".RvAnimationActivity">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/animRv"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager" />
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>
- 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<ActivityRvAnimationBinding>(
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<String>()
val itemBinding = ItemBinding.of<String>(BR.item, R.layout.item_anim_view)
init {
for (index in 0 until 80) {
items.add("Item${index}")
}
}
}
- Adapter中的代码
class RvAnimationAdapter(var datas: ArrayList<String>, var recyclerView: RecyclerView) :
RecyclerView.Adapter<RvAnimationAdapter.MyViewHolder>() {
class MyViewHolder(var dataBinding: ItemAnimViewBinding) :
RecyclerView.ViewHolder(dataBinding.root) {
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyViewHolder {
val dataBinding = DataBindingUtil.inflate<ItemAnimViewBinding>(
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的布局文件
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<data>
<variable
name="item"
type="String" />
</data>
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
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" />
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>
实现进入列表时的动画
-
实现效果如下
list_start.gif
- 该动画是从右侧平移到屏幕中,所以我们的平移动画的X轴的起点从 100%开始,终止点为0 ,y不变
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="500">
<translate
android:fromXDelta="100%p"
android:fromYDelta="0"
android:toXDelta="0"
android:toYDelta="0" />
<alpha
android:fromAlpha="0"
android:toAlpha="100" />
</set>
- 接着借助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<String>, var recyclerView: RecyclerView) :
RecyclerView.Adapter<RvAnimationAdapter.MyViewHolder>() {
/**
* 用来记录当前是向上滑动的
*/
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<ItemAnimViewBinding>(
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)
}
}
}
- 对应的动画文件
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="200">
<translate
android:fromYDelta="100%"
android:toYDelta="0" />
<alpha
android:fromAlpha="0"
android:toAlpha="1" />
</set>
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="200"
>
<translate
android:fromYDelta="-100%"
android:toYDelta="0" />
<alpha
android:fromAlpha="0"
android:toAlpha="1" />
</set>
网友评论