Android进阶系列:八、自定义View之音频抖动动效

int waveH = MIN_WAVE_HEIGHT + Math.round(maxDb * (MAX_WAVE_HEIGHT - MIN_WAVE_HEIGHT));
mWaveList.add(0, waveH);
mWaveList.removeLast();
}

public boolean isStart = false;

private class LineJitterTask implements Runnable {
@Override
public void run() {
while (isStart) {
refreshElement();
try {
Thread.sleep(updateSpeed);
} catch (Exception e) {
e.printStackTrace();
}
postInvalidate();
}
}
}

public synchronized void startRecord() {
isStart = true;
executorService.execute(task);
}

public synchronized void stopRecord() {
isStart = false;
mWaveList.clear();
resetView(mWaveList, DEFAULT_WAVE_HEIGHT);
postInvalidate();
}

1.为了控制矩形抖动的范围,我们需要设置一个最大值和最小值。
2.并利用数组设置矩形的默认值,因为有四个矩形,所以数组大小为4
3.定义一个分贝值,控制矩形的高度
4.重置View的时候把默认的数组传进去,就可以达到View的重置,比如View的初始化,和停止录音的时候
5.刷新元素方法,用于不停的刷新矩阵的高度,让矩阵抖起来。这里用随机数模拟声音大小,传给数组,每次都添加到第一个,然后每次都移除最后一个,这样能让矩阵按顺序抖动。
6.在线程中调用这个刷新矩阵的方法,当开始录音的时候,在while中刷新矩阵,并睡眠100ms,这样就实现了没100ms刷新一次view,开始录音的时候设置isStart为true。
7.在停止录音的时候设置isStart为false,并初始化矩形为原始高度。由于在线程中刷新View,应该使用postInvalidate()方法。

至此这个逻辑已经实现了,稍微润色一下即可实现录音时的音频抖动
效果如图:
6...gif
完整代码:

/**

  • 语音录制的动画效果
    */
    public class LineWaveVoiceView extends View {
    private static final String DEFAULT_TEXT = " 请录音 ";
    private static final int LINE_WIDTH = 9;//默认矩形波纹的宽度,9像素, 原则上从layout的attr获得
    private Paint paint = new Paint();
    private Runnable task;
    private ExecutorService executorService = Executors.newCachedThreadPool();
    private RectF rectRight = new RectF();//右边波纹矩形的数据,10个矩形复用一个rectF
    private RectF rectLeft = new RectF();//左边波纹矩形的数据
    private String text = DEFAULT_TEXT;
    private int updateSpeed;
    private int lineColor;
    private int textColor;
    private float lineWidth;
    private float textSize;

public LineWaveVoiceView(Context context) {
super(context);
}

public LineWaveVoiceView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}

public LineWaveVoiceView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
initView(attrs, context);
resetView(mWaveList, DEFAULT_WAVE_HEIGHT);
task = new LineJitterTask();
}

private void initView(AttributeSet attrs, Context context) {
//获取布局属性里的值
TypedArray mTypedArray = context.obtainStyledAttributes(attrs, R.styleable.LineWaveVoiceView);
lineColor = mTypedArray.getColor(R.styleable.LineWaveVoiceView_voiceLineColor, context.getColor(R.color.defaultLineColor));
lineWidth = mTypedArray.getDimension(R.styleable.LineWaveVoiceView_voiceLineWidth, LINE_WIDTH);
textSize = mTypedArray.getDimension(R.styleable.LineWaveVoiceView_voiceTextSize, 42);
textColor = mTypedArray.getColor(R.styleable.LineWaveVoiceView_voiceTextColor, context.getColor(R.color.defaultTextColor));
updateSpeed = mTypedArray.getColor(R.styleable.LineWaveVoiceView_updateSpeed, UPDATE_INTERVAL_TIME);
mTypedArray.recycle();
}

@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
//获取实际宽高的一半
int widthCentre = getWidth() / 2;
int heightCentre = getHeight() / 2;
paint.setStrokeWidth(0);
paint.setColor(textColor);
paint.setTextSize(textSize);
float textWidth = paint.measureText(text);
canvas.drawText(text, widthCentre - textWidth / 2, heightCentre - (paint.ascent() + paint.descent()) / 2, paint);

//设置颜色
paint.setColor(lineColor);
//填充内部
paint.setStyle(Paint.Style.FILL);
//设置抗锯齿
paint.setAntiAlias(true);
for (int i = 0; i < 10; i++) {
rectRight.left = widthCentre + textWidth / 2 + (1 + 2 * i) * lineWidth;
rectRight.top = heightCentre - lineWidth * mWaveList.get(i) / 2;
rectRight.right = widthCentre + textWidth / 2 + (2 + 2 * i) * lineWidth;
rectRight.bottom = heightCentre + lineWidth * mWaveList.get(i) / 2;

//左边矩形
rectLeft.left = widthCentre - textWidth / 2 - (2 + 2 * i) * lineWidth;
rectLeft.top = heightCentre - mWaveList.get(i) * lineWidth / 2;
rectLeft.right = widthCentre - textWidth / 2 - (1 + 2 * i) * lineWidth;
rectLeft.bottom = heightCentre + mWaveList.get(i) * lineWidth / 2;

canvas.drawRoundRect(rectRight, 6, 6, paint);
canvas.drawRoundRect(rectLeft, 6, 6, paint);
}
}

private static final int MIN_WAVE_HEIGHT = 2;//矩形线最小高
private static final int MAX_WAVE_HEIGHT = 12;//矩形线最大高
private static final int[] DEFAULT_WAVE_HEIGHT = {2,2, 2, 2,2, 2, 2, 2,2,2};
private static final int UPDATE_INTERVAL_TIME = 100;//100ms更新一次
private LinkedList mWaveList = new LinkedList<>();
private float maxDb;

private void resetView(List list, int[] array) {
list.clear();
for (int anArray : array) {
list.add(anArray);
}
}

private synchronized void refreshElement() {
Random random = new Random();
maxDb = random.nextInt(5) + 2;
int waveH = MIN_WAVE_HEIGHT + Math.round(maxDb * (MAX_WAVE_HEIGHT - MIN_WAVE_HEIGHT));
mWaveList.add(0, waveH);
mWaveList.removeLast();
}

public boolean isStart = false;

private class LineJitterTask implements Runnable {
@Override
public void run() {
while (isStart) {
refreshElement();
try {
Thread.sleep(updateSpeed);
} catch (Exception e) {
e.printStackTrace();
}
postInvalidate();
}
}
}

public synchronized void startRecord() {
isStart = true;
executorService.execute(task);
}

public synchronized void stopRecord() {
isStart = false;
mWaveList.clear();
resetView(mWaveList, DEFAULT_WAVE_HEIGHT);
postInvalidate();
}

public synchronized void setText(String text) {
this.text = text;
postInvalidate();
}

public void setUpdateSpeed(int updateSpeed) {
this.updateSpeed = updateSpeed;
}
自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数初中级Android工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则近万的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年Android移动开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。

img

img

img

img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!

如果你觉得这些内容对你有帮助,可以扫码获取!!(备注:Android)

最后

其实Android开发的知识点就那么多,面试问来问去还是那么点东西。所以面试没有其他的诀窍,只看你对这些知识点准备的充分程度。so,出去面试时先看看自己复习到了哪个阶段就好。

上面分享的腾讯、头条、阿里、美团、字节跳动等公司2019-2021年的高频面试题,博主还把这些技术点整理成了视频和PDF(实际上比预期多花了不少精力),包含知识脉络 + 诸多细节,由于篇幅有限,上面只是以图片的形式给大家展示一部分。

【Android思维脑图(技能树)】

知识不体系?这里还有整理出来的Android进阶学习的思维脑图,给大家参考一个方向。

【Android高级架构视频学习资源】

**Android部分精讲视频领取学习后更加是如虎添翼!**进军BATJ大厂等(备战)!现在都说互联网寒冬,其实无非就是你上错了车,且穿的少(技能),要是你上对车,自身技术能力够强,公司换掉的代价大,怎么可能会被裁掉,都是淘汰末端的业务Curd而已!现如今市场上初级程序员泛滥,这套教程针对Android开发工程师1-6年的人员、正处于瓶颈期,想要年后突破自己涨薪的,进阶Android中高级、架构师对你更是如鱼得水,赶快领取吧!

《Android学习笔记总结+移动架构视频+大厂面试真题+项目实战源码》,点击传送门即可获取!

力够强,公司换掉的代价大,怎么可能会被裁掉,都是淘汰末端的业务Curd而已!现如今市场上初级程序员泛滥,这套教程针对Android开发工程师1-6年的人员、正处于瓶颈期,想要年后突破自己涨薪的,进阶Android中高级、架构师对你更是如鱼得水,赶快领取吧!

《Android学习笔记总结+移动架构视频+大厂面试真题+项目实战源码》,点击传送门即可获取!
  • 11
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值