在做IM的需求中,难免要支持语音,那么肯定会有监听麦克风动态的改变话筒的音量的大小的view. 市面上大同小异都是仿微信的样子,而且实现方式也是序列帧动画.

这次根据效果图靠我们勤劳的双手来撸一个出来岂不是快哉…

1秒 ~ 2秒~ 3秒…来开撸.


  • 先看效果图

浓浓的简约黑白风,ins风格…跑题了.我们这期就是要画上面那个话筒,和那个取消的回车键…

少啰嗦先看东西

接下来是取消状态的样子

大概就是这个样子,一只画笔就搞定了…(建议:如果赶工期,直接找设计切序列帧图,不敢工期也要找设计切图…?)

此时来一个GIF吧,但是清晰度就凑乎看吧,嫌不清晰的,把代码导入项目自己运行也行.


好了,到此结束了,谢谢观看…(?对了还没撸呢…那么开始吧)

流程如下

  • 观察分析,抽取自定义属性
  • 考虑适配的问题,根据效果图的尺寸计算所有用到的绘制rectF的约束比例
  • 生成所有的绘制约束rectF
  • 由于有话筒动态的变化,根据画笔PorterDuffXfermode 找合适的来进行绘制

  1. 自定义属性抽取(根据效果图和你的业务扩展)
 <declare-styleable name="VchatAudioStateView"><attr name="audioTextColor" format="color" /><attr name="audioTextCancelColor" format="color" /><attr name="audioTubeColor" format="color" /><attr name="audioTextSize" format="dimension" /><attr name="audioBackgroundColor" format="color" /><attr name="audioCorners" format="dimension" /><attr name="audioStrokeColor" format="color" /><attr name="audioStrokeWidth" format="dimension" /><attr name="audioTubeFillColor" format="color" /><attr name="audioNormalDesc" format="string" /><attr name="audioCancelDesc" format="string" /><attr name="audioTubeLineWidth" format="dimension"/></declare-styleable>
  1. 计算约束比例
这个我是根据效果图的宽度当做基准比例为1,然后计算出我们要绘制的起点以及各个,绘制模块的坐标
比如我们先确定,画笔的下笔点,然后顺藤摸瓜,一次确定
  /*** 默认宽度(宽位比例基准)* 高度为绘制基准(从底部小尾巴开始绘制)*/private static final float VIEW_DEF_WIDTH = 176.F;/*** 默认高度*/private static final float VIEW_DEF_HEIGHT = 145.F;/*** 话筒大碗的半径比例*/private static final float SCALE_OF_BIG_CIRCLE_RADIO = 60.F / 580;/*** 话筒小尾巴的宽度比例*/private static final float SCALE_OF_TAIL_WIDTH = 8.F / 580;/*** 话筒小尾巴的高度比例*/private static final float SCALE_OF_TAIL_HEIGHT = 20.F / 580;/*** 话筒的倒角比例*/private static final float SCALE_OF_TUBE_RECT_RADIO = 40.F / 580;/*** 话筒的高度比例*/private static final float SCALE_OF_TUBE_RECT_HEIGHT = 114.F / 580;/*** 话筒的宽度比例*/private static final float SCALE_OF_TUBE_RECT_WIDTH = 80.F / 580;/*** 开始绘制的y坐标比例*/private static final float SCALE_OF_AUDIO_START = 274.F / 580;/*** 话筒开始绘制的比例起点y坐标*/private static final float SCALE_OF_TUBE_RECT_START = 238.F / 580;.......省略........
  1. 确定了要绘制的比例,第三步骤就是绘制了,
绘制的时候化繁为简,canvas能绘制基本图形,什么圆,弧形,矩形.还有....(其他的不重要,我也不会其他的,这三个完全能搞定这个话筒)
 @Overrideprotected void onDraw(Canvas canvas) {super.onDraw(canvas);drawStrokeBg(canvas);drawFillBg(canvas);if (cancelMode) {drawTubeCancel(canvas);drawAudioCancelDesc(canvas, audioCancelDesc);} else {drawTube(canvas);drawAudioDesc(canvas, audioNormalDesc);}}
 /*** 绘制话筒填充* 利用画布模式.来取交集** @param canvas 画布*/private void drawTubeFillRect(Canvas canvas) {audioTubePaint.setXfermode(xFermode);audioTubePaint.setColor(audioTubeFillColor);audioTubePaint.setStyle(Paint.Style.FILL);canvas.drawRect(tubeFillRect, audioTubePaint);audioTubePaint.setXfermode(null);audioTubePaint.setColor(audioTubeColor);audioTubePaint.setStyle(Paint.Style.STROKE);}/*** 绘制话筒** @param canvas 画布*/private void drawTubeRect(Canvas canvas) {canvas.drawRoundRect(tubeRect, tubeRectRadio + tailWidth, tubeRectRadio, audioTubePaint);}/*** 绘制话筒下面 大碗** @param canvas 画布*/private void drawBigCircle(Canvas canvas) {canvas.drawArc(bigCircleRectF, 0, 180, false, audioTubePaint);}/*** 绘制小尾巴** @param canvas 画布*/private void drawTail(Canvas canvas) {canvas.drawRect(tailRectF, audioTailPaint);}
上面就是绘制话筒的部分代码,我们把话筒分为静态部分和动态部分
静态部分就是那些黑色的框框,分别先绘制
1,小尾巴
2,大碗
3,话筒静态
4,动态背景
着重说下这个动态背景,这里我们就用到了画笔的X模式,那个经典的图,用
到的时候上网搜即可
这里我们用的模式是PorterDuff.Mode.MULTIPLY
我们让话筒里面灰色的矩形背景和静态的话筒相交,留下相交的部分,
把绘制的灰色矩形去掉不消交的角落即可.

经过上面的步骤就完成了话筒样式,解下来就是那个取消箭头的样式了,

这个似乎更简单了,这里只提一下,就是那个箭头的绘制

我们用path 先确定下箭头的点,然后往外面扩展两个点来定位箭头的开角度即可


代码虽然看着多,一共500多行吧,其实有300行是模板代码,还有100行注释吧,所以我们基本上可以用100行来实现这个话筒,下面就把完整的代码粘贴到下面.

/*** 文件描述:再次建议一定要UI切图即使你会画?,因为各司其职,你切你的图,* 用不用是我的事情?** @author :feilong on 2018/10/22*/
public class VchatAudioStateView extends View {private PorterDuffXfermode xFermode = new PorterDuffXfermode(PorterDuff.Mode.MULTIPLY);/*** 默认宽度(宽位比例基准)* 高度为绘制基准(从底部小尾巴开始绘制)*/private static final float VIEW_DEF_WIDTH = 176.F;/*** 默认高度*/private static final float VIEW_DEF_HEIGHT = 145.F;/*** 话筒大碗的半径比例*/private static final float SCALE_OF_BIG_CIRCLE_RADIO = 60.F / 580;/*** 话筒小尾巴的宽度比例*/private static final float SCALE_OF_TAIL_WIDTH = 8.F / 580;/*** 话筒小尾巴的高度比例*/private static final float SCALE_OF_TAIL_HEIGHT = 20.F / 580;/*** 话筒的倒角比例*/private static final float SCALE_OF_TUBE_RECT_RADIO = 40.F / 580;/*** 话筒的高度比例*/private static final float SCALE_OF_TUBE_RECT_HEIGHT = 114.F / 580;/*** 话筒的宽度比例*/private static final float SCALE_OF_TUBE_RECT_WIDTH = 80.F / 580;/*** 开始绘制的y坐标比例*/private static final float SCALE_OF_AUDIO_START = 274.F / 580;/*** 话筒开始绘制的比例起点y坐标*/private static final float SCALE_OF_TUBE_RECT_START = 238.F / 580;/*** 文字开始绘制的y坐标比例*/private static final float SCALE_OF_DESC_START = 370.F / 580;/*** 取消箭头的总高度 40.F / 140 (高度比例基准dp)*/private static final float SCALE_OF_CANCEL_HEIGHT = 40.F / 140;/*** 取消箭头的总宽度比例*/private static final float SCALE_OF_CANCEL_WIDTH = 24.F / 140;private float bigCircleRadio;private float tailWidth;private float tailHeight;private float tubeRectRadio;private float tubeRectHeight;private float tubeRectWidth;private float cancelTotalHeight;private float cancelTotalWidth;/*** 高度为基准,开始绘制的小尾巴中间点坐标*/private AudioPoint startPoint;private AudioPoint audioTextPoint;private AudioPoint tuebRectStartPoint;private int audioTextColor;private int audioTextSize;private int audioBackgroundColor;private int audioCorners;private int audioStrokeColor;private int audioStrokeWidth;private int audioTubeFillColor;private int audioTextCancelColor;private int audioTubeColor;private int audioViewHeight;private int audioViewWidth;private int audioTubeLineWidth;private Paint audioFillPaint;private Paint audioTextPaint;private Paint audioTextCancelPaint;private Paint audioStrokePaint;private Paint audioTubePaint;private Paint audioTubeFillPaint;private Paint audioTailPaint;private RectF fillRoundRect;private RectF strokeRoundRect;private RectF tailRectF;private RectF bigCircleRectF;private RectF tubeRect;private RectF tubeFillRect;private RectF cancelLeftRectF;private RectF cancelArcRectF;private RectF cancelRightRectF;private Path arrowPath;private String audioNormalDesc;private String audioCancelDesc;/*** 绘制的模式取消或者正常*/private boolean cancelMode;/*** 音量输入的大小转变成距离*/private float volumeHeight;/*** 最大音量的高度*/private float volumeMaxHeight;private float tubeFillTop;public VchatAudioStateView(Context context) {this(context, null);}public VchatAudioStateView(Context context, AttributeSet attrs) {this(context, attrs, 0);}public VchatAudioStateView(Context context, AttributeSet attrs, int defStyleAttr) {super(context, attrs, defStyleAttr);initAttrs(context, attrs, defStyleAttr);setBackgroundColor(audioBackgroundColor);initPaints();}/*** 初始化画笔*/private void initPaints() {audioTextPaint = creatPaint(audioTextColor, audioTextSize, Paint.Style.FILL, 0);audioTextCancelPaint = creatPaint(audioTextCancelColor, audioTextSize, Paint.Style.FILL, 0);audioStrokePaint = creatPaint(audioStrokeColor, 0, Paint.Style.FILL, 0);audioTubePaint = creatTubePaint(audioTubeColor, 0, Paint.Style.STROKE, 0);audioTubeFillPaint = creatPaint(audioTubeFillColor, 0, Paint.Style.FILL, 0);audioFillPaint = creatPaint(audioBackgroundColor, 0, Paint.Style.FILL, 0);audioTailPaint = creatTubePaint(audioTubeColor, 0, Paint.Style.FILL, 0);}/*** 初始化自定义属性** @param context      上线文* @param attrs        属性* @param defStyleAttr 默认style*/private void initAttrs(Context context, AttributeSet attrs, int defStyleAttr) {TypedArray typedArray = context.getTheme().obtainStyledAttributes(attrs, R.styleable.VchatAudioStateView, defStyleAttr, R.style.DefaultVchatAudioStateStyle);int count = typedArray.getIndexCount();for (int i = 0; i < count; i++) {int attr = typedArray.getIndex(i);switch (attr) {case R.styleable.VchatAudioStateView_audioTextColor:audioTextColor = typedArray.getColor(attr, Color.BLACK);break;case R.styleable.VchatAudioStateView_audioTextSize:audioTextSize = typedArray.getDimensionPixelSize(attr, 0);break;case R.styleable.VchatAudioStateView_audioBackgroundColor:audioBackgroundColor = typedArray.getColor(attr, Color.BLACK);break;case R.styleable.VchatAudioStateView_audioCorners:audioCorners = typedArray.getDimensionPixelOffset(attr, 0);break;case R.styleable.VchatAudioStateView_audioStrokeColor:audioStrokeColor = typedArray.getColor(attr, Color.BLACK);break;case R.styleable.VchatAudioStateView_audioStrokeWidth:audioStrokeWidth = typedArray.getDimensionPixelOffset(attr, 0);break;case R.styleable.VchatAudioStateView_audioTubeFillColor:audioTubeFillColor = typedArray.getColor(attr, Color.BLACK);break;case R.styleable.VchatAudioStateView_audioTubeColor:audioTubeColor = typedArray.getColor(attr, Color.BLACK);break;case R.styleable.VchatAudioStateView_audioTextCancelColor:audioTextCancelColor = typedArray.getColor(attr, Color.BLACK);break;case R.styleable.VchatAudioStateView_audioTubeLineWidth:audioTubeLineWidth = typedArray.getDimensionPixelOffset(attr, Color.BLACK);break;case R.styleable.VchatAudioStateView_audioNormalDesc:audioNormalDesc = typedArray.getString(attr);break;case R.styleable.VchatAudioStateView_audioCancelDesc:audioCancelDesc = typedArray.getString(attr);break;default:break;}}typedArray.recycle();}@Overrideprotected void onSizeChanged(int w, int h, int oldw, int oldh) {super.onSizeChanged(w, h, oldw, oldh);audioViewHeight = h;audioViewWidth = w;bigCircleRadio = w * SCALE_OF_BIG_CIRCLE_RADIO;tailWidth = w * SCALE_OF_TAIL_WIDTH;tailHeight = w * SCALE_OF_TAIL_HEIGHT;tubeRectRadio = w * SCALE_OF_TUBE_RECT_RADIO;tubeRectWidth = w * SCALE_OF_TUBE_RECT_WIDTH;tubeRectHeight = w * SCALE_OF_TUBE_RECT_HEIGHT;cancelTotalHeight = h * SCALE_OF_CANCEL_HEIGHT;cancelTotalWidth = h * SCALE_OF_CANCEL_WIDTH;startPoint = new AudioPoint(w * 0.5F, w * SCALE_OF_AUDIO_START);audioTextPoint = new AudioPoint(w * 0.5F, w * SCALE_OF_DESC_START);tuebRectStartPoint = new AudioPoint(w * 0.5F, w * SCALE_OF_TUBE_RECT_START);// 给画笔设定宽度audioTubePaint.setStrokeWidth(tailWidth);audioTubeFillPaint.setXfermode(xFermode);strokeRoundRect = new RectF(0, 0, audioViewWidth, audioViewHeight);fillRoundRect = new RectF(audioStrokeWidth, audioStrokeWidth, audioViewWidth - audioStrokeWidth, audioViewHeight - audioStrokeWidth);float tailLeft = audioViewWidth * 0.5F - tailWidth * 0.5F;float tailTop = startPoint.y - tailHeight;float tailRight = audioViewWidth * 0.5F + tailWidth * 0.5F;float tailBottom = startPoint.y;tailRectF = new RectF(tailLeft, tailTop, tailRight, tailBottom);//组装外面的大碗float bigCircleLeft = audioViewWidth * 0.5F - bigCircleRadio;float bigCircleRight = audioViewWidth * 0.5F + bigCircleRadio;float bigCircleBottom = tailRectF.top;float bigCircleTop = bigCircleBottom - bigCircleRadio * 2;bigCircleRectF = new RectF(bigCircleLeft, bigCircleTop, bigCircleRight, bigCircleBottom);// 组装话筒的圆角矩形float tubeLeft = audioViewWidth * 0.5F - tubeRectWidth * 0.5F;float tubeRight = audioViewWidth * 0.5F + tubeRectWidth * 0.5F;float tubeBottom = tuebRectStartPoint.y;float tubeTop = tubeBottom - tubeRectHeight;tubeRect = new RectF(tubeLeft, tubeTop, tubeRight, tubeBottom);// 记录最大音量的距离volumeMaxHeight = tubeRect.bottom - tubeRect.top;// 组装话筒中根据语音音量大小要改变的灰色填充北京,其中要改变的是矩形的高度范围float tubeFillLeft = tubeLeft + tailWidth * 0.5F;float tubeFillRight = tubeRight - tailWidth * 0.5F;float tubeFillBottom = tubeBottom - tailWidth * 0.5F;tubeFillTop = (tubeTop + tailWidth * 0.5F) + volumeHeight;tubeFillRect = new RectF(tubeFillLeft, tubeFillTop, tubeFillRight, tubeFillBottom);// 组装取消模式左边的矩形区域float cancelLeft = audioViewWidth * 0.5F - cancelTotalWidth * 0.5F;float cancelRight = cancelLeft + tailWidth * 1.F;float cancelBottom = audioViewHeight * 0.5F;float cancelTop = cancelBottom - cancelTotalHeight + tubeRectRadio;cancelLeftRectF = new RectF(cancelLeft, cancelTop, cancelRight, cancelBottom);// 组织取消模式上面的弧形区域float cancelArcLeft = cancelLeftRectF.left + tailWidth * 0.5F;float cancelArcTop = audioViewHeight * 0.5F - cancelTotalHeight;float cancelArcRight = cancelArcLeft + tubeRectRadio * 2 + tailWidth * 0.5F;float cancelArcBottom = cancelArcTop + tubeRectRadio * 2;cancelArcRectF = new RectF(cancelArcLeft, cancelArcTop, cancelArcRight, cancelArcBottom);// 组装取消模式右边的矩形区域float cancelRightLeft = cancelArcRectF.right - tailWidth * 0.5F;float cancelRightTop = cancelLeftRectF.top;float cancelRightRight = cancelRightLeft + tailWidth;float cancelRightBottom = cancelLeftRectF.bottom - 0.5F * (cancelLeftRectF.bottom - cancelLeftRectF.top);cancelRightRectF = new RectF(cancelRightLeft, cancelRightTop, cancelRightRight, cancelRightBottom);// 组装箭头数据float arrowTopX = cancelRightRectF.left + tailWidth * 0.5F;float arrowTopY = cancelRightRectF.bottom + tailWidth * 0.5F;float cons = 2.5F;float startX = arrowTopX - tailWidth * cons;float startY = arrowTopY - tailWidth * cons;float endX = arrowTopX + tailWidth * cons;float endY = arrowTopY - tailWidth * cons;arrowPath = new Path();arrowPath.moveTo(startX, startY);arrowPath.lineTo(arrowTopX, arrowTopY);arrowPath.lineTo(endX, endY);}@Overrideprotected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {int widthMode = MeasureSpec.getMode(widthMeasureSpec);int heightMode = MeasureSpec.getMode(heightMeasureSpec);int widthMeasure = 0;int heightMeasure = 0;if (widthMode == MeasureSpec.AT_MOST || widthMode == MeasureSpec.UNSPECIFIED) {widthMeasure = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, VIEW_DEF_WIDTH, getContext().getResources().getDisplayMetrics());widthMeasureSpec = MeasureSpec.makeMeasureSpec(widthMeasure, MeasureSpec.EXACTLY);}if (heightMode == MeasureSpec.AT_MOST || heightMode == MeasureSpec.UNSPECIFIED) {heightMeasure = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, VIEW_DEF_HEIGHT, getResources().getDisplayMetrics());heightMeasureSpec = MeasureSpec.makeMeasureSpec(heightMeasure, MeasureSpec.EXACTLY);}int viewMeasureSpec = widthMeasure - heightMeasure >= 0 ? heightMeasureSpec : widthMeasureSpec;super.onMeasure(viewMeasureSpec, viewMeasureSpec);}@Overrideprotected void onDraw(Canvas canvas) {super.onDraw(canvas);drawStrokeBg(canvas);drawFillBg(canvas);if (cancelMode) {drawTubeCancel(canvas);drawAudioCancelDesc(canvas, audioCancelDesc);} else {drawTube(canvas);drawAudioDesc(canvas, audioNormalDesc);}}/*** 创建画笔** @return 画笔*/private Paint creatPaint(int paintColor, int textSize, Paint.Style style, int lineWidth) {Paint paint = new Paint();paint.setColor(paintColor);paint.setAntiAlias(true);paint.setStrokeWidth(lineWidth);paint.setDither(true);paint.setTextSize(textSize);paint.setStyle(style);paint.setStrokeCap(Paint.Cap.ROUND);paint.setStrokeJoin(Paint.Join.ROUND);return paint;}private Paint creatTubePaint(int paintColor, int textSize, Paint.Style style, int lineWidth) {Paint paint = new Paint();paint.setColor(paintColor);paint.setAntiAlias(true);paint.setStrokeWidth(lineWidth);paint.setDither(true);paint.setTextSize(textSize);paint.setStyle(style);return paint;}/*** 绘制描述文案** @param canvas    画不* @param audioDesc 文案描述*/private void drawAudioDesc(Canvas canvas, String audioDesc) {AudioPoint audioPoint = measureTextSize(audioTextPaint, audioDesc);float textY = audioTextPoint.y + audioPoint.y;float textX = audioViewWidth * 0.5F - audioPoint.x * 0.5F;canvas.drawText(audioDesc, textX, textY, audioTextPaint);}private void drawAudioCancelDesc(Canvas canvas, String audioDesc) {AudioPoint audioPoint = measureTextSize(audioTextCancelPaint, audioDesc);float textY = audioTextPoint.y + audioPoint.y;float textX = audioViewWidth * 0.5F - audioPoint.x * 0.5F;canvas.drawText(audioDesc, textX, textY, audioTextCancelPaint);}/*** 绘制取消的图案** @param canvas 画布*/private void drawTubeCancel(Canvas canvas) {drawCancelLeftRect(canvas);drawCancelArc(canvas);drawCancelRightRect(canvas);drawCancelArrowPath(canvas);}/*** 绘制箭头** @param canvas 画布*/private void drawCancelArrowPath(Canvas canvas) {audioTailPaint.setStyle(Paint.Style.STROKE);audioTailPaint.setStrokeWidth(tailWidth);canvas.drawPath(arrowPath, audioTailPaint);audioTailPaint.setStrokeWidth(0);audioTailPaint.setStyle(Paint.Style.FILL);}/*** 绘制取消录音右边的矩形** @param canvas 画布*/private void drawCancelRightRect(Canvas canvas) {canvas.drawRect(cancelRightRectF, audioTailPaint);}/*** 绘制取消录音上面的半圆** @param canvas 画布*/private void drawCancelArc(Canvas canvas) {audioTailPaint.setStyle(Paint.Style.STROKE);audioTailPaint.setStrokeWidth(tailWidth);canvas.drawArc(cancelArcRectF, -180, 180, false, audioTailPaint);audioTailPaint.setStrokeWidth(0);audioTailPaint.setStyle(Paint.Style.FILL);}/*** 绘制取消录音左边的矩形** @param canvas 画布*/private void drawCancelLeftRect(Canvas canvas) {canvas.drawRect(cancelLeftRectF, audioTailPaint);}/*** 绘制话筒** @param canvas 画布*/private void drawTube(Canvas canvas) {drawTail(canvas);drawBigCircle(canvas);drawTubeRect(canvas);drawTubeFillRect(canvas);}/*** 绘制话筒填充* 利用画布模式.来取交集** @param canvas 画布*/private void drawTubeFillRect(Canvas canvas) {audioTubePaint.setXfermode(xFermode);audioTubePaint.setColor(audioTubeFillColor);audioTubePaint.setStyle(Paint.Style.FILL);canvas.drawRect(tubeFillRect, audioTubePaint);audioTubePaint.setXfermode(null);audioTubePaint.setColor(audioTubeColor);audioTubePaint.setStyle(Paint.Style.STROKE);}/*** 绘制话筒** @param canvas 画布*/private void drawTubeRect(Canvas canvas) {canvas.drawRoundRect(tubeRect, tubeRectRadio + tailWidth, tubeRectRadio, audioTubePaint);}/*** 绘制话筒下面 大碗** @param canvas 画布*/private void drawBigCircle(Canvas canvas) {canvas.drawArc(bigCircleRectF, 0, 180, false, audioTubePaint);}/*** 绘制小尾巴** @param canvas 画布*/private void drawTail(Canvas canvas) {canvas.drawRect(tailRectF, audioTailPaint);}/*** 绘制话筒填充色** @param canvas 画布*/private void drawFillBg(Canvas canvas) {canvas.drawRoundRect(fillRoundRect, audioCorners, audioCorners, audioFillPaint);}/*** 绘制描边背景** @param canvas 画布*/private void drawStrokeBg(Canvas canvas) {canvas.drawRoundRect(strokeRoundRect, audioCorners, audioCorners, audioStrokePaint);}/*** 坐标对象*/class AudioPoint {public float x;public float y;AudioPoint() {}AudioPoint(float x, float y) {this.x = x;this.y = y;}}/*** 返回文字的宽和高,x代表宽,y代表高度** @param textPaint 画笔* @param text      文字* @return 文字的宽和高*/private AudioPoint measureTextSize(Paint textPaint, String text) {AudioPoint point = new AudioPoint();if (!TextUtils.isEmpty(text)) {point.x = textPaint.measureText(text);Paint.FontMetrics fm = textPaint.getFontMetrics();point.y = (float) Math.ceil(fm.descent - fm.top);}return point;}/*** 设置绘制的模式,是否为取消模式** @param cancelMode 模式*/public void setCancelMode(boolean cancelMode) {this.cancelMode = cancelMode;invalidate();}/*** 音量大小百分比** @param percent 百分比*/public void updateVolume(float percent) {volumeHeight = volumeMaxHeight - volumeMaxHeight * percent;tubeFillRect.top = tubeFillTop + volumeHeight;invalidate();}public void setAudioNormalDesc(String audioNormalDesc) {this.audioNormalDesc = audioNormalDesc;invalidate();}public void setAudioCancelDesc(String audioCancelDesc) {this.audioCancelDesc = audioCancelDesc;}
}

能坚持到最后说明你耐力真的 可以啊,撸完了,到此结束,荆轲刺秦王…(怀念了可惜节目停播了)

Android_自定义绘制语音输入话筒相关推荐

  1. html5 语音输入小话筒,HTML5语音输入方法

    谷歌的网站是时逛时新啊,今天在他们首页发现了HTML5的新玩法--语音搜索.可惜的是只有webkit核心的浏览器才能使用.用法很简单 只需要在input添加属性 x-webkit-speech 即可, ...

  2. Android模仿淘宝语音输入条形动画,录音动画自定义View

    Android模仿淘宝语音输入条形动画自定义View,类似柱状音频,折线音频,音乐跳动,音频跳动,录音动画,语音输入效果 地址: https://github.com/xfans/VoiceWaveV ...

  3. html5 语音输入小话筒,科技常识:HTML5为输入框添加语音输入功能的实现方法

    今天小编跟大家讲解下有关HTML5为输入框添加语音输入功能的实现方法 ,相信小伙伴们对这个话题应该有所关注吧,小编也收集到了有关HTML5为输入框添加语音输入功能的实现方法 的相关资料,希望小伙伴们看 ...

  4. 百度上线英语语音输入功能识别准确率高得惊人

    (2018年4月25日,广州)第十届全球移动互联网大会(GMIC)明天将在北京举行.本届GMIC聚焦"人工智能",将主题定为:"AI"生万物,寓意科学技术要有人 ...

  5. 短视频平台源码,iOS 仿微信语音输入动画

    短视频平台源码,iOS 仿微信语音输入动画实现的相关代码 // // PBSpeechRecognizer.h // ParkBest // // Created by summerxx27 on 2 ...

  6. HTML5语音输入方法

    谷歌的网站是时逛时新啊,今天在他们首页发现了HTML5的新玩法--语音搜索.可惜的是只有webkit核心的浏览器才能使用.用法很简单 只需要在input添加属性 x-webkit-speech 即可, ...

  7. HoloLens开发手记 - 语音输入 Voice input

    语音是HoloLens三大重要输入形式之一.它允许你直接通过语言控制全息图像,而不用借助手势.你只要凝视全息图像然后说出语音命令即可.语音输入是自然的交互方式,它能够很好的改善复杂的交互,因为通过一条 ...

  8. uwp post php,window_Win10开发系列专题五 UWP应用添加画布及语音输入支持,这是微软Win10十个开发系列专 - phpStudy...

    Win10开发系列专题五 UWP应用添加画布及语音输入支持 这是微软Win10十个开发系列专题的第五期内容,本期微软讲解了为Windows10 UWP应用添加画布/数字墨水书写及语音输入支持的方法.微 ...

  9. 使用Matlab绘制语音信号的语谱图

    本文绘制语音信号的语谱图主要使用了spectrogram函数,spectrogram是一个MATLAB函数,使用短时傅里叶变换得到信号的频谱图.当使用时无输出参数,会自动绘制频谱图:有输出参数,则会返 ...

最新文章

  1. 人工智能尴尬的2019:需要钱却没钱可烧了
  2. 单细胞RNA降维之UMAP
  3. 阿里“火拼”拼多多,要“1元”抢占下沉市场
  4. Mac系统Git生成ssh公钥
  5. 记录密码的asp代码
  6. gitlab修改ip为url
  7. linux授权文件夹给用户_一项一项教你测等保2.0——Linux访问控制
  8. Linux debian 11上安装 Google Chrome浏览器教程
  9. 【C#桌面应用】第二节:利用Visual Studio2019 创建桌面应用
  10. Laravel核心解读--Database(二) 查询构建器
  11. python官网中cloudword在哪_Py之wordcloud:python中非常有趣的词云图wordcloud简介、安装、使用方法...
  12. python3 windows console 输出乱码问题
  13. TortoiseSVN文件夹及文件图标不显示的解决办法
  14. hdu1133-----递推+大数
  15. springSecurity jwt 认证与鉴权及异常
  16. 三菱plc与计算机无协议通讯,三菱PLC编程口协议与专用协议的区别
  17. 数学专业参考书整理推荐
  18. BP神经网络分类算法
  19. k8s高可用多节点master搭建
  20. 第四次作业——个人作业——必应词典软件案例分析

热门文章

  1. windows10 关闭指定端口
  2. storm是java还是python_当storm遇上python
  3. IT忍者神龟之weblogic问题集
  4. iOS 自定义抖音效果进度条实现拖拽修改播放进度
  5. python发送带表格的邮件_PYTHON自动发送报表邮件
  6. RJM8L151额温枪方案应用超低功耗8位高性能8051内核系列单片机
  7. 数字精准计算问题总结
  8. 百面机器学习 -- No.2 特征工程 -- 训练数据不足的情况下会带来什么问题,如何缓解?
  9. BLDC-永磁同步电机启动策略(4)- 有感启动
  10. 强化学习之蒙特卡洛学习,时序差分学习理论与实战