recyclerview item动画_这可能是你见过的迄今为止最简单的RecyclerView Item加载动画...
如何實(shí)現(xiàn)RecyclerView Item動(dòng)畫?
這個(gè)問(wèn)題想必有很多人都會(huì)講,我可以用ItemAnimator實(shí)現(xiàn)啊,這是RecyclerView官方定義的接口,專門擴(kuò)展Item動(dòng)畫的,那我為什么要尋求另外一種方法實(shí)現(xiàn)呢?因?yàn)樽罱此剂艘粋€(gè)問(wèn)題,其實(shí)很多人都有這個(gè)思維定律,那就是官方的一定是好的,真的是這樣嗎?下面我來(lái)從另一個(gè)角度說(shuō)明官方的ItemAnimator是真的不好用
ItemAnimator 棄用理由
理由一
- 第一張圖是最牛逼的星星最多的wasabeef/recyclerview-animators,基類有713行代碼,你知道這個(gè)類打包出來(lái)多大嗎?有20多kb,相當(dāng)恐怖的好嗎?
- 第二個(gè)是官方提供的默認(rèn)動(dòng)畫,也是將近700行
理由是:代碼過(guò)于臃腫
理由二
既然我想用ItemAnimator接口,且官方有一個(gè)DefaultItemAnimator,為什么我不能擴(kuò)展DefaultItemAnimator,而是要實(shí)現(xiàn)SimpleItemAnimator,寫個(gè)700行代碼才能夠捋明白一個(gè)Item的動(dòng)畫?總之,當(dāng)我知道繼承SimpleItemAnimator后,實(shí)現(xiàn)的和DefaultItemAnimator幾乎一樣的時(shí)候,我內(nèi)心是拒絕的,我不想看到這些冗余的代碼,也許是我有那么一點(diǎn)點(diǎn)潔癖
理由是:復(fù)用率太低,感覺官方根本沒當(dāng)回事(也許是我學(xué)習(xí)沒到位,沒有看到它好的地方)
理由三
notifyDataSetChanged不支持ItemAnimator動(dòng)畫,我不討論這么設(shè)計(jì)是真的好和壞,但起碼它是我不選擇ItemAnimator的另一個(gè)理由
等等吧,不知道還能吐槽什么了?雖然有這些缺點(diǎn),可我們總會(huì)遇到不得不用的時(shí)候,你說(shuō)呢?
layoutAnimation 棄用理由
這個(gè)用的比較少吧,大部分都是在用ItemAnimator,我們直接看個(gè)例子,然后再說(shuō)為什么要棄用它
step1
android:duration="@integer/anim_duration_medium">android:fromYDelta="-20%"
android:toYDelta="0"
android:interpolator="@android:anim/decelerate_interpolator"
/>
android:fromAlpha="0"
android:toAlpha="1"
android:interpolator="@android:anim/decelerate_interpolator"
/>
android:fromXScale="105%"
android:fromYScale="105%"
android:toXScale="100%"
android:toYScale="100%"
android:pivotX="50%"
android:pivotY="50%"
android:interpolator="@android:anim/decelerate_interpolator"
/>
step2
<?xml version="1.0" encoding="utf-8"?>xmlns:android="http://schemas.android.com/apk/res/android"
android:animation="@anim/item_animation_fall_down"
android:delay="15%"
android:animationOrder="normal"
/>
step3
int resId = R.anim.layout_animation_fall_down;LayoutAnimationController animation = AnimationUtils.loadLayoutAnimation(ctx, resId);
recyclerview.setLayoutAnimation(animation);
很簡(jiǎn)單對(duì)吧,當(dāng)我運(yùn)行demo的時(shí)候,似乎看起來(lái)效果很好哦,可最后我才發(fā)現(xiàn)一些問(wèn)題,我決定不使用它了
理由一
動(dòng)畫只加載第一屏?這個(gè)能不能改觀我沒有深入研究哦,可這樣一個(gè)效果我也無(wú)法忍受,但我上啦的時(shí)候,為什么下面未顯示的Item動(dòng)畫就沒了呢?這就是我棄用的理由
理由二
當(dāng)我用GridLayoutManger 的時(shí)候,我還要在定義一套 layhoutAnimation,雖然只是增加了一個(gè)xml文件,可這比起我接下來(lái)介紹的實(shí)現(xiàn)方案,那就差了一個(gè)檔次,所以我選擇棄用
最簡(jiǎn)單的Animation動(dòng)畫方案
這個(gè)方案的優(yōu)勢(shì):
- 代碼超級(jí)簡(jiǎn)潔
- 動(dòng)畫的定制度更高,沒一個(gè)Item都可以輕松的且變著花樣的加載動(dòng)畫
- 可實(shí)現(xiàn)預(yù)加載動(dòng)畫,可實(shí)現(xiàn)更新的動(dòng)畫
- 輕松實(shí)現(xiàn)一個(gè)接一個(gè)的加載動(dòng)畫
- 緩存更加輕量級(jí),減少內(nèi)存開銷
缺點(diǎn):當(dāng)然也有缺點(diǎn),這個(gè)看具體使用場(chǎng)景的取舍,也許是可以支持的,但目前我還沒有想到如何做到。
- 沒有刪除動(dòng)畫
- 沒有移動(dòng)動(dòng)畫
對(duì)的目前就這倆。
實(shí)現(xiàn)原理
很簡(jiǎn)單,就是給View加載一個(gè)Animation,通過(guò)xml配置
實(shí)現(xiàn)效果
代碼
step1
從下往上移動(dòng)的動(dòng)畫
<?xml version="1.0" encoding="utf-8"?>android:duration="@integer/anim_duration_long">
android:interpolator="@android:anim/accelerate_decelerate_interpolator"
android:fromYDelta="50%p"
android:toYDelta="0"
/>
android:fromAlpha="0"
android:toAlpha="1"
android:interpolator="@android:anim/accelerate_decelerate_interpolator"
/>
step2
//從零開始計(jì)數(shù),用來(lái)實(shí)現(xiàn)一個(gè)接一個(gè)的延遲動(dòng)畫(簡(jiǎn)單點(diǎn)就是:在一個(gè)加載一半時(shí),下一個(gè)才執(zhí)行)private var delayPosition = 0
//緩存Animation,避免重復(fù)loadAnimation,減少開銷
private val animationArray = SparseArray()
//加載xml動(dòng)畫,并放入緩存中
private fun loadAnimation(context: Context, @AnimRes itemAnimationRes: Int, key: Int): Animation {
return animationArray[key] ?: AnimationUtils.loadAnimation(context, itemAnimationRes).apply {
animationArray.append(key, this)
}
}
//清理緩存
fun RecyclerView.onDestroy(){
animationArray.clear()
}
//執(zhí)行動(dòng)畫
fun RecyclerView.ViewHolder.animationWithDelayOffset(
isEnableAnimation: Boolean,
@AnimRes itemAnimationRes: Int,
delayOffset: Int
) {
if (isEnableAnimation) {
//清理調(diào)之前的動(dòng)畫
itemView.clearAnimation()
//當(dāng)前item positon
val currentPosition = ++delayPosition
//計(jì)算下一個(gè)Item需要delay的時(shí)間
val delay = currentPosition * delayOffset / 2
itemView.animation =
loadAnimation(itemView.context, itemAnimationRes, currentPosition).apply {
//延遲多久開始執(zhí)行
startOffset = delay.toLong()
//加載完成后將計(jì)數(shù)制成零
setAnimationListener(object : Animation.AnimationListener {
override fun onAnimationRepeat(p0: Animation?) {
}
override fun onAnimationEnd(p0: Animation?) {
delayPosition = 0
}
override fun onAnimationStart(p0: Animation?) {
}
})
//如果已經(jīng)執(zhí)行一次才會(huì)調(diào)用start,因?yàn)榈谝淮斡肁nimationUtils.loadAnimation加載的時(shí)候會(huì)自動(dòng)執(zhí)行一次
if (this.hasEnded()) {
this.start()
}
}
}
}
//這個(gè)是我的DefaultViewHolder,我可以拿到ViewModel是否是第一次isFirstInit,這樣就可以實(shí)現(xiàn)只有第一次初始化后才會(huì)執(zhí)行哦
fun DefaultViewHolder.firstAnimation(
@AnimRes itemAnimationRes: Int = R.anim.item_animation_from_right,
delayOffset: Int = 200
) = animationWithDelayOffset(
getViewModel()?.isFirstInit ?: false,
itemAnimationRes,
delayOffset
)
//拿到ViewModel是否是第一次isFirstInit,這樣就可以實(shí)現(xiàn)只有第二次加載執(zhí)行哦
fun DefaultViewHolder.updateAnimation(
@AnimRes itemAnimationRes: Int = R.anim.item_animation_scale
) = animation(!(getViewModel()?.isFirstInit ?: false), itemAnimationRes)
代碼里有個(gè)細(xì)節(jié)處理《加載完成后將計(jì)數(shù)制成零》,這里是因?yàn)?#xff0c;你第一次進(jìn)頁(yè)面的時(shí)候,所有的Item的都按照順序執(zhí)行完畢后,由于delay數(shù)很大,導(dǎo)致你滑動(dòng)的時(shí)候,會(huì)出現(xiàn)很久才加載進(jìn)來(lái)的動(dòng)畫哦,這里我是想用handler的postdelay實(shí)現(xiàn),這樣就可以做到只要有接著的動(dòng)畫執(zhí)行,就不會(huì)被重制成0,保證下次動(dòng)畫的執(zhí)行一定是在上一個(gè)Item的后面,這樣確實(shí)是一個(gè)問(wèn)題,也許在我驗(yàn)證夠多的場(chǎng)景后就切過(guò)去了,嘿嘿。但目前來(lái)看,這個(gè)效果實(shí)現(xiàn)的很滿意,慢慢重構(gòu)和完善。當(dāng)然我的這種實(shí)現(xiàn)方式,確實(shí)是比較簡(jiǎn)單而且效果還不錯(cuò),既有LayoutAnimation的影子,又有ItemAnimator的功能,豈不是很不錯(cuò)。
step3
應(yīng)用,一行代碼搞定
配置更新時(shí)候的動(dòng)畫,一行搞定
感覺到簡(jiǎn)單了吧,這么順滑的動(dòng)畫實(shí)現(xiàn),你不想體驗(yàn)下嗎?
Demo地址,歡迎體驗(yàn)
https://github.com/ibaozi-cn/RecyclerViewAdapter
接下來(lái)會(huì)解決什么問(wèn)題?
這么用難道確實(shí)比ItemAnimator好嗎?當(dāng)然會(huì)有一些問(wèn)題吧,比如什么時(shí)候需要中斷,什么時(shí)候需要重新加載,甚至到底什么時(shí)候清理掉緩存更合理呢?之后還需要一些更全面的實(shí)戰(zhàn)來(lái)解決這問(wèn)題,也會(huì)借助ItemAnimator的實(shí)現(xiàn)原則來(lái)考慮當(dāng)前動(dòng)畫如何做到合理的生命周期管理。
作者
i校長(zhǎng)
- 簡(jiǎn)書 https://www.jianshu.com/u/77699cd41b28
- 掘金 https://juejin.im/user/131597127135687
- 個(gè)人網(wǎng)站 http://jetpack.net.cn ?、 http://ibaozi.cn
總結(jié)
以上是生活随笔為你收集整理的recyclerview item动画_这可能是你见过的迄今为止最简单的RecyclerView Item加载动画...的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 下列选项中不符合python语言变量命名
- 下一篇: 青海师大c语言研究生专业课,2016年青