Android

xiaoxiao2025-05-22  30

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

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

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


先看效果图

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

少啰嗦先看东西

接下来是取消状态的样子

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

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


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

流程如下

观察分析,抽取自定义属性考虑适配的问题,根据效果图的尺寸计算所有用到的绘制rectF的约束比例生成所有的绘制约束rectF由于有话筒动态的变化,根据画笔PorterDuffXfermode 找合适的来进行绘制
自定义属性抽取(根据效果图和你的业务扩展) <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,然后计算出我们要绘制的起点以及各个,绘制模块的坐标 比如我们先确定,画笔的下笔点,然后顺藤摸瓜,一次确定 /** * 默认宽度(宽位比例基准) * 高度为绘制基准(从底部小尾巴开始绘制) */ 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; .......省略........ 确定了要绘制的比例,第三步骤就是绘制了, 绘制的时候化繁为简,canvas能绘制基本图形,什么圆,弧形,矩形.还有....(其他的不重要,我也不会其他的,这三个完全能搞定这个话筒) @Override protected 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(); } @Override protected 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); } @Override protected 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); } @Override protected 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; } }

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

转载请注明原文地址: https://www.6miu.com/read-5030539.html

最新回复(0)