相关阅读:
吊炸天!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