Android点赞效果动画

1.效果图.

效果图

2.代码

1.动画视图view

import android.animation.Animator
import android.animation.AnimatorSet
import android.animation.ObjectAnimator
import android.content.Context
import android.graphics.Path
import android.util.AttributeSet
import android.util.Log
import android.view.ViewGroup
import android.widget.FrameLayout
import androidx.appcompat.widget.AppCompatImageView
import androidx.core.util.Pools

/**
 * Just : 
 * @author by Znan
 * @date on 2020/12/12 15
 */
class PriseAnimView : FrameLayout {

    private val TAG = "PriseAnimView"

    //表情对象池
    private val imageViewPool = Pools.SynchronizedPool<AppCompatImageView>(50);

    private val emojiArray = arrayOf(
        R.mipmap.ic_emoji_1,
        R.mipmap.ic_emoji_2,
        R.mipmap.ic_emoji_3,
        R.mipmap.ic_emoji_4,
        R.mipmap.ic_emoji_5
    )

    //阈值 影响动画宽度
    private val minThresholdValue = 700
    private val maxThresholdValue = 900

    //每次點擊出現圖標隨機個數的最大值
    private val MIN_COUNT = 3
    private val MAX_COUNT = 6

    constructor(context: Context) : super(context)

    constructor(context: Context, attributeSet: AttributeSet) : super(context, attributeSet)

    constructor(context: Context, attributeSet: AttributeSet, defStyleAttr: Int) : super(
        context,
        attributeSet,
        defStyleAttr
    )

    fun loadAnim(x: Float, y: Float) {
        Log.w(TAG, "load anim   x->" + x + " y->" + y)
        imageViewPool.acquire()

        val animatorSet = AnimatorSet()
        repeat(getEmojiCount()) {
            val thresholdValue = getRandomThresholdValue()

            //二阶贝塞尔
            val path = Path()

            //起点坐标 阈值偏移10%的随机数
            val startX =
                ((x - thresholdValue / 10).toInt()..(x + thresholdValue / 10).toInt()).random()
                    .toFloat()
            val startY =
                ((y - thresholdValue / 10).toInt()..(y + thresholdValue / 10).toInt()).random()
                    .toFloat()

            //终点坐标
            val endX =
                ((x - thresholdValue).toInt()..(x + thresholdValue).toInt()).random().toFloat()
            val endY = y + (thresholdValue * 0.8.toInt()..thresholdValue * 1.2.toInt()).random()

            //控制点
            val controlX = if (endX < x) {
                (endX.toInt()..x.toInt()).random().toFloat()
            } else {
                (x.toInt()..endX.toInt()).random().toFloat()
            }
            val controlY = ((y - thresholdValue / 2).toInt()..y.toInt()).random().toFloat()

            //路径
            path.moveTo(x, y)
            //path.moveTo(startX, startY)
            path.quadTo(controlX, controlY, endX, endY)

            //添加view 动画
            val imageView = obtainImageView()
            val res = getRandomEmoji()
            imageView.setImageResource(res)
            addView(imageView)
            val alphaAnimator =
                ObjectAnimator.ofFloat(
                    imageView,
                    "alpha",
                    1f,
                    0.9f,
                    0.8f,
                    0.7f,
                    0.6f,
                    0.4f,
                    0.2f,
                    0.0f
                )
            val translateAnimator = ObjectAnimator.ofFloat(imageView, "x", "y", path)
            animatorSet.playTogether(translateAnimator, alphaAnimator)
            animatorSet.addListener(object : Animator.AnimatorListener {
                override fun onAnimationStart(animation: Animator?) {
                }

                override fun onAnimationEnd(animation: Animator?) {
                    recycleImageView(imageView)
                }

                override fun onAnimationCancel(animation: Animator?) {
                }

                override fun onAnimationRepeat(animation: Animator?) {
                }
            })
            animatorSet.duration = 500
            animatorSet.start()
        }
    }

    fun removeAnimView() {
        removeAllViews()
    }

    //获取一个随机阈值
    private fun getRandomThresholdValue(): Int {
        return (minThresholdValue..maxThresholdValue).random()
    }

    private fun getRandomEmoji(): Int {
        return emojiArray[(emojiArray.indices).random()]
    }

    private fun getEmojiCount(): Int {
        return (MIN_COUNT..MAX_COUNT).random()
    }

    /**
     * 獲取imageView對象
     */
    private fun obtainImageView(): AppCompatImageView {
        val instance = imageViewPool.acquire()
        return if (instance == null) {
            val imageView = AppCompatImageView(context)
            val layoutParams = LayoutParams(
                ViewGroup.LayoutParams.WRAP_CONTENT,
                ViewGroup.LayoutParams.WRAP_CONTENT
            )
            imageView.layoutParams = layoutParams
            imageView
        } else {
            instance
        }
    }

    /**
     * 释放imageView对象
     */
    private fun recycleImageView(imageView: AppCompatImageView) {
        removeView(imageView)
        imageView.clearAnimation()
        imageView.setImageResource(0)
        imageViewPool.release(imageView)
    }

}

2.布局

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:id="@+id/cl_root_view"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
    
    <androidx.appcompat.widget.AppCompatImageView
        android:id="@+id/iv_emoji"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:src="@mipmap/ic_emoji_1"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <com.znan.androidtest.PriseAnimView
        android:id="@+id/view_prise_anim"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />


</androidx.constraintlayout.widget.ConstraintLayout>

3.触摸事件 

屏幕坐标可以通过 getLocationOnScreen

//触摸事件 100ms延迟 iv_emoji.setOnTouchListener
RxView.touches(iv_emoji)
            .throttleFirst(100, TimeUnit.MILLISECONDS)
            .subscribe {
                //执行动画
                view_prise_anim.loadAnim(iv_emoji.x, iv_emoji.y)
                when (it.action) {
                    MotionEvent.ACTION_DOWN -> {
                        logw("事件")
                    }
                }
            }
var pollDisposable :Disposable?= null


iv_emoji.setOnTouchListener { v, event ->
            when (event.action) {
                MotionEvent.ACTION_DOWN -> {
                    pollingAnim(iv_emoji.x, iv_emoji.y)
                    logw("ACTION_DOWN")
                }
                MotionEvent.ACTION_UP -> {
                    logw("ACTION_UP")
                    pollDisposable?.dispose()
                }
                MotionEvent.ACTION_CANCEL-> {
                    logw("ACTION_UP")
                    pollDisposable?.dispose()
                }
            }
            false
        }

    
private fun pollingAnim(x:Float,y: Float) {
    Observable.interval(0, 100, TimeUnit.MILLISECONDS)
        .subscribeOn(Schedulers.newThread())
        .observeOn(AndroidSchedulers.mainThread())
        .subscribe(object :Observer<Long>{
            override fun onSubscribe(d: Disposable) {
                pollDisposable = d
            }

            override fun onNext(t: Long) {
                view_prise_anim.loadAnim(x,y)
            }

            override fun onError(e: Throwable) {

            }

            override fun onComplete() {
            }
        })
}
    

 

  • 2
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
以下是一个简单的 Android 点赞变红动画代码示例: 1. 首先在你的布局文件中添加一个 ImageView,用于显示点赞的红心图标: ``` <ImageView android:id="@+id/heart_icon" android:layout_width="wrap_content" android:layout_height="wrap_content" android:src="@drawable/heart_empty" /> ``` 2. 在你的 Activity 或 Fragment 中获取这个 ImageView,并添加一个点击事件监听器: ``` ImageView heartIcon = findViewById(R.id.heart_icon); heartIcon.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { animateHeart(heartIcon); } }); ``` 3. 实现 animateHeart() 方法,使用属性动画让红心图标从空心变成实心,并且放大一些,然后再缩小回原来的大小: ``` private void animateHeart(final ImageView heartIcon) { AnimatorSet animatorSet = new AnimatorSet(); // 缩放动画,从 1.0f 放大到 1.4f,然后再缩小回 1.0f ObjectAnimator scaleXAnimator = ObjectAnimator.ofFloat(heartIcon, "scaleX", 1.0f, 1.4f, 1.0f); ObjectAnimator scaleYAnimator = ObjectAnimator.ofFloat(heartIcon, "scaleY", 1.0f, 1.4f, 1.0f); // 颜色动画,从空心的灰色变成实心的红色 ObjectAnimator colorAnimator = ObjectAnimator.ofInt(heartIcon.getDrawable(), "colorFilter", Color.GRAY, Color.RED); colorAnimator.setEvaluator(new ArgbEvaluator()); animatorSet.playTogether(scaleXAnimator, scaleYAnimator, colorAnimator); animatorSet.setDuration(500); animatorSet.start(); } ``` 这样,当用户点击红心图标时,就会播放一个简单的点赞变红动画

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值