Android双波浪自定义控件(DoubleWaveView)
发现淘宝个人页顶部的自定义控件很炫酷啊有没有(IOS端),它这里是一个动态的双波纹效果,由于IOS端的效果它有周期性地渐变振幅的功能,比较复杂。对于振幅的渐变效果,当时就想着是怎么实现的,冥思苦想了老半天不得果(每次都重新计算设置正弦函数值,有点太耗费性能了)。
后面又拿起安卓机看了一下安卓客户端的效果,结果发现是个静态的双波纹,What ? 和IOS的差距咋那么大? 想了想,淘宝应该是出于对于安卓机器性能参差不齐的考虑,所以他们的研发人员就没有在安卓机实现动态效果。好了,发一下截图对比:
Android端的效果:
IOS端的效果:
对于一个有追求的攻城狮来说,一知半解是最不能忍受的,所以说干就干,咱也弄一个出来。秉着IT界的真理:“不要重复造轮子”的思想,先在网上查资料看有没有人造好了这个轮子。果然,有类似的博客,但是很多人评论说性能消耗很大,实际使用会比较卡,尤其是在低端机上面。类似效果的博客地址:http://blog.csdn.net/tianjian4592/article/details/44222565。鉴于这样的情况,就自己进行优化吧~按照惯例,先发一下我最后实现出来的效果:
如果你只是抱着直接拿来用的心态的话,这个自定义控件我上传到JCenter上面去了,可以直接
1.引入依赖项目:
compile 'com.xiaosong520:doublewaveview:1.0.1'
2.然后在布局文件中:
<com.doublewave.DoubleWaveViewandroid:id="@+id/waveView"android:layout_width="match_parent"android:layout_height="wrap_content"DoubleWaveView:speedOne="8"DoubleWaveView:speedTwo="6"DoubleWaveView:peakValue="20dp"DoubleWaveView:waveHeight="200dp"DoubleWaveView:waveColor="@color/colorBlue"/>
由于是自定义控件,需要在根布局中添加适配:
xmlns:DoubleWaveView="http://schemas.android.com/apk/res-auto"
3.在Activity中:
waveView = (DoubleWaveView) findViewById(R.id.waveView);waveView.setAnim(false);//默认是开启动画效果的,选false可关闭waveView.setAnim(true);//如果已经关闭,重新设置True开启动画
简单的三步,就可以使用了。
如果你是想理解透它的实现原理,那么请继续往下看~
在开始码码码之前,得做好准备工作,俗话说得好,磨刀不误砍柴工嘛,先搞清楚实现思路:
1.确定水波纹的正弦函数方程;
2.根据函数方程得出每一个波纹上点的坐标(单位:px),并保存到一维数组中;
3.根据正弦函数的坐标不断绘制竖直直线,形成一个静态波纹图;
4.不断调用onDraw方法,改变正弦函数Y值进行重绘,生成动态水波纹。
Step 1:生成波纹曲线
波纹曲线是利用正弦函数来实现的,正弦函数的方程式:y = A*sin(ωx+b)+h
一开始自己也懵逼了,这几个参数分别都是什么来着了啊?努力回忆中。。。 当年的理科学霸小正太现在已然成了老年痴呆社会青年,感觉自己应该是读了个假高中。关于正弦函数的定义,如果你也忘记了的话,自行Google 百度温习一下知识吧~查找资料后可以确定:w影响周期,A影响振幅,h影响y轴位置,b为初相;周期T = 2π/ω。
Step 2:自定义DoubleWaveView的属性
在步骤一中,我们已经了解了正弦函数的各个参数的含义以及计算方法。那么接下来我们就开始自定义View:
首先创建一个DoubleWaveView类 ,继承自View。 由于波纹的颜色、振幅、移动速度等,可能会根据实际情况需要变动,所以我们接下来在项目的res-values目录下创建一个attrs的xml 文件,用于创建自定义属性。关于自定义View的步骤如果还不是蛮太懂的话,可以补习一下自定义View 的知识,推荐张鸿洋的这篇博客: Android 自定义View (一)
这里我定义的attrs属性如下:
<?xml version="1.0" encoding="utf-8"?>
<resources><attr name="peakValue" format="dimension"/><attr name="waveColor" format="color" /><attr name="speedOne" format="integer" /><attr name="speedTwo" format="integer" /><attr name="waveHeight" format="dimension" /><declare-styleable name="DoubleWaveView"><attr name="peakValue" /><attr name="waveColor" /><attr name="speedOne" /><attr name="speedTwo" /><attr name="waveHeight" /></declare-styleable></resources>
正弦函数方程式: y = A*sin(wx+b)+h
五个属性代表的含义:
peakValue :振幅 ,即对应的是A
waveHeight:波浪距离控件底部的高度。描述起来可能有点歧义,看下面的图就明白了
speedOne:第一条波浪的移动速度
speedTwo:第二条波浪的移动速度
waveColor:水波的颜色
Step 3: 绘制双波纹图形
1.先把之前在attr.xml中定义好的属性值,通过构造函数获取到,并设置好画笔,代码都有注释,就不多说了,如果有疑问可以回复讨论:
public DoubleWaveView(Context context, AttributeSet attrs){this(context, attrs, 0);}public DoubleWaveView(Context context){this(context, null);}public DoubleWaveView(Context context, AttributeSet attrs, int defStyle) {super(context, attrs, defStyle);//获取自定义属性值TypedArray a = context.getTheme().obtainStyledAttributes(attrs, R.styleable.DoubleWaveView, defStyle, 0);int n = a.getIndexCount();for (int i = 0; i < n; i++) {int attr = a.getIndex(i);switch (attr) {case R.styleable.DoubleWaveView_peakValue://振幅默认是30dpSTRETCH_FACTOR_A = a.getDimensionPixelSize(attr, (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 30, getResources().getDisplayMetrics()));break;case R.styleable.DoubleWaveView_waveColor:WAVE_PAINT_COLOR = a.getColor(attr, 0x881E90FF);//默认是蓝色break;case R.styleable.DoubleWaveView_speedOne:TRANSLATE_X_SPEED_ONE = a.getInteger(attr,7);//默认是7break;case R.styleable.DoubleWaveView_speedTwo:TRANSLATE_X_SPEED_TWO = a.getInteger(attr,5);//默认是5break;case R.styleable.DoubleWaveView_waveHeight:WaveHeight = a.getDimensionPixelSize(attr, (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 100, getResources().getDisplayMetrics()));//默认100dpbreak;}}a.recycle();// 将dp转化为px,用于控制不同分辨率上移动速度基本一致mXOffsetSpeedOne = DensityUtil.dip2px(context, TRANSLATE_X_SPEED_ONE);mXOffsetSpeedTwo = DensityUtil.dip2px(context, TRANSLATE_X_SPEED_TWO);// 初始绘制波纹的画笔mWavePaint = new Paint();// 去除画笔锯齿mWavePaint.setAntiAlias(true);// 设置风格为实线mWavePaint.setStyle(Paint.Style.FILL);// 设置画笔颜色mWavePaint.setColor(WAVE_PAINT_COLOR);mDrawFilter = new PaintFlagsDrawFilter(0, Paint.ANTI_ALIAS_FLAG | Paint.FILTER_BITMAP_FLAG);}
其中需要用到的密度转换工具类:
/*** @TODO<分辨率转换工具类>* @author 小嵩* @date 2016-8-3 09:20:46*/
public class DensityUtil {/*** 根据手机的分辨率从 dp 的单位 转成为 px(像素)*/public static int dip2px(Context context, float dpValue) {final float scale = context.getResources().getDisplayMetrics().density;return (int) (dpValue * scale + 0.5f);}/*** 根据手机的分辨率从 px(像素) 的单位 转成为 dp*/public static int px2dip(Context context, float pxValue) {final float scale = context.getResources().getDisplayMetrics().density;return (int) (pxValue / scale + 0.5f);}
}
2.覆盖onDraw方法和onSizeChanged方法
3.在onSizeChanged方法中,获取控件的宽高,根据宽高计算正弦波纹周期以及对应的Y值:
@Overrideprotected void onSizeChanged(int w, int h, int oldw, int oldh) {super.onSizeChanged(w, h, oldw, oldh);// 记录下控件设置的宽高mTotalWidth = w;mTotalHeight = h;// 一维数组, 用于保存原始波纹的y值mYPositions = new float[mTotalWidth];// 将Sin函数周期定为view总宽度, ω = 2π/TmCycleFactorW = (float) (2 * Math.PI / mTotalWidth);// 根据view总宽度得出所有对应的y值,即计算出正弦图形对应位置for (int i = 0; i < mTotalWidth; i++) {mYPositions[i] = (float) (STRETCH_FACTOR_A * Math.sin(mCycleFactorW * i) + OFFSET_Y);}}
4.在onDraw方法中绘制双波纹图形,绘制方法是:
public void drawLine(float startX, float startY, float stopX, float stopY, Paint paint),几个参数代表的含义应该都能看明白吧,分别是起点X、Y值,终点X、Y值,画笔对象。
其实就是在竖直方向一条一条直线地画,起点是从onSizeChanged中得到的正弦函数Y值的数组中得到的,而终点,就是控件底部的坐标。
画笔绘制过程如下图所示:
所以控件宽度的像素有多少,一个波纹图形需要调用多少次drawLine方法。
Step 4: 不断重绘双波纹图形,形成动态效果
在绘制完成整个静态图形的过程后,通过 postInvalidate()方法来通知系统更新UI ,其实就是相当于重新调用了onDraw方法,从而进行平移,实现动态的效果。相关代码如下。
网上的那些例子,绘制方案大都是每次平移都拷贝4次数组,造成很大的内存开销:
@Overrideprotected void onDraw(Canvas canvas) {super.onDraw(canvas);...//中间省略...resetPositonY();//拷贝两个波浪正弦函数值的数组(每调用一次onDraw,都需要4次数组的拷贝)
for (int i = 0; i < mTotalWidth; i++) {// 减500只是为了控制波纹绘制的y的在屏幕的位置(高度),大家可以改成一个变量,然后动态改变这个变量,从而形成波纹上升下降效果// 绘制第一条水波纹(竖直方向)canvas.drawLine(i, mTotalHeight - mResetOneYPositions[i] - 500, i, mTotalHeight, mWavePaint);// 绘制第二条水波纹(竖直方向)canvas.drawLine(i, mTotalHeight - mResetTwoYPositions[i] - 500, i, mTotalHeight, mWavePaint);}}
private void resetPositonY() {//Copy数组// mXOneOffset代表当前第一条水波纹要移动的距离int yOneInterval = mYPositions.length - mXOneOffset;// 使用System.arraycopy方式重新填充第一条波纹的数据System.arraycopy(mYPositions, mXOneOffset, mResetOneYPositions, 0, yOneInterval);System.arraycopy(mYPositions, 0, mResetOneYPositions, yOneInterval, mXOneOffset);int yTwoInterval = mYPositions.length - mXTwoOffset;System.arraycopy(mYPositions, mXTwoOffset, mResetTwoYPositions, 0,yTwoInterval);System.arraycopy(mYPositions, 0, mResetTwoYPositions, yTwoInterval, mXTwoOffset);}
以上方法不推荐使用,太耗费内存了,实际使用的效果会很卡,所以这里我整理了一下,综合别人的处理方案做了优化:
@Overrideprotected void onDraw(Canvas canvas) {super.onDraw(canvas);//从canvas层面去除绘制时的锯齿canvas.setDrawFilter(mDrawFilter);for(int i=0,j=0,k=0;i<mTotalWidth;i++){if(i+mXOneOffset<mTotalWidth){//第一条波纹图形绘制canvas.drawLine(i,mTotalHeight-mYPositions[mXOneOffset+i]-WaveHeight,i,mTotalHeight,mWavePaint);}else {//大于周期值,则设置为j(与相位相关,已移动的X距离,最大值为一个周期,即控件的宽度)canvas.drawLine(i,mTotalHeight-mYPositions[j]-WaveHeight,i,mTotalHeight,mWavePaint);j++;}if(i+mXTwoOffset<mTotalWidth){//第二条波纹图形绘制canvas.drawLine(i,mTotalHeight-mYPositions[mXTwoOffset+i]-WaveHeight,i,mTotalHeight,mWavePaint);}else {//大于周期值,则设置为k(与相位相关,已移动的X距离)canvas.drawLine(i,mTotalHeight-mYPositions[k]-WaveHeight,i,mTotalHeight,mWavePaint);k++;}}// 改变两条波纹的移动点mXOneOffset += mXOffsetSpeedOne;mXTwoOffset += mXOffsetSpeedTwo;// 如果已经移动到结尾处,则重头记录if (mXOneOffset >= mTotalWidth) {mXOneOffset = 0;}if (mXTwoOffset > mTotalWidth) {mXTwoOffset = 0;}// 引发view重绘,可以考虑延迟10-30ms重绘,空出时间绘制if (isAnim){new Thread(mRunnable).start();}}private Runnable mRunnable = new Runnable() {@Overridepublic void run() {{try {//界面更新的频率,每20ms更新一次界面Thread.sleep(20);//通知系统更新界面,相当于调用了onDraw函数postInvalidate();} catch (InterruptedException e) {e.printStackTrace();}}}};
另外我还完善了动画效果的启动和暂停设置,有兴趣可下载sample demo查看完整代码 。
GitHub 地址 :DoubleWave 双波纹自定义View
Android双波浪自定义控件(DoubleWaveView)相关推荐
- android自定义波浪图,Android自定义控件--波浪图控件
今天给大家分享一个android的波浪图控件制作.具体效果如下图所示: 上次有个app使用了这个控件,感觉特别酷炫.今天讲解一下这个控件的思路分析与代码编写. 思路分析: 1.绘制波浪图 2.移动波浪 ...
- android 海浪动画,android自定义波浪加载动画的实现代码
本文实例为大家分享了android自定义波浪加载动画的具体代码,供大家参考,具体内容如下 效果图 1.自定义控件 WaveView package com.example.wh.myapplicati ...
- linux系统 安卓系统安装教程,最简单的Ubuntu Touch Android 双系统安装方式
Ubuntu Touch 和Android 双启动的官方工具来了,Canonical 发布了一个可以让Nexus 设备实现在Ubuntu Touch 和Android 之间进行双启动的工具.发放这个的 ...
- 基于容器原理(docker、lxc、cells)的Android 双系统设计概要
写在前面 最近一两年预研加开发android双系统:中途用过不少开源代码或者研读过大牛BLOG,现开放双系统设计原理来回报社区. 备注:我是在android6.0上实现的. 这个项目的原型来自于,哥伦 ...
- Android开发技巧——自定义控件之自定义属性
Android开发技巧--自定义控件之自定义属性 掌握自定义控件是很重要的,因为通过自定义控件,能够:解决UI问题,优化布局性能,简化布局代码. 上一篇讲了如何通过xml把几个控件组织起来,并继承某个 ...
- html5双波浪线怎么添加,在WPS中如何给段落添加双波浪线边框
WPS给文章的段落添加一个很好的边框,使整个段落看起来大方有条理.推荐一种波浪边框,这种边框添加后效果感是非常不错的,看起来就像是我们平时生活中写的明信片一样.以下是学习啦小编为您带来的关于在WPS中 ...
- 安装windows和android双系统,安装Windows和Android双系统.doc
安装Windows和Android双系统 轻松搞定Windows和Android双系统 前言:本文介绍在windows操作系统的基础上安装Android,并且正常引导双系统启动.采用先添加启动项,后安 ...
- java解析word 波浪线,word页面边框双波浪线
word如何在文本最后添加边框线或者双波浪线~ 可以教你一个简单易懂的:1.在你文本外面画一个表格,word画表格:表格--绘制表格2.你可以在工具栏上找到边框颜色,线形,磅数等,如果你需要曲线就选择 ...
- 机甲Android on ios,全球首款iOS+Android双系统硬件机甲震撼上市
原标题:全球首款iOS+Android双系统硬件机甲震撼上市 2016年7月20日,专为苹果打造的高端智能外设--机甲在北京震撼发布.这款全球首创的iOS/Android秒级切换双系统一举颠覆iOS和 ...
最新文章
- api接口怎么对接_系统对接项目管理方面怎么做?从一次项目接口对接说起
- python继承属性_python继承,属性查找顺序
- 《编写可维护的Javascript》学习总结
- win 2008 R2 域服务器策略同步异常解决方案。
- python 获取内存使用率_获取一个python实例的总内存和cpu使用率
- mongodb数据库命令操作(转)
- 基于小米球(Ngrok)实现内网穿透
- 【雷达】一维和二维自适应波束形成(DBF))DBF附matlab代码
- 【澳大利亚英语】我的英语笔记。。。
- golang中的reflect(反射)
- 20155201李卓雯 20155212江振思 20155313杨瀚《信息安全技术》 实验三 数字证书应用...
- 戴尔Inspiron 灵越 15 5000(5580)BIOS设置U盘启动
- Bit Twiddling Hacks
- 单片机C51复习题(课后习题及答案)
- Django项目实践(商城):十五、商品列表页面
- xp系统计算机蓝屏,xp电脑开机蓝屏代码0×0000000A怎么办
- 微信H5配置测试的appId和secret
- 抓取抖音数据(fiddler+uiAutomator2+java)
- React--key值详解
- 关于开源授权协议 GPL 和 LGPL
热门文章
- 基于数据分析的“用户群组+推送”,提升用户粘性
- ios 渐变透明背景_骚气渐变色的海报设计怎么做?
- CentOS Redis安装报错:“You need tcl 8.5 or newer in order to run the Redis test”问题解决
- 银联的bankall_Visa禁止银行利用中国银联(China UnionPay Co.)境外交易
- [***Model mj_objectArrayWithKeyValuesArray:]: unrecognized selector sent to class 0x10ace5df0
- 大数据周周看:百分点集团全资并购极速洞察,精准医疗创企“海普洛斯”获2.1亿元融资
- python中面向对象的缺点_python中的面向对象和面向过程
- 微库为8亿会员的微信“偷偷”干了什么?
- 陕汽汉德、一汽集团信息化调研报告
- Cocos Creator 新手引导制作