Android自定义控件练手——波浪效果
这一次要绘制出波浪效果,也是小白的我第一次还望轻喷。首先当然是展示效果图啦:

一.首先来说说实现思路。
想到波浪效果,当然我第一反应是用正余弦波来设计啦(也能通过贝塞尔曲线,这里我不提及这个方法但是在demo里这种方法也实现了),肯定要绘制一个静态的波,然后通过不断的对它平移刷新,这样最简单的波浪效果就有了,如果再给它加一个比它提前一定周期的波一起平移,那不是波浪效果的层次就有了。
二.绘制。
首先要绘制一个静态的波形图,嗨呀说来简单但是怎么画呢,不要慌先看下面这张丑图:

通过上面的图我们发现曲线被切分成了无数的竖线,我们可以知道波浪的高低,就是波峰与波谷,根据函数公式,通过不断的绘制竖线,就能得到一个静态波形图。代码如下:
//在宽度以内绘制一条条竖线
while (drawPoint.x < mWidth) {
//第一条波的y坐标
drawPoint.y = (float) (waveHeight - waveDeep * Math.sin(drawPoint.x * anglenum));
canvas.drawLine(drawPoint.x, drawPoint.y, drawPoint.x, mHeight, mPaint);
//跳到下一个点继续
drawPoint.x++;
}
这里我们要注意绘制的默认坐标系如下图:

画出静态的波形图之后,我们只要每次让这个波向前或者向后移动一定周期再不断刷新,就能出现波浪效果了。重写view的ondraw方法就有如下:
drawPoint.x = 0;//重置为0,从原点开始绘制
Double rightperiod = Math.PI / 8 * count;//每次平移Math.PI/8个周期
if (count == 16) {//每次平移Math.PI/8个周期,平移第16次,平移了一个完整的周期
count = 0;//平移了一个完整周期归零重新开始计数
} else {
count++;
} //在宽度以内绘制一条条竖线
while (drawPoint.x < mWidth) {
//第一条波的y坐标
drawPoint.y = (float) (waveHeight - waveDeep * Math.sin(drawPoint.x * anglenum - rightperiod));
//绘制最上面显示主波的竖线
canvas.drawLine(drawPoint.x, drawPoint.y, drawPoint.x, mHeight, mPaint);
//跳到下一个点继续
drawPoint.x++;
}
//定时更新
postInvalidateDelayed(17);
这样一条会动的波浪就基本完成了,主体功能基本实现,之后再另外画一个平移一定周期的波浪并且通过用属性动画ValueAnimator对波浪波峰波谷与水位变化的调控就能达到上面的效果。下面是完整的代码:
public class WaveFunctionView extends View {
private Path mPath;//路径
private Paint mPaint, mPaintMore;//画笔
private PointF drawPoint, drawPoint2;//绘制点
private ValueAnimator animator, animatorh;
private float mWidth, mHeight, waveHeight;//控件宽,控件高,水位
private float waveDeepmin = 8f;//最小的波峰与波谷
private float waveDeepMax = 20f;//最大的波峰与波谷
private float waveDeep = 8f;//波峰与波谷
private float arcRa = 0;//圆半径
private Boolean iscircle = true;//是否是圆形图案
private Boolean antiAlias = true;//是否开启抗锯齿
public String MAINCOLOR_DEF = "#0000AA", NEXTCOLOR_DEF = "#0000FF";//默认颜色
private int mainColor = Color.parseColor(MAINCOLOR_DEF), nextColor = Color.parseColor(NEXTCOLOR_DEF);//颜色
private Double anglenum = Math.PI / 180;
private int count = 0;//绘制次数
public WaveFunctionView(Context context) {
super(context);
init();
}
public WaveFunctionView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}
public WaveFunctionView(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
mWidth = w;//获得控件宽度
mHeight = h;//获得控件高度
if (mWidth > mHeight) {//若要裁剪为圆形,以最短的长度为直径
arcRa = mHeight / 2;
if (iscircle) {
mWidth = mHeight;
}
} else {
arcRa = mWidth / 2;
if (iscircle) {
mHeight = mWidth;
}
}
waveHeight = mHeight;//初始化开始水位
ChangeWaveLevel(5);
super.onSizeChanged(w, h, oldw, oldh);
}
//是否是圆形
public void isCircle(Boolean iscircle) {
this.iscircle = iscircle;
}
//是否开启抗锯齿
public void setAntiAlias(Boolean antiAlias) {
this.antiAlias = antiAlias;
mPaint.setAntiAlias(antiAlias);
mPaintMore.setAntiAlias(antiAlias);
}
//设置主波颜色
public void setMainWaveColor(int color) {
mainColor = color;
mPaint.setColor(color);
}
//设置被遮挡的副波颜色
public void setSecondaryWaveColor(int color) {
nextColor = color;
mPaintMore.setColor(color);
}
@Override
protected void onDraw(Canvas canvas) {
// TODO Auto-generated method stub
super.onDraw(canvas);
if (iscircle) {//判断是否定义为圆形
mPath.reset();//重置路径
mPath.addCircle(arcRa, arcRa, arcRa, Path.Direction.CW);//画以(arcRa,arcRa),半径为arcRa的顺时针的圆
canvas.clipPath(mPath);//裁剪
}
drawPoint.x = 0;//重置为0,从原点开始绘制
Double rightperiod = Math.PI / 8 * count;//每次平移Math.PI/8个周期
if (count == 16) {//每次平移Math.PI/8个周期,平移第16次,平移了一个完整的周期
count = 0;//平移了一个完整周期归零重新开始计数
} else {
count++;
}
//在宽度以内绘制一条条竖线
while (drawPoint.x < mWidth) {
//第一条波的y坐标
drawPoint.y = (float) (waveHeight - waveDeep * Math.sin(drawPoint.x * anglenum - rightperiod));
//第二条波的y坐标,比第一条向右移动了Math.PI/2个周期
drawPoint2.y = (float) (waveHeight - waveDeep * Math.sin(drawPoint.x * anglenum - rightperiod - Math.PI / 2));
//先绘制被遮挡的副波的竖线
canvas.drawLine(drawPoint.x, drawPoint2.y, drawPoint.x, mHeight, mPaintMore);
//绘制最上面显示主波的竖线
canvas.drawLine(drawPoint.x, drawPoint.y, drawPoint.x, mHeight, mPaint);
//跳到下一个点继续
drawPoint.x++;
}
//定时更新
postInvalidateDelayed(17);
}
private void init() {
mPath = new Path();
mPaint = new Paint();
mPaint.setColor(mainColor);//设置颜色
mPaint.setAntiAlias(antiAlias);//抗锯齿(性能影响)
mPaint.setStyle(Paint.Style.FILL);
mPaint.setAlpha(50);
mPaintMore = new Paint();
mPaintMore.setAntiAlias(antiAlias);//抗锯齿
mPaintMore.setStyle(Paint.Style.FILL);
mPaintMore.setColor(nextColor);//设置颜色
mPaintMore.setAlpha(30);
drawPoint = new PointF(0, 0);
drawPoint2 = new PointF(0, 0);
}
public void ChangeWaveLevel(int percent) {
animator = ValueAnimator.ofFloat(waveDeepmin, waveDeepMax);//设置属性值变化范围,最大波峰波谷与最小
animator.setDuration(1000);//设置动画时间
animator.setInterpolator(new LinearInterpolator());//控制动画的变化速率
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
waveDeep = (float) animation.getAnimatedValue();
}
});
animator.setRepeatMode(ValueAnimator.REVERSE);//往返模式
animator.setRepeatCount(1);
animatorh = ValueAnimator.ofFloat(waveHeight, mHeight * (10 - percent) / 10);//水位变化
animatorh.setDuration(2000);//设置动画时间
animatorh.setInterpolator(new DecelerateInterpolator());//控制动画的变化速率
animatorh.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
waveHeight = (float) animation.getAnimatedValue();
}
});
animator.start();//开始动画
animatorh.start();//开始动画
}
}
完整代码
GitHub:https://github.com/SteinsGateZero/Mybeisaierwavetest.git
虽然简单,但是推荐还是得自己动手做一做。
Android自定义控件练手——波浪效果的更多相关文章
- Android自定义控件练手——简单的时钟
首先这应该是一个老生常谈的设计了,但是毕竟身为小白的自己都没动手做过,不动手怎么提高自己呢,所以在这梅林沉船闲暇之际,我就把我的设计流程与思路记录下来.首先来看看效果图吧: 如上图就是一个简单并没有美 ...
- android自定义控件(5)-实现ViewPager效果
对于系统的ViewGroup我们已经是十分熟悉了,最常用的LinearLayout和RelativeLayout几乎是天天要打交道,下面我们就来看看,如何一步一步将其实现: 一.首先当然也是最通常的新 ...
- Android自定义控件实战——水流波动效果的实现WaveView
转载请声明出处http://blog.csdn.net/zhongkejingwang/article/details/38556891 水流波动的波形都是三角波,曲线是正余弦曲线,但是Android ...
- Android自定义控件简单实现ratingbar效果
先上图: 一开始让我自定义控件我是拒绝的,因为android很早以前就有一个控件ratingbar,但是设置样式的时候我发现把图片设置小一点就显示不全,一直找不到解办法!(可以设置系统的自带的小样式) ...
- Android自定义控件-Path之贝赛尔曲线和手势轨迹、水波纹效果
从这篇开始,我将延续androidGraphics系列文章把图片相关的知识给大家讲完,这一篇先稍微进阶一下,给大家把<android Graphics(二):路径及文字>略去的quadTo ...
- android自定义控件(4)-自定义水波纹效果
一.实现单击出现水波纹单圈效果: 照例来说,还是一个自定义控件,观察这个效果,发现应该需要重写onTouchEvent和onDraw方法,通过在onTouchEvent中获取触摸的坐标,然后以这个坐标 ...
- Android 自定义控件实现刮刮卡效果 真的就只是刮刮卡么
转载请标明出处:http://blog.csdn.net/lmj623565791/article/details/40162163 , 本文出自:[张鸿洋的博客] 很久以前也过一个html5的刮刮卡 ...
- jQuery练手:仿新浪微博图片文字列表淡进淡出上下滚动效果
1.效果及功能说明 仿新浪微博图片文字列表上下淡进淡出间歇上下滚动 2.实现原理 首先要设定div内只能显示4个图片那么多出来的图片会自动隐藏然后在给图片添加一个动画的事件让他们可以滚动的播放出来上下 ...
- Android自定义控件实战——滚动选择器PickerView
转载请声明出处http://blog.csdn.net/zhongkejingwang/article/details/38513301 手机里设置闹钟需要选择时间,那个选择时间的控件就是滚动选择器, ...
随机推荐
- [HAOI2006]受欢迎的牛 tarjan缩点 BZOJ1051
题目背景 本题测试数据已修复. 题目描述 每头奶牛都梦想成为牛棚里的明星.被所有奶牛喜欢的奶牛就是一头明星奶牛.所有奶 牛都是自恋狂,每头奶牛总是喜欢自己的.奶牛之间的“喜欢”是可以传递的——如果A喜 ...
- shell操作数组
#!/bin/bash nums=( ) echo ${#nums[*]} #向数组中添加元素 nums[]="http://c.biancheng.net/shell/" ech ...
- 【转】eclipse上的.properties文件中文编辑显示问题
首先,解决.properties文件中的中文编辑问题,我们发现,在.properties文件中输入的中文变成了这个样子: 这是eclipse的.properties文件,默认的编码方式是iso-885 ...
- doors dxl 遍历object 查找
Module m = current; //m = edit(“xxx”) Object o for o in m do { string sht = o.”shtName” Buffer bf=cr ...
- 网络流EdmondsKarp算法模板理解
先推荐一个讲网络流的博客,我的网络流知识均吸收于此 传送门 EdmondsKarp算法基本思想:从起点到终点进行bfs,只要存在路,说明存在增广路径,则取这部分路 权值最小的一部分,即为增广路径( ...
- POJ1185炮兵阵地(状态压缩DP)
POJ飞翔.数据弱 ZQOJ飞翔 数据强 Description 司令部的将军们打算在N×M的网格地图上部署他们的炮兵部队.一个N×M的地图由N行M列组成,地图的每一格可能是山地(用"H&q ...
- Educational Codeforces Round 7 A
Description Consider the infinite sequence of integers: 1, 1, 2, 1, 2, 3, 1, 2, 3, 4, 1, 2, 3, 4, 5. ...
- vue.js依赖安装和引入
在项目开发过程中我们要安需安装依赖,安装方法 1.可以在项目的package.json文件中的dependencies写入依赖名称和依赖版本,然后打开命令行工具进入项目运行vue install安装 ...
- apply,call,bind
/*apply和call都是为了改变某个函数运行时的上下文而存在的(就是为了改变函数内部this的指向): 如果使用apply或call方法,那么this指向他们的第一个参数,apply的第二个参数是 ...
- 结合element-ui表格自动生成sku规格列表
最近在写一个根据输入的规格,属性值动态生成sku表格,实现的效果大致如图,这是在vue项目里,结合element-UI表格写的,写好了就整理了一下,把代码贴上来,方便以后使用,不过代码里还是有一些重复 ...