实现添加view 并伴随移除view动画的效果的自定义ViewGroup

实现View的添加和移除动画

点击查看采用系统实现方式

采用LayoutTransition 实现 添加view 和移除view 的动画效果
  • 优点 采用系统api 安全 实现方便快捷
  • 缺点 不能添加和删除动画同时执行,只能等到一个执行完之后才能进行操作

自定义ViewGroup实现的效果

device-2020-11-20-183752.2020-11-25 17_19_30.gif

整体代码

/**
 * @author
 * @desc : 支持添加view 伴随动画的效果
 *
 */
class AnimViewGroup(context: Context, attrs: AttributeSet?) : FrameLayout(context, attrs) {

    /**
     * 缓存一个view 为了复用
     */
    var cacheScrapView: View? = null
        private set

    private var removeAnimId = 0
    private var addAnimId = 0

    private var removeAnim: Animation? = null
    private var addAnim: Animation? = null

    init {
        context.obtainStyledAttributes(attrs, R.styleable.AnimViewGroup).apply {
            removeAnimId = getResourceId(R.styleable.AnimViewGroup_anim_remove_child, 0)
            addAnimId = getResourceId(R.styleable.AnimViewGroup_anim_add_child, 0)
        }.recycle()
    }

    override fun onFinishInflate() {
        super.onFinishInflate()
        if (childCount != 1) {
            throw Exception("this group must have only one child in xml")
        }
    }

    /**
     * 设置动画效果
     * @param enter 进入的动画
     * @param exit 离开的动画
     */
    fun setCustomAnimations(@AnimRes enter: Int, @AnimRes exit: Int){
        addAnim = loadAnimation(enter)
        removeAnim = loadAnimation(exit)
    }

    fun addChildWithAnim(view: View) {
        if (childCount < 1) {
            addView(view)
            return
        }
        //原始的view 移除动画
        val child0 = getChildAt(0)
        if (removeAnim == null) removeAnim = loadAnimation(removeAnimId)
        child0.startAnimation(removeAnim)
        child0.animation.setAnimationListener(object : Animation.AnimationListener {
            override fun onAnimationRepeat(animation: Animation?) {

            }

            override fun onAnimationEnd(animation: Animation?) {
                //此处直接调用  removeView(child0) 会导致崩溃
                //参考https://stackoverflow.com/questions/28180592/android-remove-view-when-its-animation-finished
                post {
                    removeView(child0)
                    cacheScrapView = child0
                }
            }

            override fun onAnimationStart(animation: Animation?) {

            }

        })

        //新添加的view执行的动画
        if (addAnim == null) addAnim = loadAnimation(addAnimId)
        addView(view)
        view.startAnimation(addAnim)
    }

    /**
     * 处理加载动画
     * @param animId 动画的xml文件配置
     */
    private fun loadAnimation(animId: Int): Animation? {
        if (animId != 0) {
            val dir = context.resources.getResourceTypeName(animId)
            // try AnimationUtils first
            if ("anim" == dir) {
                val animation = AnimationUtils.loadAnimation(context, animId)
                if (animation != null) {
                    return animation
                }
            }
        }
        return null
    }
}

自定义的style

    
        
        
    

anim文件下定义的动画

  • 进入的动画



    







  • 离开的动画



    

    


难点

  1. 对于动画的理解
  2. 报错的解决 Attempt to read from field 'int android.view.View.mViewFlags' on a null object reference

解决问题参考的文章

  1. Android 在动画结束回调onAnimationEnd()中remove view的崩溃解决方法及源码分析

  2. Android - remove View when its animation finished

你可能感兴趣的:(实现添加view 并伴随移除view动画的效果的自定义ViewGroup)