前言
嗯,然后大致就是这样,按住录音,然后有一个倒计时,最外层一个进度条,还有一个类似模拟声波的动画效果(其实中间的波浪会根据声音的大小浪起来的~)
实现思路
然后,我们适当的来分析一下这个录音动画的实现方式。这个肯定是通过自定义控件,咱们来把这个效果完完全全画出来。
大致包括以下几个点:
1. 最外层的进度条,最坑的就是一开始的一个渐变的效果
2. 然后进度条最前方是有一个点的(我肯定选择用图片来实现)
3. 中间的波浪(关键是要随着声音的大小浪起来)
4. 中间的倒计时
实现过程
1.画最外层的圆
/** * 画一个大圆(纯色) */mPaint.setStyle(Paint.Style.STROKE); mPaint.setStrokeWidth(dip2px(mContext, widthing)); mPaint.setColor(mContext.getResources().getColor(R.color.RoundColor)); RectF oval1 = new RectF( dip2px(mContext, pandding) , dip2px(mContext, pandding) , getWidth()-dip2px(mContext, pandding) , getHeight()-dip2px(mContext, pandding)); canvas.drawArc(oval1, progress, 360, false, mPaint); //绘制圆弧
2.画提示的文字
Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);paint.setTextSize(dip2px(mContext,textHintSize));paint.setColor(mContext.getResources().getColor(R.color.RoundHintTextColor));// 下面这行是实现水平居中,drawText对应改为传入targetRect.centerX() paint.setTextAlign(Paint.Align.CENTER);canvas.drawText("剩余录制时间", getWidth()/2, getHeight()/2+50, paint);
3.画倒计时(静止时间)
/** * 画时间 * */Paint paint2 = new Paint(Paint.ANTI_ALIAS_FLAG);paint2.setTextSize(dip2px(mContext,60));paint2.setColor(mContext.getResources().getColor(R.color.TimeTextColor));paint2.setTextAlign(Paint.Align.CENTER);canvas.drawText(countdownTime2+"", getWidth()/2, getHeight()/2-20, paint2);
4.画声波
if (lastTime == 0) { lastTime = System.currentTimeMillis(); translateX += 5; } else { if (System.currentTimeMillis() - lastTime > lineSpeed) { lastTime = System.currentTimeMillis(); translateX += 5; } else { return; } }if (volume < targetVolume && isSet) { volume += getHeight() / 30; } else { isSet = false; if (volume <= 10) { volume = 10; } else { if (volume < getHeight() / 30) { volume -= getHeight() / 60; } else { volume -= getHeight() / 30; } } }//我是分隔线-------------------------------mPaint.setColor(voiceLineColor); mPaint.setAntiAlias(true); mPaint.setStrokeWidth(2); canvas.save(); int moveY = getHeight()*3/4;for (int i = 0; i < paths.size(); i++) { paths.get(i).reset(); paths.get(i).moveTo(getWidth()*5/6, getHeight() *3/4); }for (float j = getWidth()*5/6 - 1; j >= getWidth()/6; j -= fineness) {float i = j-getWidth()/6;//这边必须保证起始点和终点的时候amplitude = 0;amplitude = 5 * volume *i / getWidth() - 5 * volume * i / getWidth() * i/getWidth()*6/4;for (int n = 1; n <= paths.size(); n++) { float sin = amplitude * (float) Math.sin((i - Math.pow(1.5, n)) * Math.PI / 180 - translateX+n*amplitude/(Math.PI*6)); paths.get(n - 1).lineTo(j, (2 * n * sin / paths.size() - 15 * sin / paths.size() + moveY)); } }for (int n = 0; n < paths.size(); n++) {if (n == paths.size() - 1) { mPaint.setAlpha(255); } else { mPaint.setAlpha(n * 130 / paths.size()); }if (mPaint.getAlpha() > 0) { canvas.drawPath(paths.get(n), mPaint); } } canvas.restore();
这边代码就不展开了,画的有点烦,简单说下,还需要自己体会哈。上面分隔线之前的说白了就是让声波动起来,也就是改变volume的值,然后后面有3个for循环。第一个for循环是为了确定声波水平线的位置,第二个是画声波,第三个是颜色的渐变。
5.画外圈进度的那个点
我们先会个图分析一下,如下图。A点就是起始坐标,一开始我们的小圆点是隐藏的,如果不算padding的话,x=witdh/2,y=0;
嗯,然后呢画图片我们用的是
canvas.drawBitmap(......);
那么要知道,drawBitmap()这个方法画的时候是我们图片的左上角去画到A点的,其实我们应该往左上角挪一点,才能让图片的中心真正意义上的和A点重合,对吧对吧,嗯,仔细思考一下。
然后继续看上面那个图,当我们A点随着时间运动到B点之后,我们要算出B点的坐标。这边用一下三角函数啦,我们设A到B,转过的角度为α,设圆的半径为r,那么A到B其实横向增加的距离应该就是
m = x+r*sin(α); n = y+r*cos(α);
那么我们该图片的所有代码就是:
/** * 画一个点(图片) * */if(r>0){ if(progress >360) return; double hu = Math.PI*Double.parseDouble(String.valueOf(progress))/180.0; Log.d(TAG,"hu: "+hu); double p = Math.sin(hu)*r; Log.d(TAG,"p: "+p); double q = Math.cos(hu)*r; Log.d(TAG,"q: "+q); float x = (float) ((getWidth()-progressDrawable.getIntrinsicWidth())/2f+p); Log.d(TAG,"x: "+x); float y = (float) ((dip2px(mContext,pandding)-progressDrawable.getIntrinsicHeight()/2f)+r-q); Log.d(TAG,"y: "+y); canvas.drawBitmap(((BitmapDrawable)progressDrawable).getBitmap(),x,y,mPaint); }
6.画外边的带进度和带渐变的大圆
我的实现方式很简单,从我们的UI图看出,外面的大圆在1/4进度的时候是渐变的,然后剩下的3/4圆其实都是一种颜色,对吧,那我就画2个圆来实现这个效果呗。当progress<90的时候,我们画那个渐变的圆环,当>90的时候,我们同时画出那个渐变的和纯色的圆环(当progress的时候,这个时候其实那个渐变的圆环没变化,只是纯色的圆环一直在变)。
/** * 这边画进度 */if(progress > 90){ mPaint.setColor(getResources().getColor(R.color.RoundFillColor)); mPaint.setStrokeWidth(dip2px(mContext, widthing)); RectF oval = new RectF( dip2px(mContext, pandding) , dip2px(mContext, pandding) , getWidth()-dip2px(mContext, pandding) , getHeight()-dip2px(mContext, pandding)); canvas.drawArc(oval, 0, progress-90, false, mPaint); //绘制圆弧 r = getHeight()/2f-dip2px(mContext,pandding); }/** * 画一个大圆(渐变) */mPaint.setStyle(Paint.Style.STROKE); canvas.rotate(-90, getWidth() / 2, getHeight() / 2); mPaint.setStrokeWidth(dip2px(mContext, widthing)); mPaint.setShader(new SweepGradient(getWidth() / 2, getHeight() / 2, doughnutColors, null)); RectF oval = new RectF( dip2px(mContext, pandding) , dip2px(mContext, pandding) , getWidth()-dip2px(mContext, pandding) , getHeight()-dip2px(mContext, pandding));//看这里,其实这里当progress大于90以后就一直只画0-90度的圆环canvas.drawArc(oval, 0, progress<90?progress:90, false, mPaint); //绘制圆弧canvas.rotate(90, getWidth() / 2, getHeight() / 2); mPaint.reset();
7.然后最后就剩下一个计时器了,还有那个上面一直出现的progress
private int countdownTime2 = 9;//倒计时时间,默认时间10秒.这是会变的...progress += 360.00/(countdownTime*950.00/5.00);
Handler mHandler = new Handler(){@Overridepublic void handleMessage(Message msg) { if(msg.what == 1){ countdownTime2--; if(countdownTime2 == 0){ listener.onCountDown(); canSetVolume = false; timeTask.cancel(); postInvalidate(); } }else if(msg.what == 2){ progress += 360.00/(countdownTime*950.00/5.00);// Log.d(TAG,"progress:"+progress); if(progress >360){ targetVolume = 1; postInvalidate(); progressTask.cancel(); }else postInvalidate(); } } };
8.最后就是提供各种接口,各种绘制和启动机制了,最主要的还是上面的绘制方法。
比如你的自定义属性
<?xml version="1.0" encoding="utf-8"?><resources> <declare-styleable name="recordView"> <attr name="hintText" format="string"/> <attr name="playHintText" format="string"/> <attr name="timeTextColor" format="reference"/> <attr name="hintTextSize" format="dimension"/> <attr name="middleLineHeight" format="dimension"/> <attr name="progressSrc" format="reference"/> <attr name="middleLineColor" format="reference"/> <attr name="unit" format="string"/> <attr name="model"> <enum name="record_model" value="1"/> <enum name="play_model" value="2"/> </attr> </declare-styleable></resources>
以及一切其余自定义View的东西,对自定义View不熟的同学可以先去学习下怎么自定义View(其实很简单,新手不要怕),然后再去实现一些看上去挺棒的效果。
总结
嗯,大致就是这样,对于公司这些动画的需求我只想说其实你想要咋弄,都是没问题的,最重要的就是时间!本身其实最后留给开发人员的时间就不多,然后如果还要加各种动画,那不是天天加班的节奏么~
共同学习,写下你的评论
评论加载中...
作者其他优质文章