1、所谓无图无真相,先上效果图。我们要实现的就是中间那个录音的按钮,周边会显示一圈音量大小的波形

2、VolumCircleBar继承自View,我们进行了自定义,代码如下

package com.rdinfo.ccenglish.ui.ccprofile.view;

import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.RectF;
import android.util.AttributeSet;
import android.view.View; import com.panshi.xuexiao.R;
/**
* 麦克风音量圆形按钮
* @author hiphonezhu@gmail.com
* @version [CCEnglish, 2014-7-25]
*/
public class VolumCircleBar extends View
{
private double volumRate; // 音量百分比
private boolean isRecording; // 录音标志
private Object lock = new Object();
private Thread uiThread;
private Paint mPaint;
private RectF arcRect;
private Matrix matrix = new Matrix();
private final int VOLUM_INDICATE_LENGTH = ; // 音量大小线长度
private final int CIRCLE_INNER_DISTANCE_TO_OUTSIDE = ; // 内切圆距离外圆的距离
public VolumCircleBar(Context context)
{
this(context, null);
} public VolumCircleBar(Context context, AttributeSet attrs)
{
this(context, attrs, );
} public VolumCircleBar(Context context, AttributeSet attrs, int defStyle)
{
super(context, attrs, defStyle);
TypedArray typedArray = context.getTheme().obtainStyledAttributes(attrs, R.styleable.VolumCircleBar, defStyle, );
init(typedArray);
} private int recordingColor; // 录音背景色
private int stoppedColor; // 停止背景色
private Bitmap centerRes; // 中间麦克风图片
private int totalBlockCount; // 块数量
private int spliteAngle; // 块之间的间隔角度大小
private int circleWidth; // 直径
/**
* 初始化
*/
private void init(TypedArray typedArray)
{
for (int i = ; i < typedArray.length(); i++)
{
int attr = typedArray.getIndex(i);
switch (attr)
{
case R.styleable.VolumCircleBar_recordingColor:
recordingColor = typedArray.getColor(i, Color.GREEN);
break;
case R.styleable.VolumCircleBar_stoppedColor:
stoppedColor = typedArray.getColor(i, Color.GRAY);
break;
case R.styleable.VolumCircleBar_centerRes:
centerRes = BitmapFactory.decodeResource(getContext().getResources(), typedArray.getResourceId(i, R.drawable.ic_launcher));
break;
case R.styleable.VolumCircleBar_blockCount:
totalBlockCount = typedArray.getInt(i, );
break;
case R.styleable.VolumCircleBar_splitAngle:
spliteAngle = typedArray.getInt(i, );
break;
}
}
typedArray.recycle();
uiThread = Thread.currentThread();
mPaint = new Paint();
if (spliteAngle * totalBlockCount > )
{
throw new IllegalArgumentException("spliteAngle * blockCount > 360, while the result should be less than 360.");
} // debug for test
isRecording = true;
volumRate = 0.5;
} @Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)
{
// 直径
int width = MeasureSpec.getSize(widthMeasureSpec);
int height = MeasureSpec.getSize(heightMeasureSpec);
circleWidth = width > height? width : height;
if (arcRect == null)
{
arcRect = new RectF(CIRCLE_INNER_DISTANCE_TO_OUTSIDE, CIRCLE_INNER_DISTANCE_TO_OUTSIDE,
circleWidth - CIRCLE_INNER_DISTANCE_TO_OUTSIDE, circleWidth - CIRCLE_INNER_DISTANCE_TO_OUTSIDE); // 音量显示区域, 内偏移几个像素
// 图片处理矩阵
initBitmapMatrix();
}
// 强制设置view大小
setMeasuredDimension(circleWidth, circleWidth);
} /**
* 中间图片压缩处理
*/
private void initBitmapMatrix()
{
float innerCircleRadius = (circleWidth - * (VOLUM_INDICATE_LENGTH + CIRCLE_INNER_DISTANCE_TO_OUTSIDE)) / 2f; // 内圆的半径
float innerRectangleWidth = (float)Math.cos((Math.PI / ) * ) * innerCircleRadius * ; // 内圆的内切正方形的边长
float translateOffset = VOLUM_INDICATE_LENGTH + CIRCLE_INNER_DISTANCE_TO_OUTSIDE + innerCircleRadius - innerRectangleWidth / ; // 偏移的offset
if (centerRes.getWidth() > (innerRectangleWidth) || centerRes.getHeight() > (innerRectangleWidth))
{
// 图片宽度或高度大于(直径-内偏移), 等比压缩
if (centerRes.getWidth() > centerRes.getHeight())
{
// 按照宽度压缩
float ratio = innerRectangleWidth / centerRes.getWidth();
matrix.postScale(ratio, ratio);
float translateY = (innerRectangleWidth - (centerRes.getHeight() * ratio)) / 2f;
// 在纵坐标方向上进行偏移,以保证图片居中显示
matrix.postTranslate(translateOffset, translateY + translateOffset);
}
else
{
// 按照高度压缩
float ratio = innerRectangleWidth / (centerRes.getHeight() * 1.0f);
matrix.postScale(ratio, ratio);
float translateX = (innerRectangleWidth - (centerRes.getWidth() * ratio)) / 2f;
// 在横坐标方向上进行偏移,以保证图片居中显示
matrix.postTranslate(translateX + translateOffset, translateOffset);
}
}
else
{
// 当图片的宽高都小于屏幕宽高时,直接让图片居中显示
float translateX = (innerRectangleWidth - centerRes.getWidth()) / 2f;
float translateY = (innerRectangleWidth - centerRes.getHeight()) / 2f;
matrix.postTranslate(translateX + translateOffset, translateY + translateOffset);
}
} /**
* 设置音量百分比
* @param rate
*/
public void updateVolumRate(double rate)
{
synchronized (lock)
{
this.volumRate = rate;
if (Thread.currentThread() != uiThread)
{
postInvalidate();
}
else
{
invalidate();
}
}
} /**
* 开始、停止录音
*/
public void toggleRecord()
{
synchronized (lock)
{
isRecording = !isRecording;
if (Thread.currentThread() != uiThread)
{
postInvalidate();
}
else
{
invalidate();
}
}
} @Override
protected void onDraw(Canvas canvas)
{
super.onDraw(canvas);
canvas.drawColor(Color.TRANSPARENT);
synchronized (lock)
{
if (isRecording) // 正在录音
{
//1.绘制绿色圆圈
mPaint.setAntiAlias(true); //消除锯齿
mPaint.setColor(recordingColor);
mPaint.setStrokeWidth();
mPaint.setStyle(Paint.Style.FILL); // 填充
canvas.drawCircle(circleWidth / 2f, circleWidth / 2f, circleWidth / 2f, mPaint);
//2.根据音量百分比、块数量、块间隔大小计算角度动态绘制音量大小
// 计算块的角度
float blockAngle = ( * 1.0f - spliteAngle * totalBlockCount) / totalBlockCount;
int drawBlockCount = (int)(totalBlockCount * volumRate); // 绘制的block数量
mPaint.setStrokeWidth(VOLUM_INDICATE_LENGTH);
mPaint.setColor(stoppedColor);
mPaint.setStyle(Paint.Style.STROKE); // 空心
for (int i = ; i < drawBlockCount; i++)
{
canvas.drawArc(arcRect, i * (blockAngle + spliteAngle) - , blockAngle, false, mPaint);
}
}
else // 录音停止
{
//1.绘制灰色圆圈
mPaint.setColor(stoppedColor);
mPaint.setStrokeWidth();
mPaint.setStyle(Paint.Style.FILL); // 填充
canvas.drawCircle(circleWidth / 2f, circleWidth / 2f, circleWidth / 2f, mPaint);
}
}
// 绘制中间话筒
canvas.drawBitmap(centerRes, matrix, null);
}
}

Android 麦克风录音带音量大小动态显示的圆形自定义View的更多相关文章

  1. Android零基础入门第24节:自定义View简单使用

    原文:Android零基础入门第24节:自定义View简单使用 当我们开发中遇到Android原生的组件无法满足需求时,这时候就应该自定义View来满足这些特殊的组件需求. 一.概述 很多初入Andr ...

  2. Android UI 绘制过程浅析(五)自定义View

    前言 这已经是Android UI 绘制过程浅析系列文章的第五篇了,不出意外的话也是最后一篇.再次声明一下,这一系列文章,是我在拜读了csdn大牛郭霖的博客文章<带你一步步深入了解View> ...

  3. Android简易实战教程--第二十七话《自定义View入门案例之开关按钮详细分析》

    转载此博客请注明出处点击打开链接       http://blog.csdn.net/qq_32059827/article/details/52444145 对于自定义view,可能是一个比较大的 ...

  4. [转]Android自定义控件三部曲系列完全解析(动画, 绘图, 自定义View)

    来源:http://blog.csdn.net/harvic880925/article/details/50995268 一.自定义控件三部曲之动画篇 1.<自定义控件三部曲之动画篇(一)—— ...

  5. Android开发之漫漫长途 番外篇——自定义View的各种姿势1

    该文章是一个系列文章,是本人在Android开发的漫漫长途上的一点感想和记录,我会尽量按照先易后难的顺序进行编写该系列.该系列引用了<Android开发艺术探索>以及<深入理解And ...

  6. Android开发之漫漫长途 番外篇——自定义View的各种姿势2

    该文章是一个系列文章,是本人在Android开发的漫漫长途上的一点感想和记录,我会尽量按照先易后难的顺序进行编写该系列.该系列引用了<Android开发艺术探索>以及<深入理解And ...

  7. Android 自定义View修炼-Android开发之自定义View开发及实例详解

    在开发Android应用的过程中,难免需要自定义View,其实自定义View不难,只要了解原理,实现起来就没有那么难. 其主要原理就是继承View,重写构造方法.onDraw,(onMeasure)等 ...

  8. Android零基础入门第52节:自定义酷炫进度条

    原文:Android零基础入门第52节:自定义酷炫进度条 Android系统默认的ProgressBar往往都不能满足实际开发需要,一般都会开发者自定义ProgressBar. 在Android开发中 ...

  9. Android零基础入门第40节:自定义ArrayAdapter

    原文:Android零基础入门第40节:自定义ArrayAdapter ListView用起来还是比较简单的,也是Android应用程序中最重要的一个组件,但其他ListView可以随你所愿,能够完成 ...

随机推荐

  1. 初学Java(一)

    基本语法: 编写Java程序时,应注意以下几点: 1.大小写敏感:java是大小写敏感的,这就意味着标识符Hello与hello是不同的. 2.类名:对于所有的类来说,类名的首字母应该大写.如果类名由 ...

  2. @Autowired注解和启动自动扫描的三种方式(spring bean配置自动扫描功能的三种方式)

    前言: @Autowired注解代码定义 @Target({ElementType.CONSTRUCTOR, ElementType.FIELD, ElementType.METHOD, Elemen ...

  3. HDOJ-1280

    前m大的数 Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)Total Submi ...

  4. python类的特殊成员和方法

    变量或方法 描述 调用时机 __init__ 构造方法 通过类创建对象时,即类名() ,自动触发执行 __doc__ 表示类的描述信息   __module__ 表示当前操作的对象在那个模块   __ ...

  5. QDUOJ 东北大炸弹 宝岛地图-枚举+数组记录+前缀和

    冰清玉洁丶YCB 发布时间: 2017年6月18日 21:39   最后更新: 2017年6月18日 21:40   时间限制: 1000ms   内存限制: 256M 描述 YCB是公认的冰清玉洁, ...

  6. JSBridge框架解决通信问题实现移动端跨平台开发

    一.跨平台开发是趋势 目前主流的移动端平台主要是Android和iOS,为了尽可能复用代码和节省开发成本,各大巨头都开发了自己的跨平台框架,比如Facebook的React-Native.阿里的Wee ...

  7. 201621123016 《Java程序设计》第十一周学习总结

    1. 本周学习总结 1.1 以你喜欢的方式(思维导图或其他)归纳总结多线程相关内容. 2. 书面作业 本次PTA作业题集多线程 1. 源代码阅读:多线程程序BounceThread 1.1 BallR ...

  8. Linux命令之清空当前文件

    vi进入文件 英文状态下按下  / 输入  %d 执行 清空当前文件 over!O(∩_∩)O哈哈~

  9. node中的url模块解析结果

    1. URL模块作用: url 模块用于处理与解析 URL. 使用方法如下: const url = require('url'); 2. URL 字符串与 URL 对象 URL 字符串是结构化的字符 ...

  10. phpStrom/webstrom如何修改Terminal命令行的字体大小

    phpstrom.webstrom中有个Terminal窗口效果就是打开cmd.exe,利用它就不用打开本地cmd命令行了. 默认的Terminal字体很大,如何修改呢? 1.Color Scheme ...