Android_Banner.jpg
簡(jiǎn)介
- 本節(jié)中我們介紹下給RecyclerView中的Item添加動(dòng)畫报慕。
- 添加的動(dòng)畫厕宗,分為徒恋,在打開列表時(shí)有Item的展示動(dòng)畫辕近,當(dāng)滑動(dòng)的時(shí)候沒有動(dòng)畫
和打開列表滑動(dòng)時(shí)有動(dòng)畫兩種
實(shí)現(xiàn)過程
實(shí)現(xiàn)一個(gè)列表
-
效果如下
Screenshot_2020-09-01-17-03-35-349_com.dashingqi.module.recyclerview.png - 接下來我們就要操作這個(gè)列表中的Item封孙,讓其產(chǎn)生動(dòng)畫
布局的實(shí)現(xiàn)代碼
- 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的實(shí)例
val viewModel = ViewModelProvider(this)[RvAnimationViewModel::class.java]
//綁定ViewModel
dataBinding.viewModel = viewModel
//設(shè)置適配器
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>
實(shí)現(xiàn)進(jìn)入列表時(shí)的動(dòng)畫
-
實(shí)現(xiàn)效果如下
list_start.gif - 該動(dòng)畫是從右側(cè)平移到屏幕中迹冤,所以我們的平移動(dòng)畫的X軸的起點(diǎn)從 100%開始,終止點(diǎn)為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設(shè)置動(dòng)畫數(shù)據(jù)
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)
//設(shè)置順序
layoutAnimationController.order = LayoutAnimationController.ORDER_NORMAL
animRv.layoutAnimation = layoutAnimationController
滑動(dòng)的時(shí)候帶有動(dòng)畫
-
效果如下
list_scroll.gif 上圖中的效果泡徙,是在ItemView可見的時(shí)候執(zhí)行動(dòng)畫,我們的切入點(diǎn)也就是這個(gè)時(shí)機(jī)膜蠢,正好Adapter中有提供一個(gè)方法onViewAttachedToWindow()
onViewAttachedToWindow() 是當(dāng)Adapter創(chuàng)建好的View依附在Window的時(shí)候調(diào)用的堪藐,所以這個(gè)方法是一個(gè)時(shí)機(jī),在這個(gè)方法中挑围,為每一個(gè)ItemView設(shè)置動(dòng)畫礁竞。
同時(shí)我們還需要為Rv設(shè)置滑動(dòng)的監(jiān)聽事件,來記錄滑動(dòng)的方向杉辙,這樣在onViewAttachedToWindow()方法中設(shè)置不同的動(dòng)畫
所以Adapter中的代碼變更如下
class RvAnimationAdapter(var datas: ArrayList<String>, var recyclerView: RecyclerView) :
RecyclerView.Adapter<RvAnimationAdapter.MyViewHolder>() {
/**
* 用來記錄當(dāng)前是向上滑動(dòng)的
*/
var isScrollUp = false
/**
* 用來記錄當(dāng)前是向下滑動(dòng)的
*/
var isScrollDown = false
/**
* 動(dòng)畫
*/
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添加滑動(dòng)事件的監(jiān)聽
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()
}
/**
* 當(dāng)創(chuàng)建好的View依附到Window上時(shí)回調(diào)的
*/
override fun onViewAttachedToWindow(holder: MyViewHolder) {
super.onViewAttachedToWindow(holder)
for (index in 0 until recyclerView.childCount) {
//獲取到Item
val itemView = recyclerView.getChildAt(index)
//清除每一個(gè)Item上的動(dòng)畫
itemView?.clearAnimation()
}
// 當(dāng)向上滑動(dòng)的時(shí)候
if (isScrollUp) {
holder.itemView.startAnimation(animationDownToUp)
}
//當(dāng)向下滑動(dòng)的時(shí)候
if (isScrollDown) {
holder.itemView.startAnimation(animationUpToDown)
}
}
}
- 對(duì)應(yīng)的動(dòng)畫文件
<?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>