Android自定义View——贝塞尔曲线实现抛物线效果

效果展示

Android自定义View——贝塞尔曲线实现抛物线效果

原理分析

抛物线效果最主要的难点和原理在于贝塞尔曲线动画的生成,我们通过图片主要讲解贝塞尔曲线动画,这里用到的是二级贝塞尔曲线

1、需要找到贝塞尔曲线的三个点,开启点、结束点、控制点
2、通过二级贝塞尔曲线的公式计算,获取贝塞尔曲线的轨迹路径点
3、通过设置点赞图片X,Y坐标,从而形成点赞的效果

Android自定义View——贝塞尔曲线实现抛物线效果

实现步骤

1、初始化变量

class Bezier2Layout : RelativeLayout, View.OnClickListener {

    private var startPoint = Point(0, 0)
    private var endPoint = Point(0, 0)
    private var controlPoint = Point(0, 0)
    private var currentPoint = Point(0, 0)
    private var valueAnimator: ValueAnimator? = null

    private var imageView: ImageView? = null
    private var layoutParams: RelativeLayout.LayoutParams =
            RelativeLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT)

    constructor(context: Context?) : this(context, null)
    constructor(context: Context?, attrs: AttributeSet?) : this(context, attrs, 0)
    constructor(context: Context?, attrs: AttributeSet?, defStyleAttr: Int) : super(context, attrs, defStyleAttr) {
        initView()
        initPoint()
        setOnClickListener(this)
    }

    /**
     * 初始化视图
     */
    private fun initView() {
        imageView = ImageView(context)
        imageView?.layoutParams = layoutParams
        imageView?.visibility = View.INVISIBLE
        imageView?.setBackgroundResource(R.drawable.christmas05)
        addView(imageView)
    }

    /**
     * 初始化三个点
     */
    fun initPoint() {
        this.startPoint = Point(0, 0)
        this.endPoint = Point(dp2px(context, 200f), dp2px(context, 200f))
        this.controlPoint = Point(dp2px(context, 100f), 0)
    }

    override fun onDetachedFromWindow() {
        super.onDetachedFromWindow()
        valueAnimator?.cancel()
    }

    /**
     * 点击开始动画
     */
    override fun onClick(v: View?) {
        startAnimation()
    }
    
    fun dp2px(context: Context, dpVal: Float): Int {
        return TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
                dpVal, context.resources.displayMetrics).toInt()
    }
}

2、点赞效果的实现

fun startAnimation() {
    valueAnimator?.cancel()
    valueAnimator = ValueAnimator.ofObject(BezierEvaluator(controlPoint), startPoint, endPoint).apply {
        addUpdateListener {
            currentPoint = it.animatedValue as Point
            imageView?.x = currentPoint.x.toFloat()
            imageView?.y = currentPoint.y.toFloat()
        }
        addListener(object : Animator.AnimatorListener {
            override fun onAnimationRepeat(animation: Animator?) {
            }

            override fun onAnimationEnd(animation: Animator?) {
                imageView?.visibility = View.INVISIBLE
            }

            override fun onAnimationCancel(animation: Animator?) {
                imageView?.visibility = View.INVISIBLE
            }

            override fun onAnimationStart(animation: Animator?) {
                imageView?.visibility = View.VISIBLE
            }
        })
        interpolator = AccelerateInterpolator()
        duration = 1000
        start()
    }
}

3、贝塞尔曲线动画

它需要一个估值器,不断的计算它的运行轨迹,从起始点到终点开始计算,当中也一个支撑点进行辅助计算,这些都是由贝塞尔曲线的公式所决定的

open class BezierEvaluator : TypeEvaluator<Point> {

    var controlPoint: Point

    constructor(controlPoint: Point) {
        this.controlPoint = controlPoint
    }

    override fun evaluate(t: Float, startValue: Point, endValue: Point): Point {
        var x = (1 - t) * (1 - t) * startValue.x + 2 * t * (1 - t) * controlPoint.x + t * t * endValue.x
        var y = (1 - t) * (1 - t) * startValue.y + 2 * t * (1 - t) * controlPoint.y + t * t * endValue.y
        return Point(x.toInt(), y.toInt())
    }
}

在不断的计算过程中,我们就可以一直获取它的轨迹点,从而执行我们的属性动画,实现贝塞尔曲线动画

addUpdateListener {
    currentPoint = it.animatedValue as Point
    imageView?.x = currentPoint.x.toFloat()
    imageView?.y = currentPoint.y.toFloat()
}

5、View的使用

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <com.example.test.customView.Bezier2Layout
        android:layout_width="match_parent"
        android:layout_height="match_parent" />

</RelativeLayout>
上一篇:android – 如何使用共享首选项保存图像路径


下一篇:如何在android中开发带有图像的PagerSlidingTabStrip?