Android自定义View实现微信语音界面
作者:Black_Hao 发布时间:2022-03-27 20:35:27
标签:Android,微信,语音
前言
因为最近的项目需要使用录音功能,开始的想法是Button+OnTouchListener+Dialog实现,在大部分手机中都没问题,只有MI8会偶尔无法触发MotionEvent.ACTION_UP,导致程序异常。所以就自己写了个自定义View来实现,主要也是通过监听
OnTouchListener+Dialog来实现。这里只实现了自定义View,并不涉及录音和播放。效果图如下:
代码
代码并不复杂,配合注释应该很容易理解。
/**
* Author : BlackHao
* Time : 2019/4/18 14:03
* Description : 自定义录音按钮布局界面
*/
public class PressedView extends View implements View.OnTouchListener {
private int normalRes;
private String normalText = "";
private int pressedRes;
private String pressedText = "";
//
private Paint paint;
private Rect rect;
//当前是否是按下状态
private boolean isPressed = false;
//
private PressCallback callback;
//按下的位置y坐标
private int pressedY = 0;
//当前是否是outSize
private boolean isOutSize = false;
//字体dp大小
private static int TEXT_SIZE = 20;
//对话框相关
private Dialog soundVolumeDialog = null;
//音量图片
private ImageView soundVolumeImg = null;
//对话框背景
private RelativeLayout soundVolumeLayout = null;
public PressedView(Context context) {
super(context);
init();
}
public PressedView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
init();
}
public PressedView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}
private void init() {
//
paint = new Paint();
paint.setAntiAlias(true);
paint.setTextSize(DensityUtil.dip2px(getContext(), TEXT_SIZE));
paint.setColor(Color.WHITE);
rect = new Rect();
//
normalRes = R.drawable.blue_btn_bk;
normalText = "按住 说话";
pressedRes = R.drawable.red_btn_bk;
pressedText = "松开 结束";
//
setOnTouchListener(this);
//
initSoundVolumeDlg();
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
rect.set(0, 0, getWidth(), getHeight());
if (!isPressed) {
setBackgroundResource(normalRes);
drawTextOnRect(canvas, rect, normalText);
} else {
setBackgroundResource(pressedRes);
drawTextOnRect(canvas, rect, pressedText);
}
}
@Override
public boolean onTouch(View v, MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
pressedY = (int) event.getRawY();
isOutSize = false;
if (!isPressed) {
isPressed = true;
postInvalidate();
if (callback != null) {
//回调
callback.onStartRecord();
//按下,弹出对话框
soundVolumeImg.setImageResource(R.mipmap.sound_volume_01);
soundVolumeImg.setVisibility(View.VISIBLE);
soundVolumeLayout.setBackgroundResource(R.mipmap.sound_volume_default_bk);
soundVolumeDialog.show();
}
}
break;
case MotionEvent.ACTION_UP:
if (isPressed) {
isPressed = false;
postInvalidate();
if (callback != null) {
int upY = (int) event.getRawY();
if (pressedY - upY < getHeight()) {
//录音结束
if (soundVolumeDialog.isShowing()) {
soundVolumeDialog.dismiss();
}
callback.onStopRecord();
} else {
//录音取消
if (soundVolumeDialog.isShowing()) {
soundVolumeDialog.dismiss();
}
callback.onCancelRecord();
}
}
}
break;
case MotionEvent.ACTION_MOVE:
if (isPressed && callback != null) {
int upY = (int) event.getRawY();
if (pressedY - upY < getHeight()) {
if (isOutSize) {
isOutSize = false;
soundVolumeLayout.setBackgroundResource(R.mipmap.sound_volume_default_bk);
}
} else {
if (!isOutSize) {
isOutSize = true;
soundVolumeLayout.setBackgroundResource(R.mipmap.sound_volume_cancel_bk);
}
}
}
break;
}
return true;
}
public void setCallback(PressCallback callback) {
this.callback = callback;
}
public interface PressCallback {
//开始录音
void onStartRecord();
//停止录音
void onStopRecord();
//取消录音
void onCancelRecord();
}
/**
* 在指定矩形中间drawText
*
* @param canvas 画布
* @param targetRect 指定矩形
* @param text 需要绘制的Text
*/
private void drawTextOnRect(Canvas canvas, Rect targetRect, String text) {
Paint.FontMetricsInt fontMetrics = paint.getFontMetricsInt();
// 获取baseLine
int baseline = targetRect.top + (targetRect.bottom - targetRect.top - fontMetrics.bottom + fontMetrics.top) / 2 - fontMetrics.top;
// 下面这行是实现水平居中,drawText对应改为传入targetRect.centerX()
paint.setTextAlign(Paint.Align.CENTER);
canvas.drawText(text, targetRect.centerX(), baseline, paint);
}
/**
* 初始化音量信息对话框
*/
private void initSoundVolumeDlg() {
soundVolumeDialog = new Dialog(getContext(), R.style.SoundVolumeStyle);
soundVolumeDialog.requestWindowFeature(Window.FEATURE_NO_TITLE);
soundVolumeDialog.getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
WindowManager.LayoutParams.FLAG_FULLSCREEN);
soundVolumeDialog.setContentView(R.layout.tt_sound_volume_dialog);
soundVolumeDialog.setCanceledOnTouchOutside(true);
soundVolumeImg = (ImageView) soundVolumeDialog.findViewById(R.id.sound_volume_img);
soundVolumeLayout = (RelativeLayout) soundVolumeDialog.findViewById(R.id.sound_volume_bk);
}
/**
* 根据分贝值设置录音时的音量动画
*/
public void setVolume(int voiceValue) {
if (voiceValue < 200.0) {
soundVolumeImg.setImageResource(R.mipmap.sound_volume_01);
} else if (voiceValue > 200.0 && voiceValue < 600) {
soundVolumeImg.setImageResource(R.mipmap.sound_volume_02);
} else if (voiceValue > 600.0 && voiceValue < 1200) {
soundVolumeImg.setImageResource(R.mipmap.sound_volume_03);
} else if (voiceValue > 1200.0 && voiceValue < 2400) {
soundVolumeImg.setImageResource(R.mipmap.sound_volume_04);
} else if (voiceValue > 2400.0 && voiceValue < 10000) {
soundVolumeImg.setImageResource(R.mipmap.sound_volume_05);
} else if (voiceValue > 10000.0 && voiceValue < 28000.0) {
soundVolumeImg.setImageResource(R.mipmap.sound_volume_06);
} else if (voiceValue > 28000.0) {
soundVolumeImg.setImageResource(R.mipmap.sound_volume_07);
}
}
}
结语
源码github地址:仿微信语音界面
来源:https://blog.csdn.net/a512337862/article/details/90602156


猜你喜欢
- 在上一篇Android RecylerView入门教程中提到,RecyclerView不再负责Item视图的布局及显示,所以Recycler
- 本文实例讲述了C#实现将汉字转化为2位大写的16进制Unicode的方法。分享给大家供大家参考。具体实现方法如下:说明:str.ToStri
- 1,Java中操作方法:import java.io.*; public class FileInputStreamTest &
- 开源配置中心 - ApolloApollo(阿波罗)是携程框架部门研发的配置管理平台,能够集中化管理应用不同环境、不同集群的配置,配置修改后
- 一、背景说明由于以前在项目中一直使用sqlmap.xml进行mybatis语句的编写和实现,其xml实现动态更新和查询较为方便,而目前由于技
- 一. Base64编码由来为什么会有Base64编码呢?因为有些网络传送渠道并不支持所有的字节,例如传统的邮件只支持可见字符的传送,像ASC
- forword跳转页面的三种方式:1.使用serlvet/** * 使用forward跳转,传递基本类型参数到页面  
- 概念介绍不同的引用类型,主要体现的是对象不同的可达性(reachable)状态和对垃圾收集的影响。01. 强引用这个就
- Android 仿今日头条评论时键盘自动弹出的效果:当点击评论时,弹出对话框,同时弹出软键盘,当点击返回键时,将对话框关闭,不只是关闭软键盘
- 在SpringMVC中 我们说到了 * , 它会在映射处理器(HandleMapping)执行时检查我们访问的地址是否配置拦截
- 这篇文章主要介绍了配置springboot项目使用外部tomcat过程解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作
- 权限上篇文章 Android 获取IP和UA中提及了获取WIFI的IP地址,本篇文章介绍下如何扫描WIFI。官方文档根据官方文档描述,扫描W
- 看代码吧~package com.mtpc.admin.controller.exportSql;import ch.qos.logback
- log4j MDC实现日志追踪MDC 中包含的可以被同一线程中执行的代码所访问内容。当前线程的子线程会继承其父线程中的 MDC 的内容。记录
- Service的生命周期 (适用于2.1及以上)1. 被startService的无论是否有任何活动绑定到该Service,都在后台运行。o
- 作为程序员,开发完一段代码,实现了某个功能时,有必要知道:我的程序需要多长时间?是什么导致我的程序消耗很多内存?比如,统计或者处理了一大批数
- 一:什么是classpath?classpath指的就是 *.java文件,资源文件等编译后存放的位置,对于maven项目就是指 targe
- Android 中解决Viewpage调用notifyDataSetChanged()时界面无刷新的问题问题描述相信很多做过Viewpage
- 在做asp.net和unity进行http通信的时候,当unity客户端发出表单请求的时候,我要将他要请求的数据以json的格式返回给客户端
- 1.会话会话: 用户打开了一个浏览器,点击了很多超链接,访问多个web次元,关闭浏览器,这个过程可以称之为会话有状态会话: 带有访问记录的会