首先先看效果图吧
为了看的更清楚,所以把圈的颜色改成绿色了
下面说一下思路,左右二边是一个ImageView,自己重写了它,用画笔在上面画一个圈,然后通过滑动的距离增加或减少圆的半径在重绘就行了
下面上ImageView代码:
public class AuditionButton extends ImageView {
/**
* 中心点的X坐标
*/
private int mCenterX;
/**
* 中心点的Y坐标
*/
private int mCenterY;
/**
* 画笔
*/
private Paint mPaint;
/**
* 半径
*/
private double mRadius;
/**
* 圆和图片的间距
*/
private float padding = 20;
/**
* 扩大值
*/
private float expandValue = 0f;
/**
* 缩放系数
*/
private double zoom = 0.6f;
//宽度
private int width;
public AuditionButton(Context context) {
this(context, null);
}
public AuditionButton(Context context, AttributeSet attrs) {
super(context, attrs);
mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mPaint.setColor(Color.GREEN);
mPaint.setStrokeWidth(2);
mPaint.setStyle(Paint.Style.STROKE);
}
@Override
protected void onFinishInflate() {
super.onFinishInflate();
post(new Runnable() {
@Override
public void run() {
int dw = getDrawable().getBounds().width();
int dh = getDrawable().getBounds().height();
//图片宽高应该相等,这里随便用什么
mRadius = (dw / 2) + padding;
width = getWidth();
}
});
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
@Override
protected void onDraw(Canvas canvas) {
mCenterX = getWidth() / 2;
mCenterY = getHeight() / 2;
canvas.drawCircle(mCenterX, mCenterY, (float) mRadius + expandValue, mPaint);
super.onDraw(canvas);
}
/**
* 设置当前选中状态
*
* @param isSelected
*/
public void setSelected(boolean isSelected) {
if (isSelected) {
mPaint.setStyle(Paint.Style.FILL);
} else {
mPaint.setStyle(Paint.Style.STROKE);
}
invalidate();
}
public void setDistance(double distance) {
if (distance > 1.0f) {
expandValue = 0;
invalidate();
return;
}
double ratio = (1 - distance);
if (ratio > zoom) {
ratio = zoom;
}
float temp = (float) (width / 2 - mRadius);
expandValue = (float) (temp * ratio);
invalidate();
}
}
然后上面抖动效果,上代码
public class FluctuateView extends View {
/**
* 刷新频率
*/
private static final int LONGTIME = 500;
/**
* 画笔
*/
private Paint mPaint;
/**
* 线数量
*/
private int mLineCount = 8;
/**
* 颜色
*/
private int mLineColor;
/**
* 线的宽度
*/
private int mLineWidth;
/**
* 间距
*/
private int mLineSpacing;
/**
* 是否停止标示
*/
private boolean isStop = true;
//低
private static final int LOW = 0;
//一般
private static final int GENERAL = 1;
//中等
private static final int MEDIUM = 2;
//高
private static final int HIGH = 3;
/**
* 抖动的频率
*/
private int mShake = LOW;
public void setmShake(int mShake) {
this.mShake = mShake;
}
public FluctuateView(Context context) {
this(context, null);
}
public FluctuateView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public FluctuateView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
TypedArray array = context.getTheme().obtainStyledAttributes(attrs, R.styleable.FluctuateView, defStyleAttr, 0);
int count = array.getIndexCount();
for (int i = 0; i < count; i++) {
int attr = array.getIndex(i);
switch (attr) {
case R.styleable.FluctuateView_line_color:
mLineColor = array.getColor(attr, Color.GREEN);//默认绿色
break;
case R.styleable.FluctuateView_line_count:
mLineCount = array.getInteger(attr, 0);
break;
case R.styleable.FluctuateView_line_spacing:
mLineSpacing = array.getDimensionPixelSize(attr, 0);
break;
case R.styleable.FluctuateView_line_width:
mLineWidth = array.getDimensionPixelSize(attr, 0);
}
}
array.recycle();
mPaint = new Paint();
mPaint.setColor(mLineColor);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
if (mLineCount > 0) {
for (int i = 0; i < mLineCount; i++) {
float left = (i * mLineSpacing) + (i * mLineWidth);
int random = new Random().nextInt(getHeight());
float top = random;
float right = left + mLineWidth;
float bottom = getHeight();
canvas.drawRect(left, top, right, bottom, mPaint);
}
}
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int widthMode = MeasureSpec.getMode(widthMeasureSpec);
// int widthSize = MeasureSpec.getSize(widthMeasureSpec);
int heightSize = MeasureSpec.getSize(heightMeasureSpec);
int width;
//不允许直接写死宽度
if (widthMode == MeasureSpec.EXACTLY) {
width = (mLineCount * mLineWidth) + ((mLineCount - 1) * mLineSpacing);
} else {
width = (mLineCount * mLineWidth) + ((mLineCount - 1) * mLineSpacing);
}
setMeasuredDimension(width, heightSize);
}
/**
* 开始
*/
public void start() {
isStop = true;
new Thread() {
@Override
public void run() {
super.run();
while (isStop) {
try {
sleep(LONGTIME);
} catch (InterruptedException e) {
e.printStackTrace();
}
postInvalidate();
}
}
}.start();
}
/**
* 停止
*/
public void stop() {
isStop = false;
}
}
中间处理滑动的类
public class AudioRecordLayout extends RelativeLayout {
private static final int UPDATETIME = 0;
/**
* 录制结果接口
*/
private onRecordStatusListener mOnRecordStatusListener;
/**
* 松开试听按钮
*/
private AuditionButton mAuditionBtn;
/**
* 删除语音按钮
*/
private AuditionButton mDeleteVoiceBtn;
/**
* 录制状态文字显示
*/
private TextView tv_hintText;
/**
* 录制时间显示
*/
private TextView tv_time;
/**
* 录制按钮
*/
private ImageView imgbtn_record;
/**
* 录制抖动效果显示的View
*/
private FluctuateView mFluctuateView1;
private FluctuateView mFluctuateView2;
/**
* 用户当前按下的X坐标
*/
private float currenX;
/**
* 用户当前按钮下的Y坐标
*/
private float currenY;
/**
* 试听按钮中心点的X坐标
*/
private float auditionX;
/**
* 试听按钮中心点的X坐标
*/
private float auditionY;
/**
* 删除按钮中心带你的X坐标
*/
private float deleteX;
/**
* 删除按钮中心带你的Y坐标
*/
private float deleteY;
/**
* 试听按钮和录制按钮之间的距离
*/
private double distance1;
/**
* 删除按钮和录制按钮之间的距离
*/
private double distance2;
/**
* 删除按钮的宽度
*/
private int delteBtnWidth;
/**
* 删除按钮的高度
*/
private int delteBtnHeight;
/**
* 试听按钮的宽度
*/
private int auditionBtnWidth;
/**
* 试听按钮的高度
*/
private int auditionBtnHeight;
//判断第一次进来
private boolean isflag = true;
/**
* 录制状态
*/
public enum RecordStatus {
PREPARE_RECOIRD,
STARTE_RECORD,
CANCLE_RECORD
}
/**
* 默认是准备录制状态
*/
private RecordStatus Status = RecordStatus.PREPARE_RECOIRD;
/**
* 当前录制的时间
*/
private int currentTime;
/**
* 更新时间
*/
private Handler mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
if (msg.what == UPDATETIME) {
if (Status == RecordStatus.STARTE_RECORD) {
currentTime += 1000;
tv_time.setText(showTimeCount(currentTime));
mHandler.sendEmptyMessageDelayed(UPDATETIME, 1000);
}
}
}
};
/**
* 设置监听器
*
* @param mOnRecordStatusListener
*/
public void setOnRecordStatusListener(onRecordStatusListener mOnRecordStatusListener) {
this.mOnRecordStatusListener = mOnRecordStatusListener;
}
public AudioRecordLayout(Context context) {
super(context);
}
public AudioRecordLayout(Context context, AttributeSet attrs) {
super(context, attrs);
}
public AudioRecordLayout(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
@Override
protected void onFinishInflate() {
super.onFinishInflate();
//获取播放View
mAuditionBtn = (AuditionButton) getChildAt(1);
mDeleteVoiceBtn = (AuditionButton) getChildAt(2);
tv_hintText = (TextView) findViewById(R.id.tv_hint);
imgbtn_record = (ImageView) findViewById(R.id.imgbtn_record);
mFluctuateView1 = (FluctuateView) findViewById(R.id.fluctuate_view1);
mFluctuateView2 = (FluctuateView) findViewById(R.id.fluctuate_view2);
tv_time = (TextView) findViewById(R.id.tv_time);
onMeasureWidthAndHeight();
}
/**
* 测量各控件宽度和高度
*/
public void onMeasureWidthAndHeight() {
//获取删除按钮宽和高
ViewTreeObserver vto = mDeleteVoiceBtn.getViewTreeObserver();
vto.addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
@Override
public void onGlobalLayout() {
mDeleteVoiceBtn.getViewTreeObserver().removeGlobalOnLayoutListener(this);
delteBtnWidth = mDeleteVoiceBtn.getWidth();
delteBtnHeight = mDeleteVoiceBtn.getHeight();
}
});
//获取试听按钮的宽和高
ViewTreeObserver vto1 = mAuditionBtn.getViewTreeObserver();
vto1.addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
@Override
public void onGlobalLayout() {
mAuditionBtn.getViewTreeObserver().removeGlobalOnLayoutListener(this);
auditionBtnWidth = mAuditionBtn.getWidth();
auditionBtnHeight = mAuditionBtn.getHeight();
}
});
}
/**
* 获取各按钮中心点的坐标
*/
public void onCenterCoordinates() {
//获取试听按钮中心点的坐标
auditionX = mAuditionBtn.getX() + auditionBtnWidth / 2;
auditionY = mAuditionBtn.getY() + auditionBtnHeight / 2;
//获取删除按钮中心点的坐标
deleteX = mDeleteVoiceBtn.getX() + delteBtnWidth / 2;
deleteY = mDeleteVoiceBtn.getY() + delteBtnHeight / 2;
}
/**
* @param event
* @return
*/
@Override
public boolean onTouchEvent(MotionEvent event) {
currenX = event.getX();
currenY = event.getY();
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
//判断当前按下的坐标是否在按钮范围里面
if (containRecordBtn()) {
//开始录制
Status = RecordStatus.STARTE_RECORD;
updateButton();
tv_time.setText("00:00");
mHandler.sendEmptyMessageDelayed(UPDATETIME, 1000);
if (mOnRecordStatusListener != null) {
mOnRecordStatusListener.onRecordStart();
}
if (isflag) {
onCenterCoordinates();
distance1 = getCoordinateDistance(auditionX, auditionY, currenX, currenY);
distance2 = getCoordinateDistance(currenX, currenY, deleteX, deleteY);
isflag = false;
}
}
break;
case MotionEvent.ACTION_MOVE:
if (Status == RecordStatus.STARTE_RECORD) {
//松开
if (containDeleteView()) {
tv_hintText.setVisibility(VISIBLE);
hideTimeLayout();
tv_hintText.setText("松开取消发送");
} else if (containPlayView()) {
tv_hintText.setVisibility(VISIBLE);
hideTimeLayout();
tv_hintText.setText("松开试听");
} else {
tv_hintText.setVisibility(GONE);
showTimeLayout();
tv_time.setText(showTimeCount(currentTime));
}
double distance = getCoordinateDistance(auditionX, auditionY, currenX, currenY);
mAuditionBtn.setDistance(distance / distance1);
double distance1 = getCoordinateDistance(currenX, currenY, deleteX, deleteY);
mDeleteVoiceBtn.setDistance(distance1 / distance2);
}
break;
case MotionEvent.ACTION_UP:
Status = RecordStatus.CANCLE_RECORD;
resetTime();
if (containDeleteView()) {
if (mOnRecordStatusListener != null) {
mOnRecordStatusListener.onRecordCancle();
}
} else if (containPlayView()) {
Log.v("test", "试听");
} else {
if (mOnRecordStatusListener != null) {
mOnRecordStatusListener.onRecordComplete();
}
}
updateButton();
mAuditionBtn.setSelected(false);
mDeleteVoiceBtn.setSelected(false);
}
return true;
}
/**
* 隐藏录制时间布局
*/
public void hideTimeLayout() {
tv_time.setVisibility(GONE);
mFluctuateView1.setVisibility(GONE);
mFluctuateView2.setVisibility(GONE);
}
/**
* 显示录制时间布局
*/
public void showTimeLayout() {
tv_time.setVisibility(VISIBLE);
mFluctuateView1.setVisibility(VISIBLE);
mFluctuateView2.setVisibility(VISIBLE);
}
/**
* 松开按钮重置时间
*/
public void resetTime() {
mHandler.removeMessages(UPDATETIME);
tv_hintText.setText("00:00");
currentTime = 0;
}
/**
* 更新显示
*/
public void updateButton() {
if (Status == RecordStatus.STARTE_RECORD) {
mAuditionBtn.setVisibility(VISIBLE);
mDeleteVoiceBtn.setVisibility(VISIBLE);
//波动效果
showTimeLayout();
mFluctuateView1.start();
mFluctuateView2.start();
tv_hintText.setVisibility(GONE);
} else {
tv_hintText.setVisibility(VISIBLE);
tv_hintText.setText("按住说话");
//试听功能
mAuditionBtn.setDistance(1f);
mAuditionBtn.setVisibility(GONE);
//删除功能
mDeleteVoiceBtn.setDistance(1f);
mDeleteVoiceBtn.setVisibility(GONE);
//波动效果
mFluctuateView1.stop();
mFluctuateView2.stop();
hideTimeLayout();
}
}
/**
* 获取二个坐标点之间的距离
*
* @param x1
* @param y1
* @param x2
* @param y2
* @return
*/
public double getCoordinateDistance(float x1, float y1, float x2, float y2) {
return Math.sqrt((x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2));
}
/**
* 判断当前当前按住的点是否在录制按钮里面
*
* @return
*/
public boolean containRecordBtn() {
if ((imgbtn_record.getX() < currenX && (imgbtn_record.getX() + imgbtn_record.getWidth()) > currenX)
&& (imgbtn_record.getY() < currenY && (imgbtn_record.getY() + imgbtn_record.getHeight()) > currenY)) {
return true;
}
return false;
}
/**
* 判断当前滑动范围是否在试听按钮里面
*/
public boolean containPlayView() {
if ((mAuditionBtn.getX() < currenX && (mAuditionBtn.getX() + mAuditionBtn.getWidth()) > currenX)
&& (mAuditionBtn.getY() < currenY && (mAuditionBtn.getY() + mAuditionBtn.getHeight()) > currenY)) {
tv_hintText.setText("松手试听");
mAuditionBtn.setSelected(true);
return true;
}
mAuditionBtn.setSelected(false);
return false;
}
/**
* 判断当前滑动范围是否在删除里面
*/
public boolean containDeleteView() {
if ((mDeleteVoiceBtn.getX() < currenX && (mDeleteVoiceBtn.getX() + mDeleteVoiceBtn.getWidth()) > currenX)
&& (mDeleteVoiceBtn.getY() < currenY && (mDeleteVoiceBtn.getY() + mDeleteVoiceBtn.getHeight()) > currenY)) {
mDeleteVoiceBtn.setSelected(true);
return true;
}
mDeleteVoiceBtn.setSelected(false);
return false;
}
/**
* 转换时间
*
* @param time
* @return
*/
public String showTimeCount(long time) {
if (time >= 360000000) {
return "00:00:00";
}
String timeCount = "";
long hourc = time / 3600000;
String hour = "0" + hourc;
hour = hour.substring(hour.length() - 2, hour.length());
long minuec = (time - hourc * 3600000) / (60000);
String minue = "0" + minuec;
minue = minue.substring(minue.length() - 2, minue.length());
long secc = (time - hourc * 3600000 - minuec * 60000) / 1000;
String sec = "0" + secc;
sec = sec.substring(sec.length() - 2, sec.length());
timeCount = minue + ":" + sec;
return timeCount;
}
/**
* 录制状态监听器
*/
public interface onRecordStatusListener {
/**
* 录制开始
*/
void onRecordStart();
/**
* 录制完成
*/
void onRecordComplete();
/**
* 录制取消
*/
void onRecordCancle();
}
attrs:
<?xml version="1.0" encoding="utf-8"?>
<resources>
<attr name="line_count" format="integer" />
<attr name="line_color" format="color" />
<attr name="line_spacing" format="dimension" />
<attr name="line_width" format="dimension" />
<declare-styleable name="FluctuateView">
<attr name="line_count" />
<attr name="line_color" />
<attr name="line_spacing" />
<attr name="line_width"></attr>
</declare-styleable>
</resources>
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@android:color/darker_gray"
android:orientation="vertical">
<com.lly.view.AudioRecordLayout
android:id="@+id/record_layout"
android:layout_width="match_parent"
android:layout_height="200dp"
android:layout_alignParentBottom="true"
android:background="@android:color/white">
<ImageView
android:id="@+id/imgbtn_record"
android:layout_width="90dp"
android:layout_height="90dp"
android:layout_centerInParent="true"
android:src="@mipmap/lab" />
<com.view.AuditionButton
android:id="@+id/img_playView"
android:layout_width="60dp"
android:layout_height="60dp"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true"
android:layout_centerVertical="true"
android:layout_marginLeft="10dp"
android:scaleType="centerInside"
android:src="@mipmap/skin_aio_audio_panel_listen_nor"
android:visibility="invisible" />
<com.view.AuditionButton
android:layout_width="60dp"
android:layout_height="60dp"
android:layout_alignParentEnd="true"
android:layout_alignParentRight="true"
android:layout_centerVertical="true"
android:layout_marginRight="10dp"
android:scaleType="centerInside"
android:src="@mipmap/skin_aio_audio_panel_del_nor"
android:visibility="invisible" />
<TextView
android:id="@+id/tv_hint"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_above="@+id/imgbtn_record"
android:layout_centerHorizontal="true"
android:layout_marginBottom="10dp"
android:text="按住说话"
android:textAppearance="?android:attr/textAppearanceSmall"
android:textSize="16sp"
android:visibility="visible" />
<TextView
android:id="@+id/tv_time"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_above="@+id/imgbtn_record"
android:layout_centerHorizontal="true"
android:layout_marginBottom="10dp"
android:textAppearance="?android:attr/textAppearanceSmall"
android:textColor="@color/color1"
android:textSize="16sp"
android:visibility="gone" />
<com.lly.view.FluctuateView
android:id="@+id/fluctuate_view2"
android:layout_width="wrap_content"
android:layout_height="16dp"
android:layout_alignTop="@+id/tv_time"
android:layout_marginLeft="8dp"
android:layout_toRightOf="@+id/tv_time"
android:visibility="gone"
app:line_color="#3bb7e9"
app:line_count="8"
app:line_spacing="4dp"
app:line_width="1dp" />
<com.lly.view.FluctuateView
android:id="@+id/fluctuate_view1"
android:layout_width="wrap_content"
android:layout_height="16dp"
android:layout_alignTop="@+id/tv_time"
android:layout_marginRight="8dp"
android:layout_toLeftOf="@+id/tv_time"
android:visibility="gone"
app:line_color="#3bb7e9"
app:line_count="8"
app:line_spacing="4dp"
app:line_width="1dp" />
</com.lly.view.AudioRecordLayout>
</RelativeLayout>
Demo地址:下载地址