专栏名称: 开发者全社区
分享和推送Java/Android方向的技术和文章,让你成为这方面的大牛,让你每天都成长一点。同时,我们也会邀请BAT的大牛分享原创!
目录
相关文章推荐
stormzhang  ·  双十一晚会不香了? ·  昨天  
鸿洋  ·  Android从上帝视角来看PackageM ... ·  6 天前  
stormzhang  ·  不让人们存钱了? ·  1 周前  
51好读  ›  专栏  ›  开发者全社区

Android水波运动效果实现:Android .Path贝塞尔绘制及操作详解

开发者全社区  · 公众号  · android  · 2017-03-09 10:16

正文

相关阅读:

吊炸天!74款APP完整源码!

Android又一重磅利器—Lottie安卓开源动画库介绍和使用

[干货]2017已来,最全面试总结——这些Android面试题你一定需要


Path是什么?有什么作用


Path主要用于绘制复杂的图形轮廓,比如折线,圆弧以及各种复杂图案

Path是封装了由直线和曲线(二次,三次贝塞尔曲线)构成的几何路径。你能用Canvas中的drawPath来把这条路径画出来(同样支持Paint的不同绘制模式),

也可以用于剪裁画布和根据路径绘制文字。我们有时会用Path来描述一个图像的轮廓,所以也会称为轮廓线(轮廓线仅是Path的一种使用方法,两者并不等价)

有了它,配合自定义View的draw()就可以实现各种动画效果,比如水滴,折线,重力下降等等

Path有哪些方法


moveTo  移动下一次操作的起点位置

setLastPoint  重置当前path中最后一个点位置,如果在绘制之前调用,效果和moveTo相同

lineTo   添加上一个点到当前点之间的直线到Path

addRect   添加矩形

addRoundRect   添加圆角矩形

addOval   添加椭圆

addCircle   添加圆

addPath   添加路径

addArc    添加圆弧

close   连接第一个点连接到最后一个点,形成一个闭合区域

isEmpty   判断Path是否为空

isRect   判断path是否是一个矩形

set   用新的路径替换到当前路径所有内容

offset   对当前路径之前的操作进行偏移(不会影响之后的操作)

quadTo, cubicTo   分别为二次和三次贝塞尔曲线的方法

水波Demo


下面是一位网友提供的一个水波Demo,逻辑和代码很清晰,不过代码中Handler类过时了,稍作了修改。

关于Handler非静态申明引起内存泄露问题      

再谈android内存泄漏—常见的八种导致 APP 内存泄漏的问题

【干货】Android内存泄漏分析实战和心得-面试常考点             

1、自定义WaveView.java类

import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.List;
import java.util.Timer;
import java.util.TimerTask;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Paint.Align;
import android.graphics.Paint.Style;
import android.graphics.Region.Op;
import android.graphics.Path;
import android.graphics.RectF;
import android.os.Handler;
import android.os.Message;
import android.util.AttributeSet;
import android.view.View;

public class WaveView extends View{    private int mViewWidth;    private int mViewHeight;     //水位线    private float mLevelLine;    //波浪起伏幅度    private float mWaveHeight = 80;    //波长    private float mWaveWidth = 200;    //被隐藏的最左边的波形    private float mLeftSide;    private float mMoveLen;    //水波平移速度    public static final float SPEED = 1.7f;    private ListPoint> mPointsList;    private Paint mPaint;    private Paint mTextPaint;    private Path mWavePath;    private boolean isMeasured = false;    private Timer timer;    private MyTimerTask mTask;    private Context mContext;    private class MyHandler extends Handler {        private final WeakReferenceWaveView> mainActivityWeakReference;        private MyHandler(WaveView mainActivityWeakReference) {            this.mainActivityWeakReference =
                       new WeakReferenceWaveView>(mainActivityWeakReference);        }        @Override        public void handleMessage(Message msg) {            super.handleMessage(msg);            WaveView activity = mainActivityWeakReference.get();            if(activity != null) {                // 记录平移总位移                mMoveLen += SPEED;                // 水位上升                mLevelLine -= 0.1f;                if (mLevelLine 0)                    mLevelLine = 0;                mLeftSide += SPEED;                // 波形平移                for (int i = 0; i mPointsList.size(); i++)                {                    mPointsList.get(i).setX(mPointsList.get(i).getX() + SPEED);                    switch (i % 4)                    {                        case 0:                        case 2:                            mPointsList.get(i).setY(mLevelLine);                            break;                        case 1:                            mPointsList.get(i).setY(mLevelLine + mWaveHeight);                            break;                        case 3:                            mPointsList.get(i).setY(mLevelLine - mWaveHeight);                            break;                    }                }                if (mMoveLen >= mWaveWidth)                {                    // 波形平移超过一个完整波形后复位                    mMoveLen = 0;                    resetPoints();                }                invalidate();            }        }    }    /**     * 所有点的x坐标都还原到初始状态,也就是一个周期前的状态     */    private void resetPoints()    {        mLeftSide = -mWaveWidth;        for (int i = 0; i mPointsList.size(); i++)        {            mPointsList.get(i).setX(i * mWaveWidth / 4 - mWaveWidth);        }    }    public WaveView(Context context)    {        super(context);        this.mContext = context;        init();    }    public WaveView(Context context, AttributeSet attrs)    {        super(context, attrs);        this.mContext = context;        init();    }    public WaveView(Context context, AttributeSet attrs, int defStyle)    {        super(context, attrs, defStyle);        this.mContext = context;        init();    }    private void init()    {        mPointsList = new ArrayListPoint>();        timer = new Timer();        mPaint = new Paint();        mPaint.setAntiAlias(true);        mPaint.setStyle(Style.FILL);        mPaint.setColor(Color.BLUE);        mTextPaint = new Paint();        mTextPaint.setColor(Color.WHITE);        mTextPaint.setTextAlign(Align.CENTER);        mTextPaint.setTextSize(30);        mWavePath = new Path();    }    //页面加载完毕执行    @Override    public void onWindowFocusChanged(boolean hasWindowFocus)    {        super.onWindowFocusChanged(hasWindowFocus);        // 开始波动        start();    }    private void start()    {        if (mTask != null)        {            mTask.cancel();            mTask = null;        }        mTask = new MyTimerTask(new MyHandler(this));        timer.schedule(mTask, 0, 10);    }    @Override    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)    {        super.onMeasure(widthMeasureSpec, heightMeasureSpec);        if (!isMeasured)        {            isMeasured = true;            mViewHeight = getMeasuredHeight();            mViewWidth = getMeasuredWidth();            // 水位线从最底下开始上升            mLevelLine = mViewHeight;            // 根据View宽度计算波形峰值            mWaveHeight = mViewWidth / 2.5f;            // 波长等于四倍View宽度也就是View中只能看到四分之一个波形,这样可以使起伏更明显            mWaveWidth = mViewWidth * 4;            // 左边隐藏的距离预留一个波形            mLeftSide = -mWaveWidth;            // 这里计算在可见的View宽度中能容纳几个波形,注意n上取整            int n = (int) Math.round(mViewWidth / mWaveWidth + 0.5);            // n个波形需要4n+1个点,但是我们要预留一个波形在左边隐藏区域,所以需要4n+5个点            for (int i = 0; i (4 * n + 5); i++)            {                // 从P0开始初始化到P4n+4,总共4n+5个点                float x = i * mWaveWidth / 4 - mWaveWidth;                float y = 0;                switch (i % 4)                {                    case 0:                    case 2:                        // 零点位于水位线上                        y = mLevelLine;                        break;                    case 1:                        // 往下波动的控制点                        y = mLevelLine + mWaveHeight;                        break;                    case 3:                        // 往上波动的控制点                        y = mLevelLine - mWaveHeight;                        break;                }                mPointsList.add(new Point(x, y));            }        }    }    @Override    protected void onDraw(Canvas canvas)    {        mWavePath.reset();        int i = 0;        mWavePath.moveTo(mPointsList.get(0).getX(), mPointsList.get(0).getY());        for (; i mPointsList.size() - 2; i = i + 2)        {            mWavePath.quadTo(mPointsList.get(i + 1).getX(),                    mPointsList.get(i + 1).getY(), mPointsList.get(i + 2)                            .getX(), mPointsList.get(i + 2).getY());        }        mWavePath.lineTo(mPointsList.get(i).getX(), mViewHeight);        mWavePath.lineTo(mLeftSide, mViewHeight);        mWavePath.close();        // mPaint的Style是FILL,会填充整个Path区域        canvas.drawPath(mWavePath, mPaint);        // 绘制百分比        canvas.drawText("" + ((int) ((1 - mLevelLine / mViewHeight) * 100))                + "%", mViewWidth / 2, mLevelLine + mWaveHeight
               
+ (mViewHeight - mLevelLine - mWaveHeight) / 2, mTextPaint);    }    class MyTimerTask extends TimerTask    {        Handler handler;        public MyTimerTask(Handler handler)        {            this.handler = handler;        }        @Override        public void run()        {            handler.sendMessage(handler.obtainMessage());        }    }    class Point    {        private float x;        private float y;        public float getX()        {            return x;        }        public void setX(float x)        {            this.x = x;        }        public float getY()        {            return y;        }        public void setY(float y)        {            this.y = y;        }        public Point(float x, float y)        {            this.x = x;            this.y = y;        }    }}

2、在布局activity.xml中引用自定义WaveView

...>
           android:layout_width="100dp"        android:layout_height="wrap_content" />

然后直接运行就行,效果如下

源码连接:http://blog.csdn.net/vrix/article/details/39206975

原文:http://www.iyuyao.top/index.php/post/62.html

看完本文有收获?请分享给更多人

Java和Android大牛频道

欢迎关注我们,一起讨论技术,扫描和长按下方的二维码可快速关注我们。搜索微信公众号:JANiubility。

公众号:JANiubility