RecyclerView 源码分析(四)RecyclerView的动画机制

转载自琼珶和予

RecyclerView的动画机制

本文不会分析ItemAnimator相关的知识,而是理解RecyclerView怎么执行ItemAnimator的,有关ItemAniamtor的知识,后面我会写专门的文章来分析。

本文参考资料:

RecyclerView animations - AndroidDevSummit write-up
RecyclerView.ItemAnimator终极解读(一)–RecyclerView源码解析
  
注意,本文所有的代码都来自于27.1.1。

1. 概述

RecyclerView之所以受欢迎,有一部分的原因得归功于它的动画机制。我们可以通过RecyclerView的setItemAnimator方法来给每个Item设置在不同行为下,执行不同的动画,非常的简单。尽管我们知道怎么给RecyclerView设置动画,但是RecyclerView是怎么通过ItemAnimator来给每个Item实现动画,这里面的原理值得我们去研究和学习。

在正式分析RecyclerView的动画机制之前,我们先对几个词语有一个概念,我们来看看:

词语 含义
Disappearance 表示在动画之前,ItemView是可见的,动画之后就可不见了。这里的操作包括,remove操作和普通的滑动导致ItemView划出屏幕
Appearance 表示动画之前,ItemView是不可见,动画之后就可见了。这里的操作包括,add操作和普通的滑动导致ItemView划入屏幕
Persistence 表示动画前后,状态是不变的。这里面的操作包括,无任何操作
change 表示动画前后,状态是不变的。这里面的操作包括,change操作

还有注意的一点就是,ViewHolder不是用来记录ItemView的位置信息,而是进行数据绑定的,所以在动画中,关于位置信息的记录不是依靠ViewHolder来实现的,而是依靠一个叫ItemHolderInfo的类实现的,在这个类里面,有四个成员变量,分别记录ItemView的left、top、right和bottom四个位置信息

最后还需要注意一点就是,我们从RecyclerView的三大流程中可以得到,在RecyclerView的内部,dispatchLayout分为三步,其中dispathchLayoutStep1被称为预布局,在这里主要是保存ItemView的OldViewHolder,同时还会记录下每个ItemView在动画之前的位置信息;与之对应的dispathchLayoutStep3被称为后布局,主要结合真正布局和预布局的相关信息来实现进行动画,当然前提是RecyclerView本身支持动画。

本文打算从两个角度来分析RecyclerView的动画,一是从普通三大的流程来看,这是动画机制的核心所在;二是从Adapeter的角度上来看,看看我们每次在调用Adapter的notify相关方法之后,是怎么进行执行动画的(实际上也是回到三大流程里面)。

1. 再来看RecyclerView的三大流程

取这个题目,我感觉有特别的含义。首先,本次分析动画机制就是重新来看看三大流程,当然本次分三大流程肯定没有之前的那么仔细,其次侧重点也不同;其次,本次再来看RecyclerView的三大流程,还可以填之前在分析RecyclerView的三大流程留下的坑。
  
本次的分析重点在于dispathchLayoutStep1和dispathchLayoutStep3,不会分析完整的三大流程,所以,还有不懂RecyclerView三大流程的同学,可以参考我的文章:RecyclerView 源码分析(一) - RecyclerView的三大流程

我们先来看看dispatchLayoutStep1方法:

private void dispatchLayoutStep1() {
   
    // ······
    if (mState.mRunSimpleAnimations) {
   
        // Step 0: Find out where all non-removed items are, pre-layout
        int count = mChildHelper.getChildCount();
        for (int i = 0; i < count; ++i) {
   
            final ViewHolder holder = getChildViewHolderInt(mChildHelper.getChildAt(i));
            if (holder.shouldIgnore() || (holder.isInvalid() && !mAdapter.hasStableIds())) {
   
                continue;
            }
            final ItemHolderInfo animationInfo = mItemAnimator
                    .recordPreLayoutInformation(mState, holder,
                            ItemAnimator.buildAdapterChangeFlagsForAnimations(holder),
                            holder.getUnmodifiedPayloads());
            mViewInfoStore.addToPreLayout(holder, animationInfo);
            if (mState.mTrackOldChangeHolders && holder.isUpdated() && !holder.isRemoved()
                    && !holder.shouldIgnore() && !holder.isInvalid()) {
   
                long key = getChangedHolderKey(holder);
                // This is NOT the only place where a ViewHolder is added to old change holders
                // list. There is another case where:
                //    * A VH is currently hidden but not deleted
                //    * The hidden item is changed in the adapter
                //    * Layout manager decides to layout the item in the pre-Layout pass (step1)
                // When this case is detected, RV will un-hide that view and add to the old
                // change holders list.
                mViewInfoStore.addToOldChangeHolders(key, holder);
            }
        }
    }
    if (mState.mRunPredictiveAnimations) {
   
        // Step 1: run prelayout: This will use the old positions of items. The layout manager
        // is expected to layout everything, even removed items (though not to add removed
        // items back to the container). This gives the pre-layout position of APPEARING views
        // which come into existence as part of the real layout.

        // Save old positions so that LayoutManager can run its mapping logic.
        saveOldPositions();
        final boolean didStructureChange = mState.mStructureChanged;
        mState.mStructureChanged = false;
        // temporarily disable flag because we are asking for previous layout
        mLayout.onLayoutChildren(mRecycler, mState);
        mState.mStructureChanged = didStructureChange;

        for (int i = 0; i < mChildHelper.getChildCount(); ++i) {
   
            final View child = mChildHelper.getChildAt(i);
            final ViewHolder viewHolder = getChildViewHolderInt(child);
            if (viewHolder.shouldIgnore()) {
   
                continue;
            }
            if (!mViewInfoStore.isInPreLayout(viewHolder)) {
   
                int flags = ItemAnimator.buildAdapterChangeFlagsForAnimations(viewHolder);
              
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
要在RecyclerView的点击事件中呈现平移动画,你可以使用以下步骤: 1. 在RecyclerView的Adapter中,为每个item添加一个点击事件监听器。 2. 在点击事件监听器中,获取被点击的item的位置信息。 3. 创建一个目标View对象,用于展示平移动画效果。 4. 计算目标View的起始位置和终止位置,可以使用item的位置信息和RecyclerView的布局参数来计算。 5. 创建一个平移动画对象,设置起始位置和终止位置,并设置动画持续时间、插值器等属性。 6. 将平移动画对象应用到目标View上,开始执行动画。 下面是一个简单的示例代码: ```kotlin class MyAdapter : RecyclerView.Adapter<MyViewHolder>() { override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyViewHolder { // 创建ViewHolder } override fun onBindViewHolder(holder: MyViewHolder, position: Int) { // 绑定数据 holder.itemView.setOnClickListener { // 获取被点击的item的位置信息 val itemPosition = holder.adapterPosition // 创建目标View对象 val targetView = View(holder.itemView.context) // 计算目标View的起始位置和终止位置 val startX = holder.itemView.left.toFloat() val startY = holder.itemView.top.toFloat() val endX = parent.width.toFloat() / 2 - targetView.width / 2 val endY = parent.height.toFloat() / 2 - targetView.height / 2 // 创建平移动画对象 val translateAnim = TranslateAnimation(startX, endX, startY, endY) translateAnim.duration = 500 translateAnim.interpolator = AccelerateDecelerateInterpolator() // 将平移动画对象应用到目标View上,开始执行动画 targetView.startAnimation(translateAnim) } } override fun getItemCount(): Int { // 返回item数量 } } ``` 在上述代码中,我们通过点击事件监听器来获取被点击的item的位置信息,然后创建一个目标View对象,并计算出目标View的起始位置和终止位置。接着,我们通过创建平移动画对象,并将其应用到目标View上,来呈现平移动画效果。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值