RecyclerView基礎(chǔ)篇-Item添加動(dòng)畫

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>
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末模捂,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌枫绅,老刑警劉巖泉孩,帶你破解...
    沈念sama閱讀 219,039評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異并淋,居然都是意外死亡寓搬,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,426評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門县耽,熙熙樓的掌柜王于貴愁眉苦臉地迎上來句喷,“玉大人,你說我怎么就攤上這事兔毙⊥偾恚” “怎么了?”我有些...
    開封第一講書人閱讀 165,417評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵澎剥,是天一觀的道長(zhǎng)锡溯。 經(jīng)常有香客問我,道長(zhǎng)哑姚,這世上最難降的妖魔是什么祭饭? 我笑而不...
    開封第一講書人閱讀 58,868評(píng)論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮叙量,結(jié)果婚禮上倡蝙,老公的妹妹穿的比我還像新娘。我一直安慰自己绞佩,他們只是感情好寺鸥,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,892評(píng)論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著品山,像睡著了一般胆建。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上肘交,一...
    開封第一講書人閱讀 51,692評(píng)論 1 305
  • 那天眼坏,我揣著相機(jī)與錄音,去河邊找鬼酸些。 笑死宰译,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的魄懂。 我是一名探鬼主播沿侈,決...
    沈念sama閱讀 40,416評(píng)論 3 419
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼市栗!你這毒婦竟也來了缀拭?” 一聲冷哼從身側(cè)響起咳短,我...
    開封第一講書人閱讀 39,326評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎蛛淋,沒想到半個(gè)月后咙好,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,782評(píng)論 1 316
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡褐荷,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,957評(píng)論 3 337
  • 正文 我和宋清朗相戀三年勾效,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片叛甫。...
    茶點(diǎn)故事閱讀 40,102評(píng)論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡层宫,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出其监,到底是詐尸還是另有隱情萌腿,我是刑警寧澤,帶...
    沈念sama閱讀 35,790評(píng)論 5 346
  • 正文 年R本政府宣布抖苦,位于F島的核電站毁菱,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏锌历。R本人自食惡果不足惜贮庞,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,442評(píng)論 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望辩涝。 院中可真熱鬧贸伐,春花似錦勘天、人聲如沸怔揩。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,996評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽商膊。三九已至,卻和暖如春宠进,著一層夾襖步出監(jiān)牢的瞬間晕拆,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,113評(píng)論 1 272
  • 我被黑心中介騙來泰國(guó)打工材蹬, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留实幕,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,332評(píng)論 3 373
  • 正文 我出身青樓堤器,卻偏偏與公主長(zhǎng)得像昆庇,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子闸溃,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,044評(píng)論 2 355