Android属性动画实现RecyclerView多级列表

效果图:
Android属性动画实现RecyclerView多级列表_第1张图片
expandable_anim.gif
分析:

gif中可看到存在2个动画,一个箭头的反转,一个二级列表的伸缩。箭头的反转比较好实现。伸缩动画刚开始尝试过平移动画,效果很僵硬不太理想。后面想到用属性动画,动态修改布局高度,达到了想要的效果。

实现步骤:

1.箭头的反转

 private fun startRotate() {
        animation = if (View.VISIBLE == hideView.visibility) {
            RotateAnimation(180f, 0f, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f)
        } else {
            RotateAnimation(0f, 180f, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f)
        }
        animation?.duration = 60
        animation?.interpolator = LinearInterpolator()
        animation?.repeatMode = Animation.REVERSE
        animation?.fillAfter = true//此行重点,动画结束的时候停留在最后一帧
        arrow.startAnimation(animation)
    }

2、列表的伸缩

  private fun expand(v: View) {
        v.visibility = View.VISIBLE
        val animator = createTelescopicAnim(v, 0, mHeight)
        animator.start()
    }

    private fun shrink(view: View) {
        val origHeight = view.height
        val animator = createTelescopicAnim(view, origHeight, 0)
        animator.addListener(object : AnimatorListenerAdapter() {
            override fun onAnimationEnd(animation: Animator) {
                view.visibility = View.GONE
            }
        })
        animator.start()
    }

    private fun createTelescopicAnim(v: View, start: Int, end: Int): ValueAnimator {
        val animator = ValueAnimator.ofInt(start, end)
        animator.addUpdateListener { arg0 ->
            val value = arg0.animatedValue as Int
            val layoutParams = v.layoutParams
            layoutParams.height = value
            v.layoutParams = layoutParams
        }
        return animator
    }
完整代码:

核心类ExpandabelAnimUtil


import android.animation.Animator
import android.animation.AnimatorListenerAdapter
import android.animation.ValueAnimator
import android.content.Context
import android.view.View
import android.view.animation.Animation
import android.view.animation.LinearInterpolator
import android.view.animation.RotateAnimation

class ExpandableAnimUtil private constructor(
    context: Context, private val hideView: View, private val arrow: View
    , height: Int
) {
    private val mHeight: Int
    private var animation: RotateAnimation? = null

    companion object {
        fun newInstance(context: Context, hideView: View, down: View, height: Int): ExpandableAnimUtil {
            return ExpandableAnimUtil(context, hideView, down, height)
        }
    }

    init {
        val mDensity = context.resources.displayMetrics.density
        mHeight = (mDensity * height).toInt()//dp2px
    }


    fun toggle() {
        startRotate()
        if (View.VISIBLE == hideView.visibility) {
            shrink(hideView)
        } else {
            expand(hideView)
        }
    }

    private fun startRotate() {
        animation = if (View.VISIBLE == hideView.visibility) {
            RotateAnimation(180f, 0f, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f)
        } else {
            RotateAnimation(0f, 180f, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f)
        }
        animation?.duration = 30
        animation?.interpolator = LinearInterpolator()
        animation?.repeatMode = Animation.REVERSE
        animation?.fillAfter = true
        arrow.startAnimation(animation)
    }

    private fun expand(v: View) {
        v.visibility = View.VISIBLE
        val animator = createTelescopicAnim(v, 0, mHeight)
        animator.start()
    }

    private fun shrink(view: View) {
        val origHeight = view.height
        val animator = createTelescopicAnim(view, origHeight, 0)
        animator.addListener(object : AnimatorListenerAdapter() {
            override fun onAnimationEnd(animation: Animator) {
                view.visibility = View.GONE
            }
        })
        animator.start()
    }

    private fun createTelescopicAnim(v: View, start: Int, end: Int): ValueAnimator {
        val animator = ValueAnimator.ofInt(start, end)
        animator.addUpdateListener { arg0 ->
            val value = arg0.animatedValue as Int
            val layoutParams = v.layoutParams
            layoutParams.height = value
            v.layoutParams = layoutParams
        }
        return animator
    }


}


Adapter,这里直接使用BRVAH,非常便捷

class ExpandableAdapter(list: ArrayList) :
    BaseItemDraggableAdapter(R.layout.item_expandable, list) {
    override fun convert(helper: BaseViewHolder?, item: String?) {

        helper?.getView(R.id.ctl_outer)?.setOnClickListener {
            ARouter.getInstance().build(RouterUri.WALLET_DETAIL_ACTIVITY).navigation()
        }

        helper?.getView(R.id.ll_inner)?.setOnClickListener {
            ARouter.getInstance().build(RouterUri.SEND_RECORD_ACTIVITY).navigation()
        }

        helper?.getView(R.id.iv_more)?.setOnClickListener {
            val animView = helper.getView(R.id.ll_inner)
            val ivMore = helper.getView(R.id.iv_more)
            ExpandableAnimUtil.newInstance(mContext, animView, ivMore, 200).toggle()
        }
    }
}

这个在项目中直接使用的,以后抽时间整理个demo再贡献完整源码吧。

你可能感兴趣的:(Android属性动画实现RecyclerView多级列表)