贝塞尔曲线在Android中的应用

来源:互联网 发布:淘宝关键词竞争指数 编辑:程序博客网 时间:2024/06/05 23:42

       今天要讲解的内容是Android中贝塞尔曲线的应用。可能很多人对贝塞尔曲线不甚了解,这里先对它的概念做一下简单介绍。

       贝塞尔曲线由多个点组成:起始点、终止点以及0到n个相互分离的中间点。根据中间点的不同,可以分为线性贝塞尔曲线、二阶贝塞尔曲线、三阶贝塞尔曲线和高阶贝塞尔曲线。一般的矢量图形软件通过它来精确画出曲线,贝塞尔曲线由线段与节点组成,节点是可拖动的支点,线段像可伸缩的皮筋。对于三阶贝塞尔曲线,它由两个锚点P0、P3和两个中间点P1、P2组成。曲线起始于P0走向P1,并从P2的方向来到P3。曲线一般不会经过P1和P2,这两个点只是提供方向资讯。P0和P1之间的间距,决定了曲线在转而趋进P3之前,走向P2方向的“长度有多长”。关于贝塞尔曲线的更多内容,可以从这里了解:http://blog.csdn.net/androidzhaoxiaogang/article/details/8680330

下面是二阶、三阶和四阶曲线的效果图,红色曲线为最终绘制出来的结果,大家可以了解一下。

                                       


       有个网站为我们提供了工具,可以生成对应的二阶贝塞尔曲线的数值:cubic-bezier.com。拖拽左边图像中的2个中间点,就会在右边显示两个中间点归一化的坐标值。我们还可以点击SAVE按钮将曲线保存下来,然后点击GO就可以查看通过当前曲线计算出来的差值器的效果。具体功能看下面的图。




       了解了贝塞尔曲线的基本概念,下面来看一下贝塞尔曲线在Android中的具体应用。贝塞尔曲线在Android中主要有三个用途:动画差值器、绘制动画轨迹、实现平滑绘图。

动画差值器

       我们知道Android动画可以设置差值器Interpolator来修饰动画效果,其作用是把0到1的浮点值变化映射到另一个浮点值变化,然后将这个值作为动画的变化率。定义差值器需要实现Interpolator接口,然后根据三阶贝塞尔曲线的公式,得到下面的差值器。其中p1、p2是两个中间点的坐标,需要调用者传入来得到不同的曲线。如果我们要得到曲线横轴方向上的变化率,就将两个中间点的y坐标带入公式;否则,就将两个中间点的x坐标带入公式。

public class BezierInterpolator implements Interpolator {    float p1;    float p2;    BezierInterpolator(float p1, float p2) {        this.p1 = p1;        this.p2 = p2;    }    @Override    public float getInterpolation(float t) {        return 0 * (1 - t) * (1 - t) * (1 - t)                + 3 * p1 * t * (1 - t) * (1 - t)                + 3 * p2 * t * t * (1 - t)                + 1 * t * t * t;    }}
绘制动画轨迹

       说明这个问题之前,必须先了解属性动画中的TypeEvaluator。TypeEvaluator有什么作用呢?简单来说,就是告诉系统动画如何从初始值过度到结束值。其实这个TypeEvaluator也可以理解为差值器,只是它的定义是泛型的,我们可以将泛型参数设置为PointF,就能得到当前运动轨迹的坐标位置。而Interpolator只是一个线性的差值器,只能得到运动速率的变化,不能得到运动轨迹。如果还不理解,就看下面的代码吧。

public class BezierEvaluator implements TypeEvaluator<PointF> {    PointF mPointF1;    PointF mPointF2;    public BezierEvaluator(PointF mPointF1, PointF mPointF2) {        this.mPointF1 = mPointF1;        this.mPointF2 = mPointF2;    }    @Override    public PointF evaluate(float t, PointF point0, PointF point3) {        //t 百分比 0~1        PointF pointF = new PointF();        pointF.x = point0.x * (1 - t) * (1 - t) * (1 - t)                + 3 * mPointF1.x * t * (1 - t) * (1 - t)                + 3 * mPointF2.x * t * t * (1 - t)                + point3.x * t * t * t;        pointF.y = point0.y * (1 - t) * (1 - t) * (1 - t)                + 3 * mPointF1.y * t * (1 - t) * (1 - t)                + 3 * mPointF2.y * t * t * (1 - t)                + point3.y * t * t * t;        return pointF;    }}

    private ValueAnimator getBezierValueAnimator() {        PointF pointf0 = new PointF(0, 0);        PointF pointf1 = new PointF(width / 8, height * 7 / 8);        PointF pointf2 = new PointF(width * 7 / 8, height / 8);        PointF pointf3 = new PointF(width - 100, height - 100);        //通过贝塞尔曲线公式,自定义估值器        final BezierEvaluator evaluator = new BezierEvaluator(pointf1, pointf2);        //将估值器传入属性动画,不断的修改控件的坐标        ValueAnimator animator = ValueAnimator.ofObject(evaluator, pointf0, pointf3);        animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {            @Override            public void onAnimationUpdate(ValueAnimator animation) {                PointF pointf = (PointF) animation.getAnimatedValue();                mImageView.setX(pointf.x);                mImageView.setY(pointf.y);            }        });        animator.setTarget(mImageView);        animator.setDuration(3000);        animator.setInterpolator(new BezierInterpolator(1.61f, -0.26f));        return animator;    }

实现平滑绘图

       实现一个绘图功能,基本思路就是处理Touch事件,通过调用lineTo去更新Path,然后通过Canvas的drawPath不断地重绘。然而,这样绘出来的曲线在弯曲处不平滑。如果要绘制出平滑的曲线,就需要用到贝塞尔曲线。Android的Path类的quadTo方法恰恰就封装了二阶贝塞尔曲线的功能。下面看具体的实现代码。

public class BezierDrawView extends View {    private float mX;    private float mY;    private final Paint mGesturePaint = new Paint();    private final Path mPath = new Path();    public BezierDrawView(Context context) {        this(context, null);    }    public BezierDrawView(Context context, AttributeSet attrs) {        this(context, attrs, 0);    }    public BezierDrawView(Context context, AttributeSet attrs, int defStyleAttr) {        super(context, attrs, defStyleAttr);        mGesturePaint.setAntiAlias(true);        mGesturePaint.setStyle(Paint.Style.STROKE);        mGesturePaint.setStrokeWidth(8);        mGesturePaint.setColor(Color.RED);    }    @Override    public boolean onTouchEvent(MotionEvent event) {        switch (event.getAction()) {            case MotionEvent.ACTION_DOWN:                handleDown(event);                break;            case MotionEvent.ACTION_MOVE:                handleMove(event);        }        invalidate();        return true;    }    @Override    protected void onDraw(Canvas canvas) {        super.onDraw(canvas);        canvas.drawPath(mPath, mGesturePaint);    }    private void handleDown(MotionEvent event) {        mPath.reset();        float x = event.getX();        float y = event.getY();        mX = x;        mY = y;        mPath.moveTo(x, y);    }    private void handleMove(MotionEvent event) {        final float x = event.getX();        final float y = event.getY();        final float preX = mX;        final float preY = mY;        final float dx = Math.abs(x - preX);        final float dy = Math.abs(y - preY);        //两点之间的距离大于等于3时,生成贝塞尔绘制曲线        if (dx >= 3 || dy >= 3) {            //设置贝塞尔曲线的操作点为起点和终点的一半            float cX = (x + preX) / 2;            float cY = (y + preY) / 2;            //二次贝塞尔,实现平滑曲线;previousX, previousY为操作点,cX, cY为终点            mPath.quadTo(preX, preY, cX, cY);//            mPath.lineTo(x, y);            mX = x;            mY = y;        }    }}

       对比一下通过quadTo和lineTo绘制出来曲线的不同,左边的曲线比右边的曲线要平滑一些,仔细看右边的图会发现曲线上有明显的拐角。

                                                        

         下面是贝塞尔曲线在Android中应用的最终动效图,我们把三个功能集成到了一个页面中。



点我下载Demo


参考文章:

http://www.cnblogs.com/benhero/p/4377374.html?utm_source=tuicool&utm_medium=referral

http://blog.csdn.net/androidzhaoxiaogang/article/details/8680330

2 0
原创粉丝点击