收藏 分销(赏)

自定义view实现水波纹效果.doc

上传人:仙人****88 文档编号:11895916 上传时间:2025-08-19 格式:DOC 页数:19 大小:337KB 下载积分:10 金币
下载 相关 举报
自定义view实现水波纹效果.doc_第1页
第1页 / 共19页
自定义view实现水波纹效果.doc_第2页
第2页 / 共19页


点击查看更多>>
资源描述
自定义view实现水波纹效果 我先们来学习效果1: 效果1实现本质:用一张波形图和一个圆形图的图片,然后圆形图在波形图上方,然后使用安卓的图片遮罩模式desIn(不懂?那么先记住有这样一个遮罩模式).(只显示上部图像和下部图像公共部分的下半部分),是不是很难懂?那么我在说清一点并且配图.假设圆形图在波形图上面,那么只会显示两者相交部分的波形图 下面是解释效果图(正方形蓝色图片在黄色圆形上面): 这次的实现我们都选择继承view,在实现的过程中我们需要关注如下几个方法: 1.onMeasure():最先回调,用于控件的测量; 2.onSizeChanged():在onMeasure后面回调,可以拿到view的宽高等数据,在横竖屏切换时也会回调; 3.onDraw():真正的绘制部分,绘制的代码都写到这里面; 先来看看我们定义的变量: //波形图 Bitmap waveBitmap; //圆形遮罩图 Bitmap circleBitmap; //波形图src Rect waveSrcRect; //波形图dst Rect waveDstRect; //圆形遮罩src Rect circleSrcRect; //圆形遮罩dst Rect circleDstRect; //画笔 Paint mpaint; //图片遮罩模式 PorterDuffXfermode mode; //控件的宽 int viewWidth; //控件的高 int viewHeight; //图片过滤器 PaintFlagsDrawFilter paintFlagsDrawFilter ; //每次移动的距离 int speek = 10 ; //当前移动距离 int nowOffSet; 介绍一个方法: void android.graphics.Canvas.drawBitmap(Bitmap bitmap, Rect src, Rect dst, Paint paint) 1 1 此方法的参数: 参数1:你的图片 参数2:矩形 .也就是说此矩形决定你画出图片参数1 的哪个位置,比如说你的矩形是设定是Rect rect= new Rect(0,0,图片宽,图片高) 那么将会画出图片全部 参数3:矩形.决定你图片缩放比例和在view中的位置.假设你的矩形Rect rect= new Rect(0,0,100,100) 那么你将在自定义view中(0,0)点到(100,100)绘画此图片并且如果图片大于(小于)此矩形那么按比例缩小(放大) 来看看 初始化方法 //初始化 private void init() { mpaint = new Paint(); //处理图片抖动 mpaint.setDither(true); //抗锯齿 mpaint.setAntiAlias(true); //设置图片过滤波 mpaint.setFilterBitmap(true); //设置图片遮罩模式 mode = new PorterDuffXfermode(PorterDuff.Mode.DST_IN); //给画布直接设定参数 paintFlagsDrawFilter = new PaintFlagsDrawFilter(0, Paint.DITHER_FLAG|Paint.ANTI_ALIAS_FLAG|Paint.FILTER_BITMAP_FLAG); //初始化图片 //使用drawable获取的方式,全局只会生成一份,并且系统会进行管理, //而BitmapFactory.decode()出来的则decode多少次生成多少张,务必自己进行recycle; //获取波形图 waveBitmap = ((BitmapDrawable)getResources().getDrawable(R.drawable.wave_2000)).getBitmap(); //获取圆形遮罩图 circleBitmap = ((BitmapDrawable)getResources().getDrawable(R.drawable.circle_500)).getBitmap(); //不断刷新波形图距离 读者可以先不看这部分内容 因为需要结合其他方法 new Thread(){ public void run() { while (true) { try { //移动波形图 nowOffSet=nowOffSet+speek; //如果移动波形图的末尾那么重新来 if (nowOffSet>=waveBitmap.getWidth()) { nowOffSet=0; } sleep(30); postInvalidate(); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } }; }.start(); } 以下获取view的宽高并设置对应的波形图和圆形图矩形(会在onMesure回调后执行) @Override protected void onSizeChanged(int w, int h, int oldw, int oldh) { super.onSizeChanged(w, h, oldw, oldh); //获取view宽高 viewWidth = w; viewHeight = h ; //波形图的矩阵初始化 waveSrcRect = new Rect(); waveDstRect = new Rect(0,0,w,h); //圆球矩阵初始化 circleSrcRect = new Rect(0,0,circleBitmap.getWidth(),circleBitmap.getHeight()); circleDstRect = new Rect(0,0,viewWidth,viewHeight); } 那么最后来看看绘画部分吧 @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); //给图片直接设置过滤效果 canvas.setDrawFilter(paintFlagsDrawFilter); //给图片上色 canvas.drawColor(Color.TRANSPARENT); //添加图层 注意!!!!!使用图片遮罩模式会影响全部此图层(也就是说在canvas.restoreToCount 所有图都会受到影响) int saveLayer = canvas.saveLayer(0,0, viewWidth,viewHeight,null, Canvas.ALL_SAVE_FLAG); //画波形图部分 矩形 waveSrcRect.set(nowOffSet, 0, nowOffSet+viewWidth/2, viewHeight); //画矩形 canvas.drawBitmap(waveBitmap,waveSrcRect,waveDstRect,mpaint); //设置图片遮罩模式 mpaint.setXfermode(mode); //画遮罩 canvas.drawBitmap(circleBitmap, circleSrcRect, circleDstRect,mpaint); //还原画笔模式 mpaint.setXfermode(null); //将图层放上 canvas.restoreToCount(saveLayer); } 最后看下完整的代码 package com.fmy.shuibo1; import android.content.Context; import android.graphics.Bitmap; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; import android.graphics.PaintFlagsDrawFilter; import android.graphics.PorterDuff; import android.graphics.PorterDuffXfermode; import android.graphics.Rect; import android.graphics.drawable.BitmapDrawable; import android.icu.text.TimeZoneFormat.ParseOption; import android.util.AttributeSet; import android.view.View; public class MySinUi extends View{ //波形图 Bitmap waveBitmap; //圆形遮罩图 Bitmap circleBitmap; //波形图src Rect waveSrcRect; //波形图dst Rect waveDstRect; //圆形遮罩src Rect circleSrcRect; //圆形遮罩dst Rect circleDstRect; //画笔 Paint mpaint; //图片遮罩模式 PorterDuffXfermode mode; //控件的宽 int viewWidth; //控件的高 int viewHeight; //图片过滤器 PaintFlagsDrawFilter paintFlagsDrawFilter ; //每次移动的距离 int speek = 10 ; //当前移动距离 int nowOffSet; public MySinUi(Context context, AttributeSet attrs) { super(context, attrs); init(); } //初始化 private void init() { mpaint = new Paint(); //处理图片抖动 mpaint.setDither(true); //抗锯齿 mpaint.setAntiAlias(true); //设置图片过滤波 mpaint.setFilterBitmap(true); //设置图片遮罩模式 mode = new PorterDuffXfermode(PorterDuff.Mode.DST_IN); //给画布直接设定参数 paintFlagsDrawFilter = new PaintFlagsDrawFilter(0, Paint.DITHER_FLAG|Paint.ANTI_ALIAS_FLAG|Paint.FILTER_BITMAP_FLAG); //初始化图片 //使用drawable获取的方式,全局只会生成一份,并且系统会进行管理, //而BitmapFactory.decode()出来的则decode多少次生成多少张,务必自己进行recycle; //获取波形图 waveBitmap = ((BitmapDrawable)getResources().getDrawable(R.drawable.wave_2000)).getBitmap(); //获取圆形遮罩图 circleBitmap = ((BitmapDrawable)getResources().getDrawable(R.drawable.circle_500)).getBitmap(); //不断刷新波形图距离 读者可以先不看这部分内容 因为需要结合其他方法 new Thread(){ public void run() { while (true) { try { //移动波形图 nowOffSet=nowOffSet+speek; //如果移动波形图的末尾那么重新来 if (nowOffSet>=waveBitmap.getWidth()) { nowOffSet=0; } sleep(30); postInvalidate(); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } }; }.start(); } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); //给图片直接设置过滤效果 canvas.setDrawFilter(paintFlagsDrawFilter); //给图片上色 canvas.drawColor(Color.TRANSPARENT); //添加图层 注意!!!!!使用图片遮罩模式会影响全部此图层(也就是说在canvas.restoreToCount 所有图都会受到影响) int saveLayer = canvas.saveLayer(0,0, viewWidth,viewHeight,null, Canvas.ALL_SAVE_FLAG); //画波形图部分 矩形 waveSrcRect.set(nowOffSet, 0, nowOffSet+viewWidth/2, viewHeight); //画矩形 canvas.drawBitmap(waveBitmap,waveSrcRect,waveDstRect,mpaint); //设置图片遮罩模式 mpaint.setXfermode(mode); //画遮罩 canvas.drawBitmap(circleBitmap, circleSrcRect, circleDstRect,mpaint); //还原画笔模式 mpaint.setXfermode(null); //将图层放上 canvas.restoreToCount(saveLayer); } @Override protected void onSizeChanged(int w, int h, int oldw, int oldh) { super.onSizeCged(w, h, oldw, oldh); //获取view宽高 viewWidth = w; viewHeight = h ; //波形图的矩阵初始化 waveSrcRect = new Rect(); waveDstRect = new Rect(0,0,w,h); //圆球矩阵初始化 circleSrcRect = new Rect(0,0,circleBitmap.getWidth(),circleBitmap.getHeight()); circleDstRect = new Rect(0,0,viewWidth,viewHeight); } } 学习效果2: 此方法实现原理:运用三角函数画出两个不同速率正弦函数图 我们先来复习三角函数吧 正余弦函数方程为: y = Asin(wx+b)+h ,这个公式里:w影响周期,A影响振幅,h影响y位置,b为初相; w:周期就是一个完整正弦曲线图此数值越大sin的周期越小 (cos越大) 如下图: A:振幅两个山峰最大的高度.如果A越大两个山峰越高和越低 h:你正弦曲线和y轴相交点.(影响正弦图初始高度的位置) b:初相会让你图片向x轴平移 具体大家可以百度学习,我们在学编程,不是数学 为什么要两个正弦图画?好看….. 先来看看变量: // 波纹颜色 private static final int WAVE_PAINT_COLOR = 0x880000aa; // 第一个波纹移动的速度 private int oneSeep = 7; // 第二个波纹移动的速度 private int twoSeep = 10; // 第一个波纹移动速度的像素值 private int oneSeepPxil; // 第二个波纹移动速度的像素值 private int twoSeepPxil; // 存放原始波纹的每个y坐标点 private float wave[]; // 存放第一个波纹的每一个y坐标点 private float oneWave[]; // 存放第二个波纹的每一个y坐标点 private float twoWave[]; // 第一个波纹当前移动的距离 private int oneNowOffSet; // 第二个波纹当前移动的 private int twoNowOffSet; // 振幅高度 private int amplitude = 20; // 画笔 private Paint mPaint; // 创建画布过滤 private DrawFilter mDrawFilter; // view的宽度 private int viewWidth; // view高度 private int viewHeight; 画初始的波形图并且保存到数组中 // 大小改变 @Override protected void onSizeChanged(int w, int h, int oldw, int oldh) { super.onSizeChanged(w, h, oldw, oldh); // 获取view的宽高 viewHeight = h; viewWidth = w; // 初始化保存波形图的数组 wave = new float[w]; oneWave = new float[w]; twoWave = new float[w]; // 设置波形图周期 float zq = (float) (Math.PI * 2 / w); // 设置波形图的周期 for (int i = 0; i < viewWidth; i++) { wave[i] = (float) (amplitude * Math.sin(zq * i)); } } 初始化各种 // 初始化 private void init() { // 创建画笔 mPaint = new Paint(); // 设置画笔颜色 mPaint.setColor(WAVE_PAINT_COLOR); // 设置绘画风格为实线 mPaint.setStyle(Style.FILL); // 抗锯齿 mPaint.setAntiAlias(true); // 设置图片过滤波和抗锯齿 mDrawFilter = new PaintFlagsDrawFilter(0, Paint.ANTI_ALIAS_FLAG | Paint.FILTER_BITMAP_FLAG); // 第一个波的像素移动值 换算成手机像素值让其在各个手机移动速度差不多 oneSeepPxil = dpChangPx(oneSeep); // 第二个波的像素移动值 twoSeepPxil = dpChangPx(twoSeep); } // 绘画方法 @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); canvas.setDrawFilter(mDrawFilter); oneNowOffSet =oneNowOffSet+oneSeepPxil; twoNowOffSet = twoNowOffSet+twoSeepPxil; if (oneNowOffSet>=viewWidth) { oneNowOffSet = 0; } if (twoNowOffSet>=viewWidth) { twoNowOffSet = 0; } //此方法会让两个保存波形图的 数组更新 头到NowOffSet变成尾部,尾部的变成头部实现动态移动 reSet(); Log.e("fmy", Arrays.toString(twoWave)); for (int i = 0; i < viewWidth; i++) { canvas.drawLine(i, viewHeight, i, viewHeight-400-oneWave[i], mPaint); canvas.drawLine(i, viewHeight, i, viewHeight-400-twoWave[i], mPaint); } postInvalidate(); } 来看看能让两个数组重置的 public void reSet() { // one是指 走到此处的波纹的位置 (这个理解方法看个人了) int one = viewWidth - oneNowOffSet; // 把未走过的波纹放到最前面 进行重新拼接 System.arraycopy(wave, oneNowOffSet, oneWave, 0, one); // 把已走波纹放到最后 System.arraycopy(wave, 0, oneWave, one, oneNowOffSet); // one是指 走到此处的波纹的位置 (这个理解方法看个人了) int two = viewWidth - twoNowOffSet; // 把未走过的波纹放到最前面 进行重新拼接 System.arraycopy(wave, twoNowOffSet, twoWave, 0, two); // 把已走波纹放到最后 System.arraycopy(wave, 0, twoWave, two, twoNowOffSet); } 最后大家看下完整代码 package com.exam1ple.myshuibo2; import java.util.Arrays; import android.content.Context; import android.graphics.Bitmap; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.DrawFilter; import android.graphics.Paint; import android.graphics.Paint.Style; import android.graphics.PaintFlagsDrawFilter; import android.graphics.PorterDuff; import android.graphics.PorterDuffXfermode; import android.graphics.Rect; import android.graphics.drawable.BitmapDrawable; import android.icu.text.TimeZoneFormat.ParseOption; import android.util.AttributeSet; import android.util.DisplayMetrics; import android.util.Log; import android.view.View; import android.view.WindowManager; public class MyUi2 extends View { // 波纹颜色 private static final int WAVE_PAINT_COLOR = 0x880000aa; // 第一个波纹移动的速度 private int oneSeep = 7; // 第二个波纹移动的速度 private int twoSeep = 10; // 第一个波纹移动速度的像素值 private int oneSeepPxil; // 第二个波纹移动速度的像素值 private int twoSeepPxil; // 存放原始波纹的每个y坐标点 private float wave[]; // 存放第一个波纹的每一个y坐标点 private float oneWave[]; // 存放第二个波纹的每一个y坐标点 private float twoWave[]; // 第一个波纹当前移动的距离 private int oneNowOffSet; // 第二个波纹当前移动的 private int twoNowOffSet; // 振幅高度 private int amplitude = 20; // 画笔 private Paint mPaint; // 创建画布过滤 private DrawFilter mDrawFilter; // view的宽度 private int viewWidth; // view高度 private int viewHeight; // xml布局构造方法 public MyUi2(Context context, AttributeSet attrs) { super(context, attrs); init(); } // 初始化 private void init() { // 创建画笔 mPaint = new Paint(); // 设置画笔颜色 mPaint.setColor(WAVE_PAINT_COLOR); // 设置绘画风格为实线 mPaint.setStyle(Style.FILL); // 抗锯齿 mPaint.setAntiAlias(true); // 设置图片过滤波和抗锯齿 mDrawFilter = new PaintFlagsDrawFilter(0, Paint.ANTI_ALIAS_FLAG | Paint.FILTER_BITMAP_FLAG); // 第一个波的像素移动值 换算成手机像素值让其在各个手机移动速度差不多 oneSeepPxil = dpChangPx(oneSeep); // 第二个波的像素移动值 twoSeepPxil = dpChangPx(twoSeep); } // 绘画方法 @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); canvas.setDrawFilter(mDrawFilter); oneNowOffSet =oneNowOffSet+oneSeepPxil; twoNowOffSet = twoNowOffSet+twoSeepPxil; if (oneNowOffSet>=viewWidth) { oneNowOffSet = 0; } if (twoNowOffSet>=viewWidth) { twoNowOffSet = 0; } reSet(); Log.e("fmy", Arrays.toString(twoWave)); for (int i = 0; i < viewWidth; i++) { canvas.drawLine(i, viewHeight, i, viewHeight-400-oneWave[i], mPaint);
展开阅读全文

开通  VIP会员、SVIP会员  优惠大
下载10份以上建议开通VIP会员
下载20份以上建议开通SVIP会员


开通VIP      成为共赢上传

当前位置:首页 > 包罗万象 > 大杂烩

移动网页_全站_页脚广告1

关于我们      便捷服务       自信AI       AI导航        抽奖活动

©2010-2026 宁波自信网络信息技术有限公司  版权所有

客服电话:0574-28810668  投诉电话:18658249818

gongan.png浙公网安备33021202000488号   

icp.png浙ICP备2021020529号-1  |  浙B2-20240490  

关注我们 :微信公众号    抖音    微博    LOFTER 

客服