Kotlin水波扩散动画,尽用它找到了附近妹子!

·  阅读 1626
Kotlin水波扩散动画,尽用它找到了附近妹子!

前言

我们知道石头扔进水里的时候有一圈圈扩撒的效果,非常细腻好看,从5.0开始,也开始有个涟漪的词进入开发中,不少APP又开始新的征程,将这个效果适配到各种手机。

默认的Button就会有这个效果,如果不满意,也可以自定义颜色,效果如下:

录屏_选择区域_20210525142123.gif

但是这种适合在点击的时候触发,在一些情况下需要一直以一圈圈的涟漪扩散显示,例如在某些社交APP中进行妹子搜索时,就可以通过这样的动画,虽然两种效果关系不大,但也是演变而来,效果如下:

录屏_选择区域_20210525143409.gif

实现

观察其中的动画,只有两个元素,一个圆形头像,还有一个是一圈圈的扩散圆,这里的圆形头像使用的是circleimageview库,当然也可以自己绘制,但是鬼知道在哪个版本的手机上会出现问题,所以使用经历过验证的库也是不错的,图片加载使用的是Glide。

implementation 'com.github.bumptech.glide:glide:4.12.0'
annotationProcessor 'com.github.bumptech.glide:compiler:4.12.0'
implementation 'de.hdodenhof:circleimageview:3.1.0'
复制代码

所以整体的布局是这样的:

image.png

在FrameLayout中的onDraw中,会一直通过drawCircle方法绘制圆圈,同时不断设置透明度,从而达到渐渐消失的效果。

绘制圆

绘制圆圈的方法很简单,参数分别是中心x、中心y、圆大小、画笔

drawCircle(float cx, float cy, float radius, @NonNull Paint paint)
复制代码

image.png

那么做到从大小为50的圆,到100的圆,其实就是不断设置参数radius,同时在设置画笔的透明度。

class WaterView @JvmOverloads constructor(
    context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0
) : View(context, attrs, defStyleAttr) {

    var TAG = "TAG";

    var circleSize: Float = 10f;
    var circleAlpha: Int = 255;
    init {

    }


    override fun onDraw(canvas: Canvas) {
        super.onDraw(canvas)
        if (circleAlpha<=0){circleAlpha=0}
        var paint = Paint().apply {
            color = Color.RED
            alpha=circleAlpha
        }

        canvas.drawCircle(
            (measuredWidth / 2).toFloat(),
            (measuredHeight / 2).toFloat(),
            circleSize,
            paint
        )
        circleSize += 20
        circleAlpha-=10

        postInvalidateDelayed(80)
    }
}
复制代码

录屏_选择区域_20210525214056.gif

接下来的工作其实就是当第一个圆圈扩散到一定大小后,增加第二个圆,等第一个圆圈扩散的差不多时候,移除他,然后不断重复这个步骤即可。

计算透明度步长

这个动画最关键的是计算好透明度的递减步长,也就是255-0,我们应该设置多少递减合适,太大的话还没扩散的View的边上,就消失,太小的话,圆圈会让人感觉到都叠加起来,所以这个值我们不能写死。

我的计算方式是这样的,先得到View最小的一边,计算递增多少次可以到达最小的一边,比如最小的一边是825,DIFFUSE_DISTANCE的值是10,那么temp就是82,这个82是圆圈从0开始扩散到View的边上递增的次数,然后通过(255/82)* 2 在得到最终值,

 var min = min(w, h)
 var temp = min / DIFFUSE_DISTANCE;
 circleAlphaReduceStep = ((255 / temp) * 2);
复制代码

完整代码

package com.example.kotlindemo.widget

import android.animation.AnimatorSet
import android.animation.ObjectAnimator
import android.content.Context
import android.graphics.Canvas
import android.graphics.Color
import android.graphics.Paint
import android.os.Handler
import android.os.Looper
import android.util.AttributeSet
import android.util.Log
import android.view.GestureDetector
import android.view.Gravity
import android.view.View
import android.view.animation.OvershootInterpolator
import android.widget.FrameLayout
import androidx.core.os.postDelayed
import com.bumptech.glide.Glide
import de.hdodenhof.circleimageview.CircleImageView
import meow.bottomnavigation.dp
import kotlin.math.max
import kotlin.math.min

class WaterView @JvmOverloads constructor(
    context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0
) : FrameLayout(context, attrs, defStyleAttr), Runnable {

    /**
     * 圆中心点的x和y
     */
    var centerXPoint: Float = 0f;
    var centerYPoint: Float = 0f;

    /**
     * 圆圈默认颜色
     */
    val CIRCLE_DEFAULT_COLOR: Int = Color.parseColor("#FFB6C1")


    /**
     * 等圆圈扩散到这个大小后,增加下一个圆圈
     */
    val CENTER_CIRCLE_SIZE = 75;

    /**
     * 圆圈集合和透明度集合  里面存放的是圆圈宽度和透明度
     */
    var circleList = mutableListOf<Int>()
    var circleAlphaList = mutableListOf<Int>()

    /**
     * 圆圈透明度递减步数
     */
    var circleAlphaReduceStep: Int = 0;

    /**
     * 圆圈扩散步数
     */
    val DIFFUSE_DISTANCE: Int = 10;

    /**
     * 圆圈画笔
     */
    lateinit var circlePaint: Paint

    /**
     * 中心圆ImageView
     */
    var circleImageView: CircleImageView = CircleImageView(context);

    var mHandler = Handler(Looper.myLooper()!!)

    /**
     * 中心圆ImageView动画
     */
    override fun run() {
        var x = ObjectAnimator.ofFloat(circleImageView, "scaleX", 1f, 1.3f, 1f)
        x.interpolator = OvershootInterpolator();

        var y = ObjectAnimator.ofFloat(circleImageView, "scaleY", 1f, 1.3f, 1f)
        y.interpolator = OvershootInterpolator();
        var animatorSet = AnimatorSet()

        animatorSet.setDuration(700)
        animatorSet.playTogether(x, y);
        animatorSet.start()

        mHandler.postDelayed(this, 800)
    }

    init {
        Glide.with(context)
            .load("https://img2.baidu.com/it/u=1194131577,2954769920&fm=26&fmt=auto&gp=0.jpg")
            .into(circleImageView);
        var layoutParams =
            FrameLayout.LayoutParams(CENTER_CIRCLE_SIZE.dp(context), CENTER_CIRCLE_SIZE.dp(context))
        layoutParams.gravity = Gravity.CENTER
        addView(circleImageView, layoutParams)
        addNewCircle();
        setWillNotDraw(false)
        mHandler.postDelayed(this, 0)
        
        circlePaint = Paint().apply {
            color = CIRCLE_DEFAULT_COLOR
        }
    }

    override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {
        super.onSizeChanged(w, h, oldw, oldh)
        centerXPoint = (w / 2).toFloat();
        centerYPoint = (h / 2).toFloat();

        /**
         * 计算出从中心点开始递减透明度的步数
         */
        var min = min(w, h)
        var temp = min / DIFFUSE_DISTANCE;
        circleAlphaReduceStep = ((255 / temp) * 2);
    }

    fun addNewCircle() {
        circleList.add(0)
        circleAlphaList.add(255)
    }

    override fun onDraw(canvas: Canvas) {
        super.onDraw(canvas)

        /**
         * 遍历所有圆圈,第一次的时候数量是 1
         */
        for (i in circleList.indices) {
            circlePaint.alpha = circleAlphaList[i];
            var circleWidth = circleList[i];
            canvas.drawCircle(centerXPoint, centerYPoint, circleWidth.toFloat(), circlePaint)
            circleList[i] = DIFFUSE_DISTANCE + circleList[i];

            /**
             * 设置当前透明度
             */
            circleAlphaList[i] = if (circleAlphaList[i] - circleAlphaReduceStep <= 0) 0 else {
                circleAlphaList[i] - circleAlphaReduceStep
            }
        }

        /**
         * 如果最内圈的宽度大于CENTER_CIRCLE_SIZE,则增加下一个圆
         */
        if (circleList[circleList.lastIndex] > CENTER_CIRCLE_SIZE) {
            addNewCircle();
        }

        /**
         * 如果最外圈的宽度大于measuredWidth,则益处
         */
        if (circleList[0] >= measuredWidth - 100) {
            circleList.removeAt(0);
            circleAlphaList.removeAt(0);

        }

        /**
         * 延迟75毫秒后开始下一次
         */
        postInvalidateDelayed(75)
    }
}
复制代码
分类:
Android
标签: