杭州的设计网站建设,自己做家具网站,推广项目网站,有没有一种网站做拍卖厂的效果图#xff1a;网上垂直的水波纹进度条很多#xff0c;但横向的很少#xff0c;将垂直的水波纹改为水平的还遇到了些麻烦#xff0c;现在完善后发布出来#xff0c;希望遇到的人少躺点坑。思路分析整体效果可分为三个#xff0c;绘制圆角背景和圆角矩形#xff0c;绘…效果图网上垂直的水波纹进度条很多但横向的很少将垂直的水波纹改为水平的还遇到了些麻烦现在完善后发布出来希望遇到的人少躺点坑。思路分析整体效果可分为三个绘制圆角背景和圆角矩形绘制第一条和第二条水波浪根据自定义进度变化效果。功能实现1、绘制圆角背景和圆角矩形边框圆角矩形边框private RectF rectBorder;
if (rectBorder null) {rectBorder new RectF(0.5f * dp1, 0.5f * dp1, waveActualSizeWidth - 0.5f * dp1, waveActualSizeHeight - 0.5f * dp1);
}
canvas.drawRoundRect(rectBorder, dp27, dp27, borderPaint);我们创建一个新的画布然后在画布里画上圆角矩形背景和第一条和第二条水波浪//这里用到了缓存 根据参数创建新位图
if (circleBitmap null) {circleBitmap Bitmap.createBitmap(waveActualSizeWidth, waveActualSizeHeight, Bitmap.Config.ARGB_8888);
}
//以该bitmap为底创建一块画布
if (bitmapCanvas null) {bitmapCanvas new Canvas(circleBitmap);
}
// 圆角矩形背景为了能让波浪填充完整个圆形背景
if (rectBg null) {rectBg new RectF(0, 0, waveActualSizeWidth, waveActualSizeHeight);
}
bitmapCanvas.drawRoundRect(rectBg, dp27, dp27, backgroundPaint);
//裁剪图片
canvas.drawBitmap(circleBitmap, 0, 0, null);2、通过贝塞尔曲线实现双水波1实现第一条水波/*** 绘制波浪线*/
private Path canvasWavePath() {//要先清掉路线wavePath.reset();//起始点移至(0,0) p0 -p1 的高度随着进度的变化而变化wavePath.moveTo((currentPercent) * waveActualSizeWidth, -moveDistance);//最多能绘制多少个波浪//其实也可以用 i getWidth() ;iwaveLength来判断 这个没那么完美//绘制p0 - p1 绘制波浪线 这里有一段是超出View的在View右边距的右边 所以是* 2for (int i 0; i waveNumber * 2; i) {wavePath.rQuadTo(waveHeight, waveLength / 2, 0, waveLength);wavePath.rQuadTo(-waveHeight, waveLength / 2, 0, waveLength);}//连接p1 - p2wavePath.lineTo(0, waveActualSizeHeight);//连接p2 - p0wavePath.lineTo(0, 0);//封闭起来填充wavePath.close();return wavePath;
}moveDistance为水波垂直方向移动的距离。waveLength为水波长度一个上弧加一个下弧为一个波长。path的起始点为(0,0)可根据进度动态改变然后循环画曲线长度是有几个波浪就是多长然后连接到view高度的位置最后到(00)形成一个封闭的区域这样就实现了一个填充的水波效果。2绘制第二条水波第二条水波和第一条类似只是起始点变了/*** 绘制第二层波浪*/
private Path canvasSecondPath() {secondWavePath.reset();//初始点移动到下方secondWavePath.moveTo((currentPercent) * waveActualSizeWidth, waveActualSizeHeight moveDistance);for (int i 0; i waveNumber * 2; i) {secondWavePath.rQuadTo(waveHeight, -waveLength / 2, 0, -waveLength);secondWavePath.rQuadTo(-waveHeight, -waveLength / 2, 0, -waveLength);}secondWavePath.lineTo(0, 0);secondWavePath.lineTo(0, waveActualSizeHeight);secondWavePath.close();return secondWavePath;
}3、设置动画使进度和水波纹变化/*** 设置进度** param currentProgress 进度* param duration 达到进度需要的时间*/
public void setProgress(int currentProgress, long duration, AnimatorListenerAdapter listenerAdapter) {float percent currentProgress * 1f / maxProgress;this.currentProgress currentProgress;//从0开始变化currentPercent 0;moveDistance 0;mProgressAnimator ValueAnimator.ofFloat(0, percent);//设置动画时间mProgressAnimator.setDuration(duration);//让动画匀速播放避免出现波浪平移停顿的现象mProgressAnimator.setInterpolator(new LinearInterpolator());mProgressAnimator.addUpdateListener(listener);mProgressAnimator.addListener(listenerAdapter);mProgressAnimator.start();// 波浪线startWaveAnimal();
}/*** 波浪动画*/
private void startWaveAnimal() {//动画实例化if (waveProgressAnimator null) {waveProgressAnimator new WaveProgressAnimal();//设置动画时间waveProgressAnimator.setDuration(2000);//设置循环播放waveProgressAnimator.setRepeatCount(Animation.INFINITE);//让动画匀速播放避免出现波浪平移停顿的现象waveProgressAnimator.setInterpolator(new LinearInterpolator());//当前视图开启动画this.startAnimation(waveProgressAnimator);}
}其中波浪动画是通过改变moveDistance的值改变纵坐标达到进度主要是通过改变百分比currentPercent改变波浪的横坐标达到。完整源码/*** 横向双水波浪进度条** author jingbin**/
public class HorizontalWaveProgressView extends View {//绘制波浪画笔private Paint wavePaint;//绘制波浪Pathprivate Path wavePath;//波浪的宽度private final float waveLength;//波浪的高度private final float waveHeight;//波浪组的数量 一个波浪是一低一高private int waveNumber;//自定义View的波浪宽高private int waveDefaultWidth;private int waveDefaultHeight;//测量后的View实际宽高private int waveActualSizeWidth;private int waveActualSizeHeight;//当前进度值占总进度值的占比private float currentPercent;//当前进度值private int currentProgress;//进度的最大值private int maxProgress;//动画对象private WaveProgressAnimal waveProgressAnimator;private ValueAnimator mProgressAnimator;private ValueAnimator mEndAnimator;//波浪平移距离private float moveDistance 0;//圆形背景画笔private Paint backgroundPaint;// 边框private Paint borderPaint;//bitmapprivate Bitmap circleBitmap;//bitmap画布private Canvas bitmapCanvas;//波浪颜色private final int wave_color;//圆形背景进度框颜色private final int backgroundColor;//进度条显示值监听接口private UpdateTextListener updateTextListener;//是否绘制双波浪线private boolean isShowSecondWave;//第二层波浪的颜色private final int secondWaveColor;//边框色private final int borderColor;//第二层波浪的画笔private Paint secondWavePaint;private Path secondWavePath;private int dp1;// 圆角角度private int dp27;public HorizontalWaveProgressView(Context context) {this(context, null);}public HorizontalWaveProgressView(Context context, Nullable AttributeSet attrs) {this(context, attrs, 0);}public HorizontalWaveProgressView(Context context, Nullable AttributeSet attrs, int defStyleAttr) {super(context, attrs, defStyleAttr);//获取attrs文件下配置属性TypedArray typedArray context.obtainStyledAttributes(attrs, R.styleable.HorizontalWaveProgressView);//获取波浪宽度 第二个参数如果xml设置这个属性则会取设置的默认值 也就是说xml没有指定wave_length这个属性就会取Density.dip2px(context,25)waveLength typedArray.getDimension(R.styleable.HorizontalWaveProgressView_wave_length, DensityUtil.dip2px(context, 25));//获取波浪高度waveHeight typedArray.getDimension(R.styleable.HorizontalWaveProgressView_wave_height, DensityUtil.dip2px(context, 5));//获取波浪颜色wave_color typedArray.getColor(R.styleable.HorizontalWaveProgressView_wave_color, Color.parseColor(#B76EFF));//圆形背景颜色backgroundColor typedArray.getColor(R.styleable.HorizontalWaveProgressView_wave_background_color, Color.WHITE);//当前进度currentProgress typedArray.getInteger(R.styleable.HorizontalWaveProgressView_currentProgress, 0);//最大进度maxProgress typedArray.getInteger(R.styleable.HorizontalWaveProgressView_maxProgress, 100);//是否显示第二层波浪isShowSecondWave typedArray.getBoolean(R.styleable.HorizontalWaveProgressView_second_show, false);//第二层波浪的颜色secondWaveColor typedArray.getColor(R.styleable.HorizontalWaveProgressView_second_color, Color.parseColor(#DEBCFF));//边框色borderColor typedArray.getColor(R.styleable.HorizontalWaveProgressView_border_color, Color.parseColor(#DEBCFF));//记得把TypedArray回收//程序在运行时维护了一个 TypedArray的池程序调用时会向该池中请求一个实例用完之后调用 recycle() 方法来释放该实例从而使其可被其他模块复用。//那为什么要使用这种模式呢答案也很简单TypedArray的使用场景之一就是上述的自定义View会随着 Activity的每一次Create而Create//因此需要系统频繁的创建array对内存和性能是一个不小的开销如果不使用池模式每次都让GC来回收很可能就会造成OutOfMemory。//这就是使用池单例模式的原因这也就是为什么官方文档一再的强调使用完之后一定 recycle,recycle,recycletypedArray.recycle();init(context);}/*** 初始化一些画笔路径配置*/private void init(Context context) {//设置自定义View的宽高waveDefaultWidth DensityUtil.dip2px(context, 152);waveDefaultHeight DensityUtil.dip2px(context, 40);dp1 DensityUtil.dip2px(getContext(), 1);dp27 DensityUtil.dip2px(getContext(), 27);wavePath new Path();wavePaint new Paint();//设置画笔为取交集模式wavePaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));//设置波浪颜色wavePaint.setColor(wave_color);//设置抗锯齿wavePaint.setAntiAlias(true);//矩形背景backgroundPaint new Paint();backgroundPaint.setColor(backgroundColor);backgroundPaint.setAntiAlias(true);//边框borderPaint new Paint(Paint.ANTI_ALIAS_FLAG);borderPaint.setColor(borderColor);borderPaint.setAntiAlias(true);borderPaint.setStrokeWidth(dp1);borderPaint.setStyle(Paint.Style.STROKE);if (isShowSecondWave) {//是否绘制双波浪线secondWavePath new Path();//初始化第二层波浪画笔secondWavePaint new Paint();secondWavePaint.setColor(secondWaveColor);secondWavePaint.setAntiAlias(true);//因为要覆盖在第一层波浪上且要让半透明生效所以选SRC_ATOP模式secondWavePaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_ATOP));}//占比一开始设置为0currentPercent currentProgress * 1f / maxProgress;}Overrideprotected void onDraw(Canvas canvas) {super.onDraw(canvas);//这里用到了缓存 根据参数创建新位图circleBitmap Bitmap.createBitmap(waveActualSizeWidth, waveActualSizeHeight, Bitmap.Config.ARGB_8888);//以该bitmap为底创建一块画布bitmapCanvas new Canvas(circleBitmap);// 绘制背景为了能让波浪填充完整个圆形背景RectF rectBg new RectF(0, 0, waveActualSizeWidth, waveActualSizeHeight);bitmapCanvas.drawRoundRect(rectBg, dp27, dp27, backgroundPaint);if (isShowSecondWave) {//绘制第二层波浪bitmapCanvas.drawPath(canvasSecondPath(), secondWavePaint);}
//绘制波浪形bitmapCanvas.drawPath(canvasWavePath(), wavePaint);//裁剪图片canvas.drawBitmap(circleBitmap, 0, 0, null);// 绘制边框RectF rectBorder new RectF(0.5f * dp1, 0.5f * dp1, waveActualSizeWidth - 0.5f * dp1, waveActualSizeHeight - 0.5f * dp1);canvas.drawRoundRect(rectBorder, dp27, dp27, borderPaint);}/*** 绘制波浪线*/private Path canvasWavePath() {//要先清掉路线wavePath.reset();//起始点移至(0,0) p0 -p1 的高度随着进度的变化而变化wavePath.moveTo((currentPercent) * waveActualSizeWidth, -moveDistance);
// wavePath.moveTo(-moveDistance,(1-currentPercent) * waveActualSize);//最多能绘制多少个波浪//其实也可以用 i getWidth() ;iwaveLength来判断 这个没那么完美//绘制p0 - p1 绘制波浪线 这里有一段是超出View的在View右边距的右边 所以是* 2for (int i 0; i waveNumber * 2; i) {wavePath.rQuadTo(waveHeight, waveLength / 2, 0, waveLength);wavePath.rQuadTo(-waveHeight, waveLength / 2, 0, waveLength);}//连接p1 - p2wavePath.lineTo(waveActualSizeWidth, waveActualSizeHeight);//连接p2 - p3wavePath.lineTo(0, waveActualSizeHeight);//连接p3 - p0 p3-p0d的高度随着进度变化而变化wavePath.lineTo(0, 0);//封闭起来填充wavePath.close();return wavePath;}/*** 绘制第二层波浪方法*/private Path canvasSecondPath() {float secondWaveHeight waveHeight;secondWavePath.reset();//移动到右上方也就是p1点secondWavePath.moveTo((currentPercent) * waveActualSizeWidth, waveActualSizeHeight moveDistance);//p1 - p0for (int i 0; i waveNumber * 2; i) {secondWavePath.rQuadTo(secondWaveHeight, -waveLength / 2, 0, -waveLength);secondWavePath.rQuadTo(-secondWaveHeight, -waveLength / 2, 0, -waveLength);}//p3-p0的高度随着进度变化而变化secondWavePath.lineTo(0, 0);//连接p3 - p2secondWavePath.lineTo(0, waveActualSizeHeight);secondWavePath.lineTo(waveActualSizeHeight, waveActualSizeWidth);//连接p2 - p1secondWavePath.lineTo(waveActualSizeWidth, waveActualSizeHeight moveDistance);//封闭起来填充secondWavePath.close();return secondWavePath;}Overrideprotected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {super.onMeasure(widthMeasureSpec, heightMeasureSpec);int width measureSize(waveDefaultWidth, widthMeasureSpec);int height measureSize(waveDefaultHeight, heightMeasureSpec);//把View改为正方形setMeasuredDimension(width, height);//waveActualSize是实际的宽高waveActualSizeWidth width;waveActualSizeHeight height;//Math.ceil(a)返回求不小于a的最小整数// 举个例子:// Math.ceil(125.9)126.0// Math.ceil(0.4873)1.0// Math.ceil(-0.65)-0.0//这里是调整波浪数量 就是View中能容下几个波浪 用到ceil就是一定让View完全能被波浪占满 为循环绘制做准备 分母越小就约精准waveNumber (int) Math.ceil(Double.parseDouble(String.valueOf(waveActualSizeHeight / waveLength / 2)));}/*** 返回指定的值** param defaultSize 默认的值* param measureSpec 模式*/private int measureSize(int defaultSize, int measureSpec) {int result defaultSize;int specMode MeasureSpec.getMode(measureSpec);int specSize MeasureSpec.getSize(measureSpec);//View.MeasureSpec.EXACTLY如果是match_parent 或者设置定值就//View.MeasureSpec.AT_MOSTwrap_contentif (specMode MeasureSpec.EXACTLY) {result specSize;} else if (specMode MeasureSpec.AT_MOST) {result Math.min(result, specSize);}return result;}//新建一个动画类public class WaveProgressAnimal extends Animation {//在绘制动画的过程中会反复的调用applyTransformation函数// 每次调用参数interpolatedTime值都会变化该参数从0渐 变为1当该参数为1时表明动画结束Overrideprotected void applyTransformation(float interpolatedTime, Transformation t) {super.applyTransformation(interpolatedTime, t);//左边的距离moveDistance interpolatedTime * waveNumber * waveLength * 2;//重新绘制invalidate();}}/*** 直接结束** param duration 结束时间*/public void setProgressEnd(long duration, AnimatorListenerAdapter listenerAdapter) {// 如果是100会不满因为在波动if (currentProgress maxProgress) {// 到底了就从头开始currentPercent 0;}mEndAnimator ValueAnimator.ofFloat(currentPercent, 1.1f);mEndAnimator.setInterpolator(new DecelerateInterpolator());mEndAnimator.setDuration(duration);mEndAnimator.addUpdateListener(listener);mEndAnimator.addListener(listenerAdapter);mEndAnimator.start();// 波浪线startWaveAnimal();}/*** 设置进度** param currentProgress 进度* param duration 达到进度需要的时间*/public void setProgress(int currentProgress, long duration, AnimatorListenerAdapter listenerAdapter) {float percent currentProgress * 1f / maxProgress;this.currentProgress currentProgress;//从0开始变化currentPercent 0;moveDistance 0;mProgressAnimator ValueAnimator.ofFloat(0, percent);//设置动画时间mProgressAnimator.setDuration(duration);//让动画匀速播放避免出现波浪平移停顿的现象mProgressAnimator.setInterpolator(new LinearInterpolator());mProgressAnimator.addUpdateListener(listener);mProgressAnimator.addListener(listenerAdapter);mProgressAnimator.start();// 波浪线startWaveAnimal();}/*** 波浪动画*/private void startWaveAnimal() {//动画实例化if (waveProgressAnimator null) {waveProgressAnimator new WaveProgressAnimal();//设置动画时间waveProgressAnimator.setDuration(2000);//设置循环播放waveProgressAnimator.setRepeatCount(Animation.INFINITE);//让动画匀速播放避免出现波浪平移停顿的现象waveProgressAnimator.setInterpolator(new LinearInterpolator());//当前视图开启动画this.startAnimation(waveProgressAnimator);}}/*** 进度的监听*/ValueAnimator.AnimatorUpdateListener listener new ValueAnimator.AnimatorUpdateListener() {Overridepublic void onAnimationUpdate(ValueAnimator animation) {// 当前进度百分比[0,1]currentPercent (float) animation.getAnimatedValue();//这里直接根据进度值显示if (updateTextListener ! null) {updateTextListener.updateText(currentPercent, maxProgress);}}};public interface UpdateTextListener {/*** 提供接口 给外部修改数值样式 等** param currentPercent 当前进度百分比* param maxProgress 进度条的最大数值*/void updateText(float currentPercent, float maxProgress);}/*** 设置监听*/public void setUpdateTextListener(UpdateTextListener updateTextListener) {this.updateTextListener updateTextListener;}/*** 停止动画销毁对象*/public void stopAnimal() {if (waveProgressAnimator ! null) {waveProgressAnimator.cancel();}if (mProgressAnimator ! null mProgressAnimator.isStarted()) {mProgressAnimator.removeAllListeners();mProgressAnimator.cancel();}if (mEndAnimator ! null mEndAnimator.isStarted()) {mEndAnimator.removeAllListeners();mEndAnimator.cancel();}}
}