美文网首页
Android 高度自定义的粒子框架(支持普通View和surf

Android 高度自定义的粒子框架(支持普通View和surf

作者: 愿来缘来 | 来源:发表于2020-05-09 10:13 被阅读0次

    序言

    当前Android 主流的系统中粒子的实现方式大致可以分为两类三种

    1. 继承普通的View,主线程刷新绘制

    2. 使用surfaceView,子线程刷新绘制

    3. 使用surfaceView+openGl,子线程刷新绘制

    因为第三种方式不是很常用,这里使用前两种方式实现粒子

    效果图(视频转GIf有点卡)

    image

    实现功能

    1. 可以自定义粒子刷新的频率,每次刷新的数量

    2. 粒子自己维护自己的生命周期

    3. 可以预先自己定义的粒子数量加载粒子 避免一加载就是满屏粒子的尴尬

    4. 复用消亡粒子

    5. 实现粒子的高度自定义 避免过度封装

    理论概述

    两种实现方式 对粒子的处理大致相同 不同的是绘制的位置区别

    这里以第一种方式为例 画出粒子的UML框架:

    image

    如上图所示

    ParticleView继承Runnable 使用handler定时刷新,每次刷新进行以下一个步骤

    1. 新增粒子 从cacheItems集合中获取缓存粒子 如果没有则调用adapter进行创建

    2. 调用transForms 移动粒子并将消亡粒子移除绘制集合中

    3. 调用drawItems方法绘制所有的粒子

    使用

    这里因为是高度资质粒子,所以如果我们想要绘制粒子需要首先继承BaseItem接口定义自己的粒子类:

    然后实现适配器ParticleAdapter

    如:

      particle.setAdapter(object : ParticleAdapter() {
    
                override fun newItem(parentWidth: Int, parentHeight: Int): BaseItem {
    
       //返回自己实现的粒子
    
                    return BubbleItem(parentWidth, parentHeight, this@MainActivity)
    
                }
    
                override fun preCreateCount(): Int {
    
                    return 50
    
                }
    
            })
    
            particle.setIncreaseParticleInterval(100)
    
            particle.setRenderTime(16)
    
            particle.setIncreaseParticleCount(1)
    
    我这里实现的粒子如下:BubbleItem.kotlin
    
    class BubbleItem(private val parentWidth: Int, private val parentHeight: Int,context: Context) :
    
        BaseItem {
    
        companion object {
    
            const val STATE_LIVE = 1
    
            const val STATE_DIE = 0
    
        }
    
        private val baseBubbleRadius = ScreenUtil.dip2px(context,2f)
    
        private val intervalBubbleRadius = ScreenUtil.dip2px(context,3f)
    
        //起点
    
        private var origX: Float = 0f
    
        private var origY: Float = parentHeight.toFloat()
    
        //终点
    
        private var desY: Float = 0f
    
        //当前的位置
    
        private var curX = 0f
    
        private var curY = 0f
    
        //每次刷新 在Y轴的偏移量
    
        private var speedY = ScreenUtil.dip2px(context,2f)
    
        private val baseSpeedX =ScreenUtil.dip2px(context,0.5f)
    
        //每次刷新 在X轴的偏移量
    
        private var speedX = 0f
    
        var radius = 20f
    
        //透明的距离
    
        private var alphaDis = 0f
    
        var state = STATE_DIE
    
        private val paint = Paint(Paint.ANTI_ALIAS_FLAG)
    
        private var drawBitmap: Bitmap? = null
    
        private var resRect: Rect? = null
    
        init {
    
            paint.style = Paint.Style.FILL_AND_STROKE
    
            paint.color = Color.BLUE
    
        }
    
        /**
    
        * 初始化 随机生成气泡的出生地点
    
        */
    
        override fun init(context: Context) {
    
            //获取气泡的bitmap
    
            if (drawBitmap == null) {
    
                drawBitmap = BitmapFactory.decodeResource(context.resources, R.drawable.bubble)
    
                resRect = Rect(0, 0, drawBitmap?.width ?: 0, drawBitmap?.height ?: 0)
    
            }
    
            origX = Random.nextInt(100, parentWidth - 100).toFloat()
    
            desY = 2 * parentHeight / 3 - Random.nextInt(0, parentHeight / 2).toFloat()
    
            alphaDis = (origY - desY) * 0.2f
    
            radius = Random.nextFloat() * intervalBubbleRadius + baseBubbleRadius
    
            speedX = baseSpeedX * Random.nextFloat() * if (Random.nextBoolean()) {
    
                1
    
            } else {
    
                -1
    
            }
    
            curX = origX
    
            curY = origY
    
            state = STATE_LIVE
    
            //在边界处的粒子 没有横向速度
    
            if (curX <= 200 || curX > (parentWidth - 200)) {
    
                speedX = 0f
    
            }
    
            paint.alpha = 255
    
        }
    
        override fun preInit(context: Context) {
    
            //起点的X轴坐标
    
            init(context)
    
            curY = desY + max((origY - desY) * Random.nextFloat(), 0f)
    
        }
    
        override fun move(): Boolean {
    
            curY -= speedY
    
            curX += speedX
    
            val diff = curY - desY
    
            if (diff <= alphaDis) {
    
                if (diff <= alphaDis * 0.4 && diff >=0.3 * alphaDis) {
    
                    paint.alpha = 255
    
                } else {
    
                    //开始透明
    
                    paint.alpha = (255 * diff / alphaDis + 0.5f).toInt()
    
                }
    
            }
    
            if (curY < desY) {
    
                state = STATE_DIE
    
                return false
    
            }
    
            if (curX <= 20 || curX >= parentWidth - 20) {
    
                state = STATE_DIE
    
                return false
    
            }
    
            return true
    
        }
    
        override fun reset() {
    
        }
    
        override fun draw(canvas: Canvas) {
    
            drawBitmap?.apply {
    
                if (!isRecycled) {
    
                    canvas.drawBitmap(this, resRect, RectF(curX - radius, curY - radius, curX + radius, curY + radius), paint)
    
                }
    
            }
    
        }
    
        override fun isLive(): Boolean {
    
            return state == STATE_LIVE
    
        }
    
        override fun destroy() {
    
            drawBitmap?.recycle()
    
            drawBitmap = null
    
        }
    
    }
    

    使用普通View 主线程刷新绘制

    普通的View绘制就是 使用handler主线程绘制刷新实现如下:

    class ParticleView(context: Context, attributeSet: AttributeSet) : View(context, attributeSet),
    
        BaseParticle, Runnable {
    
        private var particleAdapter: ParticleAdapter? = null
    
        private var isAutoPlay = true
    
        private var intervalTime = 10 * Constant.RENDER_TIME
    
        private var renderTime = Constant.RENDER_TIME
    
        private var increaseParticleCount = 1
    
        private var childTotal = Int.MAX_VALUE
    
        private var temTime = -1
    
        //缓存的view
    
        private var cacheItems = LinkedList<BaseItem>()
    
        //要绘制的所有View
    
        private var drawItems = ArrayList<BaseItem>()
    
        private var renderHandler: Handler = Handler()
    
        private var isInit: Boolean = false
    
        override fun preCreate() {
    
            repeat(particleAdapter?.preCreateCount() ?: 0) {
    
                val newItem = particleAdapter!!.newItem(measuredWidth, measuredHeight)
    
                newItem.preInit(context)
    
                drawItems.add(newItem)
    
            }
    
        }
    
        override fun getItem(): BaseItem {
    
            val newItem = if (cacheItems.size > 0) {
    
                cacheItems.removeFirst()
    
            } else {
    
                particleAdapter!!.newItem(measuredWidth, measuredHeight)
    
            }
    
            newItem.init(context)
    
            return newItem
    
        }
    
        override fun drawItems(canvas: Canvas) {
    
            if (drawItems.size < childTotal) {
    
                if (temTime == -1) {
    
                    temTime = ((intervalTime / renderTime.toFloat() + 0.5).toInt())
    
                } else if (temTime == 0) {
    
                    repeat(increaseParticleCount) {
    
                        drawItems.add(getItem())
    
                    }
    
                }
    
                temTime--
    
            }
    
            drawItems.forEach {
    
                it.draw(canvas)
    
            }
    
        }
    
        override fun transForms() {
    
            val iterator = drawItems.iterator()
    
            while (iterator.hasNext()) {
    
                val next = iterator.next()
    
                val isLive = next.move()
    
                if (!isLive) {
    
                    iterator.remove()
    
                    cacheItems.add(next)
    
                }
    
            }
    
        }
    
        override fun destroyAllView() {
    
            drawItems.forEach {
    
                it.destroy()
    
            }
    
            cacheItems.forEach {
    
                it.destroy()
    
            }
    
        }
    
        override fun startAnimation() {
    
            renderHandler.removeCallbacks(this)
    
            renderHandler.post(this)
    
        }
    
        override fun stopAnimation() {
    
            renderHandler.removeCallbacks(this)
    
        }
    
        override fun setAdapter(adapter: ParticleAdapter) {
    
            particleAdapter = adapter
    
            if (particleAdapter!!.maxParticleCount() <= 0) {
    
                return
    
            }
    
            childTotal = particleAdapter!!.maxParticleCount()
    
        }
    
        override fun setRenderTime(renderTime: Long) {
    
            if (intervalTime < renderTime || renderTime < 0) {
    
                return
    
            }
    
            this.renderTime = renderTime
    
        }
    
        override fun setIncreaseParticleInterval(intervalTime: Long) {
    
            if (intervalTime < renderTime || renderTime < 0) {
    
                return
    
            }
    
            this.intervalTime = intervalTime
    
        }
    
        override fun setIncreaseParticleCount(count: Int) {
    
            if (count <= 0) {
    
                return
    
            }
    
            increaseParticleCount = count
    
        }
    
        override fun setIsAutoPlay(isAuto: Boolean) {
    
            isAutoPlay = isAuto
    
        }
    
        /**
    
        * 设置大小则按照设置的大小计算 否则按照屏幕的宽高来计算
    
        */
    
        override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
    
            //获取屏幕宽高
    
            val screenWidth = ScreenUtil.getScreenWidth(context)
    
            val screenHeight = ScreenUtil.getScreenRealHeight(context)
    
            setMeasuredDimension(
    
                getDefaultSize(screenWidth, widthMeasureSpec),
    
                getDefaultSize(screenHeight, screenHeight)
    
            )
    
        }
    
        override fun onLayout(changed: Boolean, left: Int, top: Int, right: Int, bottom: Int) {
    
            super.onLayout(changed, left, top, right, bottom)
    
            if (!isInit) {
    
                isInit = true
    
                preCreate()
    
            }
    
        }
    
        override fun onDraw(canvas: Canvas?) {
    
            if (visibility != VISIBLE) {
    
                return
    
            }
    
            if (canvas == null) {
    
                renderHandler.removeCallbacks(this)
    
                return
    
            }
    
            drawItems(canvas)
    
        }
    
        override fun run() {
    
            transForms()
    
            invalidate()
    
            renderHandler.postDelayed(this, renderTime)
    
        }
    
        override fun setVisibility(visibility: Int) {
    
            super.setVisibility(visibility)
    
            if (isAutoPlay) {
    
                if (visibility == VISIBLE) {
    
                    startAnimation()
    
                } else {
    
                    renderHandler.removeCallbacksAndMessages(null)
    
                }
    
            }
    
        }
    
        override fun onDetachedFromWindow() {
    
            super.onDetachedFromWindow()
    
            renderHandler.removeCallbacks(this)
    
        }
    
        override fun onAttachedToWindow() {
    
            super.onAttachedToWindow()
    
            renderHandler.post(this)
    
        }
    
    }
    

    使用surfaceView,子线程刷新绘制

    实现如下:

    class ParticleSurfaceView(context: Context, attributeSet: AttributeSet) :
    
        SurfaceView(context, attributeSet), BaseParticle, SurfaceHolder.Callback, Runnable {
    
        private var particleAdapter: ParticleAdapter? = null
    
        private var isAutoPlay = true
    
        private var intervalTime = 10 * Constant.RENDER_TIME
    
        private var renderTime = Constant.RENDER_TIME
    
        private var increaseParticleCount = 1
    
        private var childTotal = Int.MAX_VALUE
    
        private var temTime = -1
    
        private val surfaceHolder: SurfaceHolder = holder
    
        private lateinit var handlerThread: HandlerThread
    
        private lateinit var renderHandler: Handler
    
        //缓存的view
    
        private var cacheItems = LinkedList<BaseItem>()
    
        //要绘制的所有View
    
        private var drawItems = ArrayList<BaseItem>()
    
        init {
    
            surfaceHolder.setKeepScreenOn(true)
    
            surfaceHolder.addCallback(this)
    
            isFocusable = true
    
            isFocusableInTouchMode = true
    
            setZOrderOnTop(true)
    
            //设置背景为透明色
    
            surfaceHolder.setFormat(PixelFormat.TRANSPARENT)
    
        }
    
        override fun preCreate() {
    
            repeat(particleAdapter?.preCreateCount() ?: 0) {
    
                val newItem = particleAdapter!!.newItem(measuredWidth, measuredHeight)
    
                newItem.preInit(context)
    
                drawItems.add(newItem)
    
            }
    
        }
    
        override fun getItem(): BaseItem {
    
            val newItem = if (cacheItems.size > 0) {
    
                cacheItems.removeFirst()
    
            } else {
    
                particleAdapter!!.newItem(measuredWidth, measuredHeight)
    
            }
    
            newItem.init(context)
    
            return newItem
    
        }
    
        override fun drawItems(canvas: Canvas) {
    
            if (drawItems.size < childTotal) {
    
                if (temTime == -1) {
    
                    temTime = ((intervalTime / renderTime.toFloat() + 0.5).toInt())
    
                } else if (temTime == 0) {
    
                    repeat(increaseParticleCount) {
    
                        drawItems.add(getItem())
    
                    }
    
                }
    
                temTime--
    
            }
    
            drawItems.forEach {
    
                it.draw(canvas)
    
            }
    
        }
    
        override fun transForms() {
    
            val iterator = drawItems.iterator()
    
            while (iterator.hasNext()) {
    
                val next = iterator.next()
    
                val isLive = next.move()
    
                if (!isLive) {
    
                    iterator.remove()
    
                    cacheItems.add(next)
    
                }
    
            }
    
        }
    
        override fun destroyAllView() {
    
            drawItems.forEach {
    
                it.destroy()
    
            }
    
            cacheItems.forEach {
    
                it.destroy()
    
            }
    
        }
    
        override fun startAnimation() {
    
            renderHandler.removeCallbacks(this)
    
            renderHandler.post(this)
    
        }
    
        override fun stopAnimation() {
    
            renderHandler.removeCallbacks(this)
    
        }
    
        override fun setAdapter(adapter: ParticleAdapter) {
    
            particleAdapter = adapter
    
            if (particleAdapter!!.maxParticleCount() <= 0) {
    
                return
    
            }
    
            childTotal = particleAdapter!!.maxParticleCount()
    
        }
    
        override fun setRenderTime(renderTime: Long) {
    
            if (intervalTime < renderTime || renderTime < 0) {
    
                return
    
            }
    
            this.renderTime = renderTime
    
        }
    
        override fun setIncreaseParticleInterval(intervalTime: Long) {
    
            if (intervalTime < renderTime || renderTime < 0) {
    
                return
    
            }
    
            this.intervalTime = intervalTime
    
        }
    
        override fun setIncreaseParticleCount(count: Int) {
    
            if (count <= 0) {
    
                return
    
            }
    
            increaseParticleCount = count
    
        }
    
        override fun setIsAutoPlay(isAuto: Boolean) {
    
            isAutoPlay = isAuto
    
        }
    
        override fun surfaceChanged(holder: SurfaceHolder?, format: Int, width: Int, height: Int) {
    
        }
    
        override fun surfaceDestroyed(holder: SurfaceHolder?) {
    
            renderHandler.removeCallbacks(this)
    
            destroyAllView()
    
        }
    
        override fun surfaceCreated(holder: SurfaceHolder?) {
    
            if (particleAdapter == null) {
    
                throw NullPointerException("particleAdapter must not be null")
    
            }
    
            handlerThread = HandlerThread("BaseSurfaceView")
    
            handlerThread.start()
    
            renderHandler = Handler(handlerThread.looper)
    
            preCreate()
    
            if (isAutoPlay) {
    
                renderHandler.post(this)
    
            }
    
        }
    
        override fun run() {
    
            transForms()
    
            val lockCanvas = surfaceHolder.lockCanvas(null)
    
            lockCanvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR)
    
            drawItems(lockCanvas)
    
            surfaceHolder.unlockCanvasAndPost(lockCanvas)
    
            renderHandler.postDelayed(this, renderTime)
    
        }
    
    }
    

    项目地址:项目地址传送门 点我 点我 点我!!!

    相关文章

      网友评论

          本文标题:Android 高度自定义的粒子框架(支持普通View和surf

          本文链接:https://www.haomeiwen.com/subject/qzphnhtx.html