自定义控件之水波纹效果
前言
最近花了将近一周时间拜读了任玉刚大神的《Android开发艺术探索》,这本书很适合进阶阶段的童靴阅读,因为定位就是进阶书籍,所以很多基础的知识都是一笔带过的。在书本中学到了很多之前忽略的细节知识点。下面我想写一个水波纹效果的自定义控件,权当练练手。
写之前得了解贝塞尔曲线,贝塞尔曲线真是一个有意思的东西,这里给大家推荐一个链接Android自定义控件-Path之贝赛尔曲线和手势轨迹、水波纹效果,其实这个水波纹效果还是很好实现的,只要知道了其中的一些方法加上灵活运用即可(Path类的使用和动画效果)。
首先说一下思路吧,我们需要画出两条完整的波浪线(屏幕中和屏幕外各一条,为了看起来好看),然后给动画设置一个变量值,不停的根据这个值重新绘制波浪的话,那么这个动画就出现了。
下面开始正式的代码步骤了,我们写一个MyWaveView继承View,成员变量需要一个画笔Paint,路径Path,还有波长mWaveLength(我们为了波浪线的美观,我简单试验了多种波长,感觉只有完整波长正好等于屏幕宽度的时候波浪线抖动看起来比较完美,这里mWaveLength=800像素),还有变量dx和波纹距离顶部距离mCustomY。在构造方法中初始化Path和画笔Paint设置颜色,样式等属性,下面开始相对重要的代码了,我们需要先将起始点移动到屏幕左边第一个波浪起点位置
mPath.moveTo(-mWaveLength + dx, mCustomY);
再for循环画出两个波浪线(Path的r开头的方法和不含r开头的方法其实效果都是一样的,不过r开头的是以当前起点为坐标原点,而不含r的是以坐标原点为起点),这里我习惯用r开头的方法,使用二阶贝塞尔曲线Path.rQuadTo(float dx1, float dy1, float dx2, float dy2)画两个连续的波浪,mPath.rQuadTo(halfWaveLen / 2, -100, halfWaveLen, 0);mPath.rQuadTo(halfWaveLen / 2, 100, halfWaveLen, 0);
再将整个屏幕涵盖住`mPath.lineTo(getWidth(), getHeight());mPath.lineTo(0, getHeight());mPath.close();canvas.drawPath(mPath, mPaint);- 最后写一个启动动画的方法即可实现效果
public void startAnim() {
ValueAnimator animator = ValueAnimator.ofInt(0, mWaveLength);
animator.setDuration(1500);
animator.setRepeatCount(ValueAnimator.INFINITE);
animator.setInterpolator(new LinearInterpolator());
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
dx = (int) animation.getAnimatedValue();
postInvalidate();
}
});
animator.start();
}
- 效果图
- 最后附上完整代码
public class MyWaveView extends View {
private static final String TAG = "MyView";
private Paint mPaint;
private Path mPath;
private int mWaveLength = 800;
private int dx;
private int mCustomY;
public MyWaveView(Context context, AttributeSet attrs) {
super(context, attrs);
mPath = new Path();
mPaint = new Paint();
mPaint.setColor(Color.GREEN);
mPaint.setStyle(Paint.Style.FILL_AND_STROKE);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
mPath.reset();
mCustomY = 400;
int halfWaveLen = mWaveLength / 2;
mPath.moveTo(-mWaveLength + dx, mCustomY);
for (int i = -mWaveLength; i <= getWidth() + mWaveLength; i += mWaveLength) {
Log.i(TAG, "onDraw: "+i);
mPath.rQuadTo(halfWaveLen / 2, -100, halfWaveLen, 0);
mPath.rQuadTo(halfWaveLen / 2, 100, halfWaveLen, 0);
}
mPath.lineTo(getWidth(), getHeight());
mPath.lineTo(0, getHeight());
mPath.close();
canvas.drawPath(mPath, mPaint);
}
public void startAnim() {
ValueAnimator animator = ValueAnimator.ofInt(0, mWaveLength);
animator.setDuration(1500);
animator.setRepeatCount(ValueAnimator.INFINITE);
animator.setInterpolator(new LinearInterpolator());
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
dx = (int) animation.getAnimatedValue();
postInvalidate();
}
});
animator.start();
}
}