亚洲激情专区-91九色丨porny丨老师-久久久久久久女国产乱让韩-国产精品午夜小视频观看

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

Android怎么實現點贊動畫效果

發布時間:2022-02-07 09:52:59 來源:億速云 閱讀:298 作者:iii 欄目:開發技術

今天小編給大家分享一下Android怎么實現點贊動畫效果的相關知識點,內容詳細,邏輯清晰,相信大部分人都還太了解這方面的知識,所以分享這篇文章給大家參考一下,希望大家閱讀完這篇文章后有所收獲,下面我們一起來了解一下吧。

    一、前言

    對接下來功能實現的探索。

    二、需求拆分

    仔細觀察點贊交互,看出大概以下幾個步驟:

    1:點贊控件需要自定義,對其觸摸事件進行處理。

    2:點贊動畫的實現。

    3:要有一個存放動畫的容器。

    三、實現方案

    1、點贊控件觸摸事件處理

    點贊控件是區分長按和點擊處理的,另外我們發現在手指按下以后包括手指的移動直到手指的抬起都在執行動畫。因為點贊的點擊區域可能包括點贊次數,所以這里就自定義了點贊控件,并處理onTouchEvent(event: MotionEvent)事件,區分長按和單擊是使用了點擊到手指抬起的間隔時間區分的,偽代碼如下:

    override fun onTouchEvent(event: MotionEvent): Boolean {
        var onTouch: Boolean
        when (event.action) {
            MotionEvent.ACTION_DOWN -> {
                isRefreshing = false
                isDowning = true
                //點擊
                lastDownTime = System.currentTimeMillis()
                postDelayed(autoPollTask, CLICK_INTERVAL_TIME)
                onTouch = true
            }
            MotionEvent.ACTION_UP -> {
                isDowning = false
                //抬起
                if (System.currentTimeMillis() - lastDownTime < CLICK_INTERVAL_TIME) {
                    //小于間隔時間按照單擊處理
                    onFingerDowningListener?.onDown(this)
                } else {
                    //大于等于間隔時間按照長按抬起手指處理
                    onFingerDowningListener?.onUp()
                }
                removeCallbacks(autoPollTask)
                onTouch = true
            }
            MotionEvent.ACTION_CANCEL ->{
                isDowning = false
                removeCallbacks(autoPollTask)
                onTouch = false
            }
            else -> onTouch = false
        }
        return onTouch
    }

    長按時使用Runnable的postDelayed(Runnable action, long delayMillis)方法來進行不斷的執行動畫,偽代碼:

    private inner class AutoPollTask : Runnable {
        override fun run() {
            onFingerDowningListener?.onLongPress(this@LikeView)
            if(!canLongPress){
                removeCallbacks(autoPollTask)
            }else{
                postDelayed(autoPollTask, CLICK_INTERVAL_TIME)
            }
        }
    
    }

    2、點贊動畫的實現

    點贊效果元素分為:點贊表情圖標、點贊次數數字以及點贊文案

    2.1、點贊效果圖片的獲取和存儲管理

    這里參考了SuperLike的做法,對圖片進行了緩存處理,代碼如下:

    object BitmapProviderFactory {
        fun getProvider(context: Context): BitmapProvider.Provider {
            return BitmapProvider.Builder(context)
                .setDrawableArray(
                    intArrayOf(
                            R.mipmap.emoji_1, R.mipmap.emoji_2, R.mipmap.emoji_3,
                            R.mipmap.emoji_4, R.mipmap.emoji_5, R.mipmap.emoji_6,
                            R.mipmap.emoji_7, R.mipmap.emoji_8, R.mipmap.emoji_9, R.mipmap.emoji_10,
                            R.mipmap.emoji_11, R.mipmap.emoji_12, R.mipmap.emoji_13,
                            R.mipmap.emoji_14
                    )
                )
                .setNumberDrawableArray(
                    intArrayOf(
                            R.mipmap.multi_digg_num_0, R.mipmap.multi_digg_num_1,
                            R.mipmap.multi_digg_num_2, R.mipmap.multi_digg_num_3,
                            R.mipmap.multi_digg_num_4, R.mipmap.multi_digg_num_5,
                            R.mipmap.multi_digg_num_6, R.mipmap.multi_digg_num_7,
                            R.mipmap.multi_digg_num_8, R.mipmap.multi_digg_num_9
                    )
                )
                .setLevelDrawableArray(
                    intArrayOf(
                            R.mipmap.multi_digg_word_level_1, R.mipmap.multi_digg_word_level_2,
                            R.mipmap.multi_digg_word_level_3
                    )
                )
                .build()
        }
    }
    object BitmapProvider {
        class Default(
            private val context: Context,
            cacheSize: Int,
            @DrawableRes private val drawableArray: IntArray,
            @DrawableRes private val numberDrawableArray: IntArray?,
            @DrawableRes private val levelDrawableArray: IntArray?,
            private val levelStringArray: Array<String>?,
            private val textSize: Float
        ) : Provider {
            private val bitmapLruCache: LruCache<Int, Bitmap> = LruCache(cacheSize)
            private val NUMBER_PREFIX = 0x70000000
            private val LEVEL_PREFIX = -0x80000000
    
            /**
             * 獲取數字圖片
             * @param number
             * @return
             */
            override fun getNumberBitmap(number: Int): Bitmap? {
                var bitmap: Bitmap?
                if (numberDrawableArray != null && numberDrawableArray.isNotEmpty()) {
                    val index = number % numberDrawableArray.size
                    bitmap = bitmapLruCache[NUMBER_PREFIX or numberDrawableArray[index]]
                    if (bitmap == null) {
                        bitmap =
                            BitmapFactory.decodeResource(context.resources, numberDrawableArray[index])
                        bitmapLruCache.put(NUMBER_PREFIX or numberDrawableArray[index], bitmap)
                    }
                } else {
                    bitmap = bitmapLruCache[NUMBER_PREFIX or number]
                    if (bitmap == null) {
                        bitmap = createBitmapByText(textSize, number.toString())
                        bitmapLruCache.put(NUMBER_PREFIX or number, bitmap)
                    }
                }
                return bitmap
            }
    
            /**
             * 獲取等級文案圖片
             * @param level
             * @return
             */
            override fun getLevelBitmap(level: Int): Bitmap? {
                var bitmap: Bitmap?
                if (levelDrawableArray != null && levelDrawableArray.isNotEmpty()) {
                    val index = level.coerceAtMost(levelDrawableArray.size)
                    bitmap = bitmapLruCache[LEVEL_PREFIX or levelDrawableArray[index]]
                    if (bitmap == null) {
                        bitmap =
                            BitmapFactory.decodeResource(context.resources, levelDrawableArray[index])
                        bitmapLruCache.put(LEVEL_PREFIX or levelDrawableArray[index], bitmap)
                    }
                } else {
                    bitmap = bitmapLruCache[LEVEL_PREFIX or level]
                    if (bitmap == null && !levelStringArray.isNullOrEmpty()) {
                        val index = level.coerceAtMost(levelStringArray.size)
                        bitmap = createBitmapByText(textSize, levelStringArray[index])
                        bitmapLruCache.put(LEVEL_PREFIX or level, bitmap)
                    }
                }
                return bitmap
            }
    
            /**
             * 獲取隨機表情圖片
             * @return
             */
            override val randomBitmap: Bitmap
                get() {
                    val index = (Math.random() * drawableArray.size).toInt()
                    var bitmap = bitmapLruCache[drawableArray[index]]
                    if (bitmap == null) {
                        bitmap = BitmapFactory.decodeResource(context.resources, drawableArray[index])
                        bitmapLruCache.put(drawableArray[index], bitmap)
                    }
                    return bitmap
                }
    
            private fun createBitmapByText(textSize: Float, text: String): Bitmap {
                val textPaint = TextPaint()
                textPaint.color = Color.BLACK
                textPaint.textSize = textSize
                val bitmap = Bitmap.createBitmap(
                    textPaint.measureText(text).toInt(),
                    textSize.toInt(), Bitmap.Config.ARGB_4444
                )
                val canvas = Canvas(bitmap)
                canvas.drawColor(Color.TRANSPARENT)
                canvas.drawText(text, 0f, textSize, textPaint)
                return bitmap
            }
    
        }
    
        class Builder(var context: Context) {
            private var cacheSize = 0
    
            @DrawableRes
            private var drawableArray: IntArray? = null
    
            @DrawableRes
            private var numberDrawableArray: IntArray? = null
    
            @DrawableRes
            private var levelDrawableArray: IntArray? = null
            private var levelStringArray: Array<String>? = null
            private var textSize = 0f
    
            fun setCacheSize(cacheSize: Int): Builder {
                this.cacheSize = cacheSize
                return this
            }
    
            /**
             * 設置表情圖片
             * @param drawableArray
             * @return
             */
            fun setDrawableArray(@DrawableRes drawableArray: IntArray?): Builder {
                this.drawableArray = drawableArray
                return this
            }
    
            /**
             * 設置數字圖片
             * @param numberDrawableArray
             * @return
             */
            fun setNumberDrawableArray(@DrawableRes numberDrawableArray: IntArray): Builder {
                this.numberDrawableArray = numberDrawableArray
                return this
            }
    
            /**
             * 設置等級文案圖片
             * @param levelDrawableArray
             * @return
             */
            fun setLevelDrawableArray(@DrawableRes levelDrawableArray: IntArray?): Builder {
                this.levelDrawableArray = levelDrawableArray
                return this
            }
    
            fun setLevelStringArray(levelStringArray: Array<String>?): Builder {
                this.levelStringArray = levelStringArray
                return this
            }
    
            fun setTextSize(textSize: Float): Builder {
                this.textSize = textSize
                return this
            }
    
            fun build(): Provider {
                if (cacheSize == 0) {
                    cacheSize = 32
                }
                if (drawableArray == null || drawableArray?.isEmpty() == true) {
                    drawableArray = intArrayOf(R.mipmap.emoji_1)
                }
                if (levelDrawableArray == null && levelStringArray.isNullOrEmpty()) {
                    levelStringArray = arrayOf("次贊!", "太棒了!!", "超贊同!!!")
                }
                return Default(
                    context, cacheSize, drawableArray!!, numberDrawableArray,
                    levelDrawableArray, levelStringArray, textSize
                )
            }
        }
    
        interface Provider {
    
            /**
             * 獲取隨機表情圖片
             */
            val randomBitmap: Bitmap
    
            /**
             * 獲取數字圖片
             * [number] 點擊次數
             */
            fun getNumberBitmap(number: Int): Bitmap?
    
            /**
             * 獲取等級文案圖片
             * [level] 等級
             */
            fun getLevelBitmap(level: Int): Bitmap?
        }
    }
    2.2、點贊表情圖標動畫實現

    這里的實現參考了toutiaothumb,表情圖標的動畫大致分為:上升動畫的同時執行圖標大小變化動畫和圖標透明度變化,在上升動畫完成時進行下降動畫。代碼如下:

    class EmojiAnimationView @JvmOverloads constructor(
        context: Context,
        private val provider: BitmapProvider.Provider?,
        attrs: AttributeSet? = null,
        defStyleAttr: Int = 0
    ) : View(context, attrs, defStyleAttr) {
        private var mThumbImage: Bitmap? = null
        private var mBitmapPaint: Paint? = null
        private var mAnimatorListener: AnimatorListener? = null
    
        /**
         * 表情圖標的寬度
         */
        private var emojiWith = 0
    
        /**
         * 表情圖標的高度
         */
        private var emojiHeight = 0
    
    
        private fun init() {
            //初始化圖片,取出隨機圖標
            mThumbImage = provider?.randomBitmap
        }
    
        init {
            //初始化paint
            mBitmapPaint = Paint()
            mBitmapPaint?.isAntiAlias = true
        }
    
        /**
         * 設置動畫
         */
        private fun showAnimation() {
            val imageWidth = mThumbImage?.width ?:0
            val imageHeight = mThumbImage?.height ?:0
            val topX = -1080 + (1400 * Math.random()).toFloat()
            val topY = -300 + (-700 * Math.random()).toFloat()
            //上升動畫
            val translateAnimationX = ObjectAnimator.ofFloat(this, "translationX", 0f, topX)
            translateAnimationX.duration = DURATION.toLong()
            translateAnimationX.interpolator = LinearInterpolator()
            val translateAnimationY = ObjectAnimator.ofFloat(this, "translationY", 0f, topY)
            translateAnimationY.duration = DURATION.toLong()
            translateAnimationY.interpolator = DecelerateInterpolator()
            //表情圖片的大小變化
            val translateAnimationRightLength = ObjectAnimator.ofInt(
                this, "emojiWith",
                0,imageWidth,imageWidth,imageWidth,imageWidth, imageWidth, imageWidth, imageWidth, imageWidth, imageWidth
            )
            translateAnimationRightLength.duration = DURATION.toLong()
            val translateAnimationBottomLength = ObjectAnimator.ofInt(
                this, "emojiHeight",
                0,imageHeight,imageHeight,imageHeight,imageHeight,imageHeight, imageHeight, imageHeight, imageHeight, imageHeight
            )
            translateAnimationBottomLength.duration = DURATION.toLong()
            translateAnimationRightLength.addUpdateListener {
                invalidate()
            }
            //透明度變化
            val alphaAnimation = ObjectAnimator.ofFloat(
                this,
                "alpha",
                0.8f,
                1.0f,
                1.0f,
                1.0f,
                0.9f,
                0.8f,
                0.8f,
                0.7f,
                0.6f,
                0f
            )
            alphaAnimation.duration = DURATION.toLong()
            //動畫集合
            val animatorSet = AnimatorSet()
            animatorSet.play(translateAnimationX).with(translateAnimationY)
                .with(translateAnimationRightLength).with(translateAnimationBottomLength)
                .with(alphaAnimation)
    
            //下降動畫
            val translateAnimationXDown =
                ObjectAnimator.ofFloat(this, "translationX", topX, topX * 1.2f)
            translateAnimationXDown.duration = (DURATION / 5).toLong()
            translateAnimationXDown.interpolator = LinearInterpolator()
            val translateAnimationYDown =
                ObjectAnimator.ofFloat(this, "translationY", topY, topY * 0.8f)
            translateAnimationYDown.duration = (DURATION / 5).toLong()
            translateAnimationYDown.interpolator = AccelerateInterpolator()
            //設置動畫播放順序
            val animatorSetDown = AnimatorSet()
            animatorSet.start()
            animatorSet.addListener(object : Animator.AnimatorListener {
                override fun onAnimationStart(animation: Animator) {}
                override fun onAnimationEnd(animation: Animator) {
                    animatorSetDown.play(translateAnimationXDown).with(translateAnimationYDown)
                    animatorSetDown.start()
                }
    
                override fun onAnimationCancel(animation: Animator) {}
                override fun onAnimationRepeat(animation: Animator) {}
            })
            animatorSetDown.addListener(object : Animator.AnimatorListener {
                override fun onAnimationStart(animation: Animator) {}
                override fun onAnimationEnd(animation: Animator) {
                    //動畫完成后通知移除動畫view
                    mAnimatorListener?.onAnimationEmojiEnd()
                }
    
                override fun onAnimationCancel(animation: Animator) {}
                override fun onAnimationRepeat(animation: Animator) {}
            })
        }
    
    
        override fun onDraw(canvas: Canvas) {
            super.onDraw(canvas)
            drawEmojiImage(canvas)
        }
    
        /**
         * 繪制表情圖片
         */
        private fun drawEmojiImage(canvas: Canvas) {
            mThumbImage?.let{
                val dst = Rect()
                dst.left = 0
                dst.top = 0
                dst.right = emojiWith
                dst.bottom = emojiHeight
                canvas.drawBitmap(it, null, dst, mBitmapPaint)
            }
    
        }
    
        /**
         * 這些get\set方法用于表情圖標的大小動畫
         * 不能刪除
         */
        fun getEmojiWith(): Int {
            return emojiWith
        }
    
        fun setEmojiWith(emojiWith: Int) {
            this.emojiWith = emojiWith
        }
    
        fun getEmojiHeight(): Int {
            return emojiHeight
        }
    
        fun setEmojiHeight(emojiHeight: Int) {
            this.emojiHeight = emojiHeight
        }
    
        fun setEmojiAnimation() {
            showAnimation()
        }
    
        fun setAnimatorListener(animatorListener: AnimatorListener?) {
            mAnimatorListener = animatorListener
        }
    
        interface AnimatorListener {
            /**
             *  動畫結束
             */
            fun onAnimationEmojiEnd()
        }
    
    
        fun setEmoji() {
            init()
        }
    
        companion object {
            //動畫時長
            const val DURATION = 500
        }
    }
    2.3、點贊次數和點贊文案的繪制

    這里的點贊次數處理了從1到999,并在不同的點贊次數區間顯示不同的點贊文案。代碼如下:

    class NumberLevelView @JvmOverloads constructor(
        context: Context,
        private val provider: BitmapProvider.Provider?,
        private val x: Int,
        attrs: AttributeSet? = null,
        defStyleAttr: Int = 0
    ) : View(context, attrs, defStyleAttr) {
        private var textPaint: Paint = Paint()
    
        /**
         * 點擊次數
         */
        private var mNumber = 0
    
        /**
         * 等級文案圖片
         */
        private var bitmapTalk: Bitmap? = null
    
        /**
         * 等級
         */
        private var level = 0
    
        /**
         * 數字圖片寬度
         */
        private var numberImageWidth = 0
    
        /**
         * 數字圖片的總寬度
         */
        private var offsetX = 0
    
        /**
         * x 初始位置
         */
        private var initialValue = 0
    
        /**
         * 默認數字和等級文案圖片間距
         */
        private var spacing = 0
    
        init {
            textPaint.isAntiAlias = true
            initialValue = x - PublicMethod.dp2px(context, 120f)
            numberImageWidth = provider?.getNumberBitmap(1)?.width ?: 0
            spacing = PublicMethod.dp2px(context, 10f)
        }
        override fun onDraw(canvas: Canvas) {
            super.onDraw(canvas)
            val levelBitmap = provider?.getLevelBitmap(level) ?: return
            //等級圖片的寬度
            val levelBitmapWidth = levelBitmap.width
    
            val dst = Rect()
            when (mNumber) {
                in 0..9 -> {
                    initialValue = x - levelBitmapWidth
                    dst.left =  initialValue
                    dst.right = initialValue + levelBitmapWidth
                }
                in 10..99 -> {
                    initialValue  = x - PublicMethod.dp2px(context, 100f)
                    dst.left =  initialValue + numberImageWidth + spacing
                    dst.right = initialValue+ numberImageWidth  + spacing+ levelBitmapWidth
                }
                else -> {
                    initialValue = x - PublicMethod.dp2px(context, 120f)
                    dst.left =  initialValue + 2*numberImageWidth + spacing
                    dst.right = initialValue+ 2*numberImageWidth + spacing + levelBitmapWidth
                }
            }
            dst.top = 0
            dst.bottom = levelBitmap.height
            //繪制等級文案圖標
            canvas.drawBitmap(levelBitmap, null, dst, textPaint)
    
            while (mNumber > 0) {
                val number = mNumber % 10
                val bitmap = provider.getNumberBitmap(number)?:continue
                offsetX += bitmap.width
                //這里是數字
                val rect = Rect()
                rect.top = 0
                when {
                    mNumber/ 10 < 1 -> {
                        rect.left = initialValue - bitmap.width
                        rect.right = initialValue
                    }
                    mNumber/ 10 in 1..9 -> {
                        rect.left = initialValue
                        rect.right = initialValue + bitmap.width
                    }
                    else -> {
                        rect.left = initialValue +  bitmap.width
                        rect.right = initialValue +2* bitmap.width
                    }
                }
    
                rect.bottom = bitmap.height
                //繪制數字
                canvas.drawBitmap(bitmap, null, rect, textPaint)
                mNumber /= 10
            }
    
        }
    
        fun setNumber(number: Int) {
            this.mNumber = number
            if (mNumber >999){
                mNumber = 999
            }
            level = when (mNumber) {
                in 1..20 -> {
                    0
                }
                in 21..80 -> {
                    1
                }
                else -> {
                    2
                }
            }
            //根據等級取出等級文案圖標
            bitmapTalk = provider?.getLevelBitmap(level)
            invalidate()
        }
    }

    3、存放點贊動畫的容器

    我們需要自定義一個view來存放動畫,以及提供開始動畫以及回收動畫view等工作。代碼如下:

    class LikeAnimationLayout @JvmOverloads constructor(
        context: Context,
        attrs: AttributeSet? = null,
        defStyleAttr: Int = 0
    ) : FrameLayout(context, attrs, defStyleAttr) {
    
        private var lastClickTime: Long = 0
        private var currentNumber = 1
        private var mNumberLevelView: NumberLevelView? = null
    
        /**
         * 有無表情動畫 暫時無用
         */
        private var hasEruptionAnimation = false
    
        /**
         * 有無等級文字 暫時無用
         */
        private var hasTextAnimation = false
    
        /**
         * 是否可以長按,暫時無用 目前用時間來管理
         */
        private var canLongPress = false
    
        /**
         * 最大和最小角度暫時無用
         */
        private var maxAngle = 0
        private var minAngle = 0
    
        private var pointX = 0
        private var pointY = 0
        var provider: BitmapProvider.Provider? = null
            get() {
                if (field == null) {
                    field = BitmapProvider.Builder(context)
                        .build()
                }
                return field
            }
    
    
        private fun init(context: Context, attrs: AttributeSet?, defStyleAttr: Int) {
            val typedArray = context.obtainStyledAttributes(
                attrs,
                    R.styleable.LikeAnimationLayout,
                defStyleAttr,
                0
            )
            maxAngle =
                typedArray.getInteger(R.styleable.LikeAnimationLayout_max_angle, MAX_ANGLE)
            minAngle =
                typedArray.getInteger(R.styleable.LikeAnimationLayout_min_angle, MIN_ANGLE)
            hasEruptionAnimation = typedArray.getBoolean(
                    R.styleable.LikeAnimationLayout_show_emoji,
                true
            )
            hasTextAnimation = typedArray.getBoolean(R.styleable.LikeAnimationLayout_show_text, true)
            typedArray.recycle()
    
        }
    
        /**
         * 點擊表情動畫view
         */
        private fun addEmojiView(
            context: Context?,
            x: Int,
            y: Int
        ) {
    
            for (i in 0 .. ERUPTION_ELEMENT_AMOUNT) {
                val layoutParams = RelativeLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT)
                layoutParams.setMargins(x, y, 0, 0)
                val articleThumb = context?.let {
                    EmojiAnimationView(
                        it, provider
                    )
                }
    
                articleThumb?.let {
                    it.setEmoji()
                    this.addView(it, -1, layoutParams)
                    it.setAnimatorListener(object : EmojiAnimationView.AnimatorListener {
                        override fun onAnimationEmojiEnd() {
                            removeView(it)
                            val handler = Handler()
                            handler.postDelayed({
                                if (mNumberLevelView != null && System.currentTimeMillis() - lastClickTime >= SPACING_TIME) {
                                    removeView(mNumberLevelView)
                                    mNumberLevelView = null
                                }
                            }, SPACING_TIME)
                        }
    
                    })
                    it.setEmojiAnimation()
    
                }
    
            }
        }
    
        /**
         * 開啟動畫
         */
        fun launch(x: Int, y: Int) {
            if (System.currentTimeMillis() - lastClickTime >= SPACING_TIME) {
                pointX = x
                pointY = y
                //單次點擊
                addEmojiView(context, x, y-50)
                lastClickTime = System.currentTimeMillis()
                currentNumber = 1
                if (mNumberLevelView != null) {
                    removeView(mNumberLevelView)
                    mNumberLevelView = null
                }
            } else { //連續點擊
                if (pointX != x || pointY != y){
                    return
                }
                lastClickTime = System.currentTimeMillis()
                Log.i(TAG, "當前動畫化正在執行")
                addEmojiView(context, x, y)
                //添加數字連擊view
                val layoutParams = RelativeLayout.LayoutParams(
                    ViewGroup.LayoutParams.MATCH_PARENT,
                    ViewGroup.LayoutParams.WRAP_CONTENT
                )
               layoutParams.setMargins(0, y - PublicMethod.dp2px(context, 60f), 0, 0)
                if (mNumberLevelView == null) {
                    mNumberLevelView = NumberLevelView(context,provider,x)
                    addView(mNumberLevelView, layoutParams)
                }
                currentNumber++
                mNumberLevelView?.setNumber(currentNumber)
            }
        }
    
        companion object {
            private const val TAG = "LikeAnimationLayout"
    
            /**
             * 表情動畫單次彈出個數,以后如果有不同需求可以改為配置
             */
            private const val ERUPTION_ELEMENT_AMOUNT = 8
            private const val MAX_ANGLE = 180
            private const val MIN_ANGLE = 70
            private const val SPACING_TIME = 400L
        }
    
        init {
            init(context, attrs, defStyleAttr)
        }
    }

    注意:動畫完成之后一定要清除view。

    4、啟動動畫

    點贊控件的手勢回調,偽代碼如下:

    holder.likeView.setOnFingerDowningListener(object : OnFingerDowningListener {
        /**
         * 長按回調
         */
        override fun onLongPress(v: View) {
            if (!bean.hasLike) {
                //未點贊
                if (!fistLongPress) {
                    //這里同步點贊接口等數據交互
                    bean.likeNumber++
                    bean.hasLike = true
                    setLikeStatus(holder, bean)
                }
    
                //顯示動畫
                onLikeAnimationListener?.doLikeAnimation(v)
            } else {
                if (System.currentTimeMillis() - lastClickTime <= throttleTime && lastClickTime != 0L) {
                    //處理點擊過后為點贊狀態的情況
                    onLikeAnimationListener?.doLikeAnimation(v)
                    lastClickTime = System.currentTimeMillis()
                } else {
                    //處理長按為點贊狀態后的情況
                    onLikeAnimationListener?.doLikeAnimation(v)
                }
            }
    
            fistLongPress = true
    
        }
    
        /**
         * 長按抬起手指回調處理
         */
        override fun onUp() {
            fistLongPress = false
        }
    
        /**
         * 單擊事件回調
         */
        override fun onDown(v: View) {
            if (System.currentTimeMillis() - lastClickTime > throttleTime || lastClickTime == 0L) {
                if (!bean.hasLike) {
                    //未點贊情況下,點贊接口和數據交互處理
                    bean.hasLike = true
                    bean.likeNumber++
                    setLikeStatus(holder, bean)
                    throttleTime = 1000
                    onLikeAnimationListener?.doLikeAnimation(v)
                } else {
                    //點贊狀態下,取消點贊接口和數據交互處理
                    bean.hasLike = false
                    bean.likeNumber--
                    setLikeStatus(holder, bean)
                    throttleTime = 30
                }
            } else if (lastClickTime != 0L && bean.hasLike) {
                //在時間范圍內,連續點擊點贊,顯示動畫
                onLikeAnimationListener?.doLikeAnimation(v)
            }
            lastClickTime = System.currentTimeMillis()
    
        }
    
    
    })

    在顯示動畫頁面初始化工作時初始化動畫資源:

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_list)
        likeAnimationLayout?.provider = BitmapProviderFactory.getProvider(this)
    }

    在顯示動畫的回調中啟動動畫:

    override fun doLikeAnimation(v: View) {
        val itemPosition = IntArray(2)
        val superLikePosition = IntArray(2)
        v.getLocationOnScreen(itemPosition)
        likeAnimationLayout?.getLocationOnScreen(superLikePosition)
        val x = itemPosition[0] + v.width / 2
        val y = itemPosition[1] - superLikePosition[1] + v.height / 2
        likeAnimationLayout?.launch(x, y)
    }

    四、遇到的問題

    因為流列表中使用了SmartRefreshLayout下拉刷新控件,如果在列表前幾條內容進行點贊動畫當手指移動時觸摸事件會被SmartRefreshLayout攔截去執行下拉刷新,那么手指抬起時點贊控件得不到響應會一直進行動畫操作,目前想到的解決方案是點贊控件在手指按下時查看父布局有無SmartRefreshLayout,如果有通過反射先禁掉下拉刷新功能,手指抬起或者取消進行重置操作。代碼如下:

    override fun dispatchTouchEvent(event: MotionEvent?): Boolean {
        parent?.requestDisallowInterceptTouchEvent(true)
        return super.dispatchTouchEvent(event)
    }
    
    override fun onTouchEvent(event: MotionEvent): Boolean {
        var onTouch: Boolean
        when (event.action) {
            MotionEvent.ACTION_DOWN -> {
                isRefreshing = false
                isDowning = true
                //點擊
                lastDownTime = System.currentTimeMillis()
                findSmartRefreshLayout(false)
                if (isRefreshing) {
                    //如果有下拉控件并且正在刷新直接不響應
                    return false
                }
                postDelayed(autoPollTask, CLICK_INTERVAL_TIME)
                onTouch = true
            }
            MotionEvent.ACTION_UP -> {
                isDowning = false
                //抬起
                if (System.currentTimeMillis() - lastDownTime < CLICK_INTERVAL_TIME) {
                    //小于間隔時間按照單擊處理
                    onFingerDowningListener?.onDown(this)
                } else {
                    //大于等于間隔時間按照長按抬起手指處理
                    onFingerDowningListener?.onUp()
                }
                findSmartRefreshLayout(true)
                removeCallbacks(autoPollTask)
                onTouch = true
            }
            MotionEvent.ACTION_CANCEL ->{
                isDowning = false
                findSmartRefreshLayout(true)
                removeCallbacks(autoPollTask)
                onTouch = false
            }
            else -> onTouch = false
        }
        return onTouch
    }
    
    /**
     * 如果父布局有SmartRefreshLayout 控件,設置控件是否可用
     */
    private fun findSmartRefreshLayout(enable: Boolean) {
        var parent = parent
        while (parent != null && parent !is ContentFrameLayout) {
            if (parent is SmartRefreshLayout) {
                isRefreshing = parent.state == RefreshState.Refreshing
                if (isRefreshing){
                    //如果有下拉控件并且正在刷新直接結束
                    break
                }
                if (!enable && firstClick){
                    try {
                        firstClick = false
                        val field: Field = parent.javaClass.getDeclaredField("mEnableRefresh")
                        field.isAccessible = true
                        //通過反射獲取是否可以先下拉刷新的初始值
                        enableRefresh = field.getBoolean(parent)
                    }catch (e: Exception) {
                        e.printStackTrace()
                    }
                }
                if (enableRefresh){
                    //如果初始值不可以下拉刷新不要設置下拉刷新狀態
                    parent.setEnableRefresh(enable)
                }
                parent.setEnableLoadMore(enable)
                break
            } else {
                parent = parent.parent
            }
        }
    }

    五、實現效果

    Android怎么實現點贊動畫效果

    以上就是“Android怎么實現點贊動畫效果”這篇文章的所有內容,感謝各位的閱讀!相信大家閱讀完這篇文章都有很大的收獲,小編每天都會為大家更新不同的知識,如果還想學習更多的知識,請關注億速云行業資訊頻道。

    向AI問一下細節

    免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

    AI

    赣榆县| 普宁市| 伊川县| 高安市| 宁海县| 松原市| 巴彦淖尔市| 德令哈市| 桂阳县| 沁水县| 汕头市| 兴隆县| 宁乡县| 临漳县| 南昌市| 禄丰县| 托里县| 鄂托克旗| 遂川县| 三亚市| 神池县| 上高县| 梨树县| 温州市| 句容市| 石景山区| 抚顺县| 海城市| 鄂托克前旗| 苏尼特左旗| 象山县| 达尔| 新兴县| 台山市| 九龙城区| 镇雄县| 莱西市| 广西| 大田县| 台湾省| 云和县|