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 手机里设置闹钟需要选择时间,那个选择时间的控件就是滚动选择器, ...
随机推荐
- sublime中设置view_in_browser
使用package control 管理的view-in-browser插件,用户可以在浏览器中打开任意当前sublime Text中tab所处的文件,例如html.js等. 1.Ctrl+Shift ...
- 网站后台登录asp 提示错误号:-2147467259
方法/步骤 1 asp 提示错误号:-2147467259 提示:[] 错误号:-2147467259 错误描述:操作必须使用一个可更新的查询. SQL错误 解决方法 把data文件夹的权限改下 ...
- 2-29 while
do while for
- visio 使用技巧汇总
1.visio中图形旋转任意角度的方法 视图----任务窗格----大小与位置----角度 2.箭头形状 更多形状---流程图----箭头形状 3.汇制虚线框 从基本形状中拖出一个矩形,右击矩形,选择 ...
- POJ 2299 Ultra-QuickSort (树状数组 && 离散化&&逆序)
题意 : 给出一个数n(n<500,000), 再给出n个数的序列 a1.a2.....an每一个ai的范围是 0~999,999,999 要求出当通过相邻两项交换的方法进行升序排序时需要交换 ...
- Nginx根据目录自动生成二级域名
前言: 每次创建二级域名如果都修改一次配置文件的话,项目多了会带来很多不必要的工作量,如果能够在一个web目录下创建一个文件夹并且自动生成文件目录的话,那真是极好的,本文就基于Nginx贴出这 ...
- ButterKnife 8.4注入失败
1,第一步:项目的gradle中增加 classpath 'com.neenbedankt.gradle.plugins:android-apt:1.8'buildscript { repositor ...
- Linux系统调用接口添加简单示例
1. Linux体系结构 Linux系统的地址空间分为用户空间和内核空间,通过系统调用和硬件中断能够完成从用户空间到内核空间的转移. 2. 系统调用接口 ① 一般情况下,用户进程不能访问内核空间.Li ...
- vmware 三种连接方式 如何设置独立ip
转载:https://blog.csdn.net/xiaoyangsavvy/article/details/73718473
- connect to 10.104.11.128 port 9999 (tcp) failed: No route to host
问题: iptables当找到匹配的规则时,就会执行相应的动作,而不会向下继续匹配. 可以看到https没有添加,匹配不到规则,所以就会包错 解决方法: iptables -I INPUT -p tc ...