前言

先看效果图

嗯,然后大致就是这样,按住录音,然后有一个倒计时,最外层一个进度条,还有一个类似模拟声波的动画效果(其实中间的波浪会根据声音的大小浪起来的~)

实现思路

然后,我们适当的来分析一下这个录音动画的实现方式。这个肯定是通过自定义控件,咱们来把这个效果完完全全画出来。
大致包括以下几个点:
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的时候,这个时候其实那个渐变的圆环没变化,只是纯色的圆环一直在变)。如图:A是那个渐变的圆环,B是纯色不变的圆环

/**
* 这边画进度
*/
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(){
@Override
public 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();}elsepostInvalidate();}
}
};

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(其实很简单,新手不要怕),然后再去实现一些看上去挺棒的效果。

总结

嗯,大致就是这样,对于公司这些动画的需求我只想说其实你想要咋弄,都是没问题的,最重要的就是时间!本身其实最后留给开发人员的时间就不多,然后如果还要加各种动画,那不是天天加班的节奏么~

下载地址

github地址:https://github.com/Blincheng/RecordingAnimation

【Android】自定义录音、播放动画View,让你的录音浪起来相关推荐

  1. Android按下录音录音动画效果 ,自定义录音、播放动画View

    Android按下录音录音动画效果 ,自定义录音.播放动画View https://download.csdn.net/download/abc2522/10327428?spm=1001.2101. ...

  2. Android 自定义音乐播放器实现

    Android自定义音乐播放器 一:首先介绍用了哪些Android的知识点: 1 MediaPlayer工具来播放音乐 2 Handle.因为存在定时任务(歌词切换,动画,歌词进度条变换等)需要由Ha ...

  3. Android自定义水波纹动画Layout

    Android自定义水波纹动画Layout 源码是双11的时候就写好了,但是我觉得当天发不太好,所以推迟了几天,没想到过了双11女友就变成了前女友,桑心.唉不说了,来看看代码吧. 展示效果 Hi前辈 ...

  4. android 自定义MP4播放器

    昨天,在网上找了好多资料,终于做了一个自定义的播发器. 视频播放方式 在Android中播放视频的方式有两种: 1.使用MediaPlayer结合SurfaceView进行播放.其中通过Surface ...

  5. android动画框架,GitHub - azhengyongqin/CustomAnimationFramework: Android自定义曲线路径动画框架...

    Android自定义曲线路径动画框架 最近在一个项目中需要一个像QQ打开个人爱好那样的动画效果如下图: 可以看出每个小球都是以顺时针旋转出来的,说明像这样的曲线动画用Android中自带的平移动画是很 ...

  6. Android自定义底部带有动画的Dialog

    Android自定义底部带有动画的Dialog 效果图 先看效果图,是不是你想要的呢 自定义Dialog package --.view;import android.app.Dialog; impo ...

  7. Android自定义Activity切换动画完全解析

    Android自定义Activity切换动画完全解析 在Android开发中,Activity之间的切换是最常见的业务场景了,而且系统默认的Activity之间的切换都是带动画效果的(右进右出).但是 ...

  8. Android自定义仿Siri曲线View

    Android自定义仿Siri曲线View 效果图 代码实现 仔细观察效果图可以发现,波浪其实是由4条贝塞尔曲线组成的,可以在自定义View的onDraw函数中,用Path.quadTo函数画出4条曲 ...

  9. android 自定义ViewGroup和对view进行切图动画实现滑动菜单SlidingMenu[转]

    http://blog.csdn.net/jj120522/article/details/8095852 示意图就不展示了,和上一节的一样,滑动菜单SlidingMenu效果如何大家都比较熟悉,在这 ...

  10. android 自定义加载动画效果,Android自定义View实现loading动画加载效果

    项目开发中对Loading的处理是比较常见的,安卓系统提供的不太美观,引入第三发又太麻烦,这时候自己定义View来实现这个效果,并且进行封装抽取给项目提供统一的loading样式是最好的解决方式了. ...

最新文章

  1. Flash XSS 漏洞详解 根治的好办法
  2. 神经网络 测试集loss不下降_【NLP高频面试题】神经网络及其优化
  3. 记一种数据库水平扩展的技巧
  4. 深度学习之基于DCGAN实现手写数字生成
  5. 完成聊天室的私聊功能
  6. leetcode—22.二分查找题目leetcode总结
  7. python文件和目录访问File and Directory Access
  8. 十天学会php之第六天
  9. 视频监控流媒体服务器工作原理,流媒体服务器传输基本原理
  10. java应聘面试自我介绍范文
  11. 利用python爬虫(案例1)--某电影网站的小电影们
  12. 【Codeforces】WHU校赛2019 Store(线段树+二分)
  13. linux中的正则表达式
  14. Linux服务器下载日志到本地
  15. python爬虫 403 Forbidden 解决方法
  16. Android组件——使用DrawerLayout仿网易新闻v4.4侧滑菜单
  17. html5支持4k视频播放器,哪个是最好的4K视频播放器?五个最佳播放软件(个人经验)...
  18. 信息安全系统设计基础实验五—20135222胡御风20135215黄伟业
  19. 大数据信息资料采集:公众号武志红文章评论爬取八爪鱼采集器规则
  20. 社会网络中新事物的传播

热门文章

  1. 我知道你不想交智商税
  2. 职称计算机考试题库excel,职称计算机考试模块题库(word、excel、powerpoint、xp).docx...
  3. QT QMediaPlayer 部分笔记本无法播放在线音乐(声音)
  4. 特斯拉/奔驰/大众「押宝」中国,高阶智能驾驶迎来新增长周期
  5. 人工智能和显扬科技机器视觉如何影响视觉机器人
  6. 尚硅谷甄选--(暂时不更新,实习,后期有时间更)
  7. 动物之美计算机教案,一年级美术下册第14课 可爱的动物教案
  8. 华信告诉你网站建设与搭建的几个常见误区
  9. 计算机毕业设计Java-ssm毕业生就业管理系统源码+系统+数据库+lw文档
  10. C# Path用法解析