java开发的web系统怎么播放javaweb和androidd录制的amr音频文件

Android录制声音文件(音频),并播放
readme:1、这个demo中没有对多次点击同一个声音文件做详细处理,偶尔会有崩溃,用的时候需要注意。2、按住录音按钮录音过程中,只对竖直方向处理了一下,水平方向没写;3、没有做删除某个声音文件的操作,但是测试的时候实现了功能,需要用到的话,在MainActivity&&onItemClick中的TODO中有详细说明;4、这只是个demo,如果要在项目中使用,先写出demo,没问题了,再引入项目,在写成demo后,在真机上运行的时候,如果出现获取录音权限,最好选择&允许&,如果拒绝,可能会崩溃。
记得打开手机运行录音的权限
先来效果图:
目录结构:
1、添加权限:vcD4NCjxwcmUgY2xhc3M9"brush:">
2、新建MediaRecorderUtils,复制以下:
package com.chen.
import android.media.MediaR
import android.os.H
import android.util.L
import android.widget.ImageV
import java.io.F
* 录音工具类
public class MediaRecorderUtils {
private static MediaR
static MediaRecorderUtils mediaRecorderU
static ImageView mimageV
* 获得单例对象,传入一个显示音量大小的imageview对象,如不需要显示可以传null
public static MediaRecorderUtils getInstence(ImageView imageView) {
if (mediaRecorderUtils == null) {
mediaRecorderUtils = new MediaRecorderUtils();
mimageView = imageV
return mediaRecorderU
* 获得音频路径
public String getPath() {
private void init() {
recorder = new MediaRecorder();// new出MediaRecorder对象
recorder.setAudioSource(MediaRecorder.AudioSource.MIC);
// 设置MediaRecorder的音频源为麦克风
recorder.setOutputFormat(MediaRecorder.OutputFormat.RAW_AMR);
// 设置MediaRecorder录制的音频格式
recorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB);
// 设置MediaRecorder录制音频的编码为amr.
File file = new File(Utils.IMAGE_SDCARD_MADER);
if (!file.exists()) {
file.mkdirs();
path = Utils.IMAGE_SDCARD_MADER + Utils.getVoiceFileName() + &stock.amr&;
recorder.setOutputFile(path);
// 设置录制好的音频文件保存路径
recorder.prepare();// 准备录制
} catch (Exception e) {
e.printStackTrace();
* 开始录音
public void MediaRecorderStart() {
recorder.start();
if (mimageView != null) {
updateMicStatus();
} catch (Exception e) {
e.printStackTrace();
Log.e(&chen&, &录制失败&);
* 停止录音
public void MediaRecorderStop() {
recorder.stop();
recorder.release(); //释放资源
mimageView =
recorder =
} catch (Exception e) {
e.toString();
* 删除已录制的音频
public void MediaRecorderDelete() {
File file = new File(path);
if (file.isFile()) {
file.delete();
file.exists();
private final Handler mHandler = new Handler();
private Runnable mUpdateMicStatusTimer = new Runnable() {
public void run() {
updateMicStatus();
private int BASE = 1;
private int SPACE = 1000;// 间隔取样时间
private boolean flag =
* 更新话筒状态
private void updateMicStatus() {
if (recorder != null) {
double ratio = (double) recorder.getMaxAmplitude() / BASE;
double db = 0;// 分贝
if (ratio & 1) {
db = 20 * Math.log10(ratio);
int i = (int) db / 10;
switch (i) {
mimageView.setImageResource(R.drawable.rc_ic_volume_1);
mimageView.setImageResource(R.drawable.rc_ic_volume_2);
mimageView.setImageResource(R.drawable.rc_ic_volume_3);
mimageView.setImageResource(R.drawable.rc_ic_volume_4);
mimageView.setImageResource(R.drawable.rc_ic_volume_5);
mimageView.setImageResource(R.drawable.rc_ic_volume_6);
mimageView.setImageResource(R.drawable.rc_ic_volume_7);
mimageView.setImageResource(R.drawable.rc_ic_volume_8);
if (flag) {
mHandler.postDelayed(mUpdateMicStatusTimer, SPACE);
3、创建MyChronometer,复制以下代码
package com.chen.
import android.annotation.SuppressL
import android.content.C
import android.os.H
import android.os.M
import android.os.SystemC
import android.util.AttributeS
import android.view.accessibility.AccessibilityE
import android.view.accessibility.AccessibilityNodeI
import android.widget.TextV
public class MyChronometer extends TextView {
private static final String TAG = &MyChronometer&;
* A callback that notifies when the MyChronometer has incremented on its
public interface OnMyChronometerTickListener {
* Notification that the MyChronometer has changed.
void onMyChronometerTick(int time);
public interface OnMyChronometerTimeListener {
* Notification that the MyChronometer has changed.
void OnMyChronometerTimeListener(int time);
private OnMyChronometerTimeListener OnMyChronometerTimeL
private long mB
private boolean mV
private boolean mS
private boolean mR
private OnMyChronometerTickListener mOnMyChronometerTickL
private long now_
private static final int TICK_WHAT = 2;
* Initialize this MyChronometer object. Sets the base to the current time.
public MyChronometer(Context context) {
this(context, null, 0);
* Initialize with standard view layout information. Sets the base to the
* current time.
public MyChronometer(Context context, AttributeSet attrs) {
this(context, attrs, 0);
* Initialize with standard view layout information and style. Sets the base
* to the current time.
public MyChronometer(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
private void init() {
mBase = SystemClock.elapsedRealtime();
updateText(mBase);
* Set the time that the count-up timer is in reference to.
* @param base Use the {@link SystemClock#elapsedRealtime} time base.
public void setBase(long base) {
updateText(SystemClock.elapsedRealtime());
* Sets the listener to be called when the MyChronometer changes.
* @param listener The listener.
public void setOnMyChronometerTickListener(OnMyChronometerTickListener listener) {
mOnMyChronometerTickListener =
public void setOnMyChronometerTimeListener(OnMyChronometerTimeListener listener) {
OnMyChronometerTimeListener =
* Start counting up. This does not affect the base as set from
* {@link #setBase}, just the view display.
* MyChronometer works by regularly scheduling messages to the handler, even * when the Widget is not visible. To make sure resource leaks do not occur, * the user should make sure that each start() call has a reciprocal call to * {@link #stop}. */ public void start() { mStarted = updateRunning(); } /** * Stop counting up. This does not affect the base as set from * {@link #setBase}, just the view display. *
* This stops the messages to the handler, effectively releasing resources * that would be held as the MyChronometer is running, via {@link #start}. */ public void stop() { mStarted = updateRunning(); now_time /= 10; if (OnMyChronometerTimeListener != null) { OnMyChronometerTimeListener.OnMyChronometerTimeListener((int) now_time); } } @Override protected void onDetachedFromWindow() { super.onDetachedFromWindow(); mVisible = updateRunning(); } @Override protected void onWindowVisibilityChanged(int visibility) { super.onWindowVisibilityChanged(visibility); mVisible = visibility == VISIBLE; updateRunning(); } private synchronized void updateText(long now) { long seconds = now - mB seconds /= 10; now_time = int time_m = (int) (seconds / 100); if (mOnMyChronometerTickListener != null) { mOnMyChronometerTickListener.onMyChronometerTick(time_m); } int time_s = (int) (seconds % 100); setText(time_m + &&); } private void updateRunning() { boolean running = mVisible && mS if (running != mRunning) { if (running) { updateText(SystemClock.elapsedRealtime()); mHandler.sendMessageDelayed(Message.obtain(mHandler, TICK_WHAT), 1000); } else { mHandler.removeMessages(TICK_WHAT); } mRunning = } } private Handler mHandler = new Handler() { public void handleMessage(Message m) { if (mRunning) { updateText(SystemClock.elapsedRealtime()); sendMessageDelayed(Message.obtain(this, TICK_WHAT), 1000); } } }; @SuppressLint(&NewApi&) @Override public void onInitializeAccessibilityEvent(AccessibilityEvent event) { super.onInitializeAccessibilityEvent(event); event.setClassName(MyChronometer.class.getName()); } @SuppressLint(&NewApi&) @Override public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) { super.onInitializeAccessibilityNodeInfo(info); info.setClassName(MyChronometer.class.getName()); } }
4、创建工具类
package com.chen.
import android.M
import android.content.C
import android.content.pm.PackageM
import android.os.E
import android.support.v4.content.ContextC
import android.widget.T
import java.io.F
import java.text.SimpleDateF
import java.util.ArrayL
public class Utils {
* SD卡下语音目录
public static final String IMAGE_SDCARD_MADER = Environment
.getExternalStorageDirectory()
+ &/chen/voice/&;
* 检查录音权限6.0
public static boolean checkVoice(Context context) {
if (ContextCompat.checkSelfPermission(context, Manifest.permission.RECORD_AUDIO) != PackageManager.PERMISSION_GRANTED) {
} catch (Exception e) {
private static T
* 单例吐司
public static void showToast(Context context, String msg) {
if (toast == null) {
toast = Toast.makeText(context, msg, Toast.LENGTH_SHORT);
toast.setText(msg);
toast.show();
* 获取指定文件夹下的所有文件路径
* @param root 指定文件夹路径
* @return 指定文件夹下的所有文件
public static ArrayList getVideoFiles(String root) {
if (root == null || root == &&)
ArrayList list = new ArrayList&&();
File file = new File(root);
File[] fileList = file.listFiles();
for (File f : fileList) {
list.add(f.getPath());
* 获取声音文件名字
* @return 假如当前录制声音时间是号14点30分30秒。得到的文件名字就是30.这样保证文件名的唯一性
public static String getVoiceFileName() {
long getNowTimeLong = System.currentTimeMillis();
SimpleDateFormat time = new SimpleDateFormat(&yyyyMMddHHmmss&);
String result = time.format(getNowTimeLong);
5、MainActivity
package com.chen.
import android.app.A
import android.graphics.drawable.AnimationD
import android.graphics.drawable.ColorD
import android.media.MediaP
import android.os.B
import android.os.SystemC
import android.util.L
import android.view.LayoutI
import android.view.MotionE
import android.view.V
import android.view.ViewG
import android.view.W
import android.view.WindowM
import android.widget.AdapterV
import android.widget.BaseA
import android.widget.ImageV
import android.widget.ListV
import android.widget.PopupW
import android.widget.TextV
import java.io.F
import java.util.ArrayL
import java.util.L
public class MainActivity extends Activity implements View.OnTouchListener, AdapterView.OnItemClickListener {
* 开始录音按钮
private TextV
* 用于定位。使录音时展示的popupwindow,展示在该控件 的下面
private TextView voice_
* 展示指定文件夹下所有录制的声音文件
private TextView show_voice_
* 展示目标文件夹下,所有已录制的声音路径
private ListView show_voices_
private List voiceL
* 停止播放声音
private TextView stop_show_
* 播放声音时,动的图片
private ImageView voice_
private MediaPlayer mediaP
private Boolean flag =
private float int_x = 0;
private float int_y = 0;
* 用于限制最大录音时常。单位是秒。意义是:最大录60秒的音频,到了60秒的是,自动停止
private int maxRecordTime = 60;
* 用于显示频繁操作时间间隔。单位是毫秒。意义是:500毫秒内再次操作,就算是频频操作,做相应处理
private int oftenOperationTime = 500;
private MyAdapter myA
private AnimationD
* 录音popup
private PopupWindow voice_popupW
* 录音时声音变化
private ImageView voice_
* 录音计时器
private MyChron
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_NO_TITLE);
setContentView(R.layout.activity_main);
voiceList = new ArrayList();
voice = (TextView) findViewById(R.id.voice);
voice_popup = (TextView) findViewById(R.id.voice_popup);
voice_anim = (ImageView) findViewById(R.id.voice_anim);
voice_anim.setImageResource(R.drawable.lcs_voice_receive);
show_voice_list = (TextView) findViewById(R.id.show_voice_list);
show_voice_list.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
voiceList = Utils.getVideoFiles(Utils.IMAGE_SDCARD_MADER);
if (voiceList.size()&0){
myAdapter.notifyDataSetChanged();
Utils.showToast(MainActivity.this, &没有文件&);
show_voices_listview = (ListView) findViewById(R.id.show_voices);
show_voices_listview.setOnItemClickListener(this);
myAdapter = new MyAdapter();
stop_show_voice = (TextView) findViewById(R.id.stop_show_voice);
* 停止播放的监听器
stop_show_voice.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
Log.e(&chen&, &点击了停止播放按钮&);
if (mediaPlayer != null) {
if (mediaPlayer.isPlaying()) {
mediaPlayer.release();// 释放资源
mediaPlayer =
if (animation != null && animation.isRunning()) {
animation.stop();
voice_anim.setImageResource(R.drawable.lcs_voice_receive);
show_voices_listview.setAdapter(myAdapter);
voice.setOnTouchListener(this);
* 声音文件列表的item点击事件,播放对应声音文件
* @param parent
* @param view
* @param position
* @param id
public void onItemClick(AdapterView parent, View view, int position, long id) {
//TODO 以下4行,是用来做测试,点击item,手机SD卡上对应路径下的声音文件就会被删除。如果录制声音失败,或者不满足条件,可以把以下4行写成一个工具方法调用,删除不满意的文件。这里不做详细演示
//File f_delete=new File(voiceList.get(position));
//f_delete.delete();
//voiceList.remove(voiceList.get(position));
//myAdapter.notifyDataSetChanged();
//TODO 以上4行,是用来做测试,点击item,手机SD卡上对应路径下的声音文件就会被删除。
mediaPlayer = new MediaPlayer();
* 播放过程中展示的动画
mediaPlayer.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
public void onPrepared(MediaPlayer mp) {
if (mp != null) {
mp.start();
voice_anim.setImageResource(R.drawable.voice_anim);
播放完成监听
mediaPlayer.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
public void onCompletion(MediaPlayer mp) {
if (mp.isPlaying()) {
mp.release();// 释放资源
animation = (AnimationDrawable) voice_anim.getDrawable();
if (animation != null && animation.isRunning()) {
animation.stop();
voice_anim.setImageResource(R.drawable.lcs_voice_receive);
mediaPlayer.setDataSource(voiceList.get(position));
mediaPlayer.prepare();
} catch (Exception e) {
Utils.showToast(MainActivity.this, &语音异常,加载失败&);
* 展示声音列表的adapter
class MyAdapter extends BaseAdapter {
public int getCount() {
return voiceList.size() == 0 ? 0 : voiceList.size();
public Object getItem(int position) {
public long getItemId(int position) {
public View getView(int position, View convertView, ViewGroup parent) {
TextView tv = new TextView(MainActivity.this);
tv.setText(voiceList.get(position));
tv.setTextSize(20);
* 开始录制按钮的onTouch事件
* @param v
* @param event
public boolean onTouch(View v, MotionEvent event) {
if (v.getId() == R.id.voice) {
//检查权限
if (!Utils.checkVoice(this)) {
if (event.getAction() == MotionEvent.ACTION_DOWN) {
Utils.showToast(this, &录音权限未打开,请打开录音权限!&);
//避免短时间里频繁操作
if (!getTimeTF(SystemClock.elapsedRealtime()) && event.getAction() == MotionEvent.ACTION_DOWN) {
Utils.showToast(this, &操作过于频繁&);
if (event.getAction() == MotionEvent.ACTION_DOWN) {
setTime(SystemClock.elapsedRealtime());
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
int_x = event.getRawX();
int_y = event.getRawY();
VoicePopupWindow();
mychronometer.setBase(SystemClock.elapsedRealtime());
mychronometer.start();
MediaRecorderUtils.getInstence(voice_shengyin).MediaRecorderStart();
mychronometer.setOnMyChronometerTickListener(new MyChronometer.OnMyChronometerTickListener() {
public void onMyChronometerTick(int time) {
if (time == maxRecordTime || time & maxRecordTime) {
mychronometer.setText(&60&);
setVoiceToUp();
case MotionEvent.ACTION_MOVE:
if (flag) {
if (Math.abs(int_y) - Math.abs(event.getRawY()) & 100.0 && flag) {
voice_popupWindow.dismiss();
mychronometer.stop();
MediaRecorderUtils.getInstence(voice_shengyin).MediaRecorderStop();
MediaRecorderUtils.getInstence(voice_shengyin).MediaRecorderDelete();
case MotionEvent.ACTION_CANCEL:
if (flag) {
voice_popupWindow.dismiss();
mychronometer.stop();
MediaRecorderUtils.getInstence(voice_shengyin).MediaRecorderStop();
case MotionEvent.ACTION_UP:
if (flag) {
setVoiceToUp();
private long base_time = 0;
private void setTime(long time) {
base_time =
private boolean getTimeTF(long time) {
int data = (int) (time - base_time) / oftenOperationT
if (data & 1) {
* 声音popupwindow
public void VoicePopupWindow() {
View view = LayoutInflater.from(this).inflate(R.layout.voice_popupwindow, null);
voice_popupWindow = new PopupWindow(this);
voice_popupWindow.setWidth(WindowManager.LayoutParams.MATCH_PARENT);
voice_popupWindow.setHeight(WindowManager.LayoutParams.MATCH_PARENT);
voice_shengyin = (ImageView) view.findViewById(R.id.voice_shengyin);
mychronometer = (MyChronometer) view.findViewById(R.id.mychronometer);
voice_popupWindow.setContentView(view);
voice_popupWindow.setFocusable(true);
ColorDrawable dw = new ColorDrawable(0x);
voice_popupWindow.setBackgroundDrawable(dw);
voice_popupWindow.showAsDropDown(voice_popup);
private void setVoiceToUp() {
voice_popupWindow.dismiss();
mychronometer.stop();
MediaRecorderUtils.getInstence(voice_shengyin).MediaRecorderStop();
int time = Integer.parseInt(mychronometer.getText().toString());
if (time != 0) {
File file = new File(MediaRecorderUtils.getInstence(voice_shengyin).getPath());
if (file.length() & 0) {
voiceList = Utils.getVideoFiles(Utils.IMAGE_SDCARD_MADER);
myAdapter.notifyDataSetChanged();
Utils.showToast(this, &录音失败,请检查权限&);
Utils.showToast(this, &录音时间太短&);
6、activity_main布局
7、voice_popupwindow布局代码:录音的时候,会出现以下图片中的popupwindow
8、还有一个动画布局,播放声音的时候,有个动画效果
附录:用到的图片资源说明:如果手上没有这样的图片,可以随便用其他图片代替,有效果,就算成功7417人阅读
Android笔记(78)
readme:1、这个demo中没有对多次点击同一个声音文件做详细处理,偶尔会有崩溃,用的时候需要注意。2、按住录音按钮录音过程中,只对竖直方向处理了一下,水平方向没写;3、没有做删除某个声音文件的操作,但是测试的时候实现了功能,需要用到的话,在MainActivity—&onItemClick中的TODO中有详细说明;4、这只是个demo,如果要在项目中使用,先写出demo,没问题了,再引入项目,在写成demo后,在真机上运行的时候,如果出现获取录音权限,最好选择“允许”,如果拒绝,可能会崩溃。
记得打开手机运行录音的权限
先来效果图:
目录结构:
1、添加权限:
android:name="android.permission.RECORD_AUDIO"/&
android:name="android.permission.WAKE_LOCK"/&
android:name="android.permission.MODIFY_AUDIO_SETTINGS"/&
android:name="android.permission.VIBRATE"/&
android:name="android.permission.WRITE_SETTINGS"/&
android:name="android.permission.INTERACT_ACROSS_USERS_FULL"/&
android:name="android.permission.RECEIVE_BOOT_COMPLETED"/&
android:name="android.permission.ACCESS_FINE_LOCATION"/&
android:name="android.permission.CALL_PHONE"/&
android:name="android.permission.READ_EXTERNAL_STORAGE"/&
android:name="android.permission.INTERNET" /&
android:name="android.permission.WRITE_EXTERNAL_STORAGE" /&
android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS" /&
2、新建MediaRecorderUtils,复制以下源码:
package com.chen.
import android.media.MediaR
import android.os.H
import android.util.L
import android.widget.ImageV
import java.io.F
* 录音工具类
public class MediaRecorderUtils {
private static MediaR
static MediaRecorderUtils mediaRecorderU
static ImageView mimageV
* 获得单例对象,传入一个显示音量大小的imageview对象,如不需要显示可以传null
public static MediaRecorderUtils getInstence(ImageView imageView) {
if (mediaRecorderUtils == null) {
mediaRecorderUtils = new MediaRecorderUtils();
mimageView = imageV
return mediaRecorderU
* 获得音频路径
public String getPath() {
private void init() {
recorder = new MediaRecorder();
recorder.setAudioSource(MediaRecorder.AudioSource.MIC);
recorder.setOutputFormat(MediaRecorder.OutputFormat.RAW_AMR);
recorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB);
File file = new File(Utils.IMAGE_SDCARD_MADER);
if (!file.exists()) {
file.mkdirs();
path = Utils.IMAGE_SDCARD_MADER + Utils.getVoiceFileName() + "stock.amr";
recorder.setOutputFile(path);
recorder.prepare();
} catch (Exception e) {
e.printStackTrace();
* 开始录音
public void MediaRecorderStart() {
recorder.start();
flag = true;
if (mimageView != null) {
updateMicStatus();
} catch (Exception e) {
e.printStackTrace();
Log.e("chen", "录制失败");
* 停止录音
public void MediaRecorderStop() {
recorder.stop();
recorder.release();
flag = false;
mimageView = null;
recorder = null;
} catch (Exception e) {
e.toString();
* 删除已录制的音频
public void MediaRecorderDelete() {
File file = new File(path);
if (file.isFile()) {
file.delete();
file.exists();
private final Handler mHandler = new Handler();
private Runnable mUpdateMicStatusTimer = new Runnable() {
public void run() {
updateMicStatus();
private int BASE = 1;
private int SPACE = 1000;
private boolean flag = true;
* 更新话筒状态
private void updateMicStatus() {
if (recorder != null) {
double ratio = (double) recorder.getMaxAmplitude() / BASE;
double db = 0;
if (ratio & 1) {
db = 20 * Math.log10(ratio);
int i = (int) db / 10;
switch (i) {
mimageView.setImageResource(R.drawable.rc_ic_volume_1);
mimageView.setImageResource(R.drawable.rc_ic_volume_2);
mimageView.setImageResource(R.drawable.rc_ic_volume_3);
mimageView.setImageResource(R.drawable.rc_ic_volume_4);
mimageView.setImageResource(R.drawable.rc_ic_volume_5);
mimageView.setImageResource(R.drawable.rc_ic_volume_6);
mimageView.setImageResource(R.drawable.rc_ic_volume_7);
mimageView.setImageResource(R.drawable.rc_ic_volume_8);
if (flag) {
mHandler.postDelayed(mUpdateMicStatusTimer, SPACE);
3、创建MyChronometer,复制以下代码
package com.chen.
import android.annotation.SuppressL
import android.content.C
import android.os.H
import android.os.M
import android.os.SystemC
import android.util.AttributeS
import android.view.accessibility.AccessibilityE
import android.view.accessibility.AccessibilityNodeI
import android.widget.TextV
public class MyChronometer extends TextView {
private static final String TAG = "MyChronometer";
* A callback that notifies when the MyChronometer has incremented on its
public interface OnMyChronometerTickListener {
* Notification that the MyChronometer has changed.
void onMyChronometerTick(int time);
public interface OnMyChronometerTimeListener {
* Notification that the MyChronometer has changed.
void OnMyChronometerTimeListener(int time);
private OnMyChronometerTimeListener OnMyChronometerTimeL
private long mB
private boolean mV
private boolean mS
private boolean mR
private OnMyChronometerTickListener mOnMyChronometerTickL
private long now_
private static final int TICK_WHAT = 2;
* Initialize this MyChronometer object. Sets the base to the current time.
public MyChronometer(Context context) {
this(context, null, 0);
* Initialize with standard view layout information. Sets the base to the
* current time.
public MyChronometer(Context context, AttributeSet attrs) {
this(context, attrs, 0);
* Initialize with standard view layout information and style. Sets the base
* to the current time.
public MyChronometer(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
private void init() {
mBase = SystemClock.elapsedRealtime();
updateText(mBase);
* Set the time that the count-up timer is in reference to.
* base Use the {@link SystemClock#elapsedRealtime} time base.
public void setBase(long base) {
updateText(SystemClock.elapsedRealtime());
* Sets the listener to be called when the MyChronometer changes.
* listener The listener.
public void setOnMyChronometerTickListener(OnMyChronometerTickListener listener) {
mOnMyChronometerTickListener =
public void setOnMyChronometerTimeListener(OnMyChronometerTimeListener listener) {
OnMyChronometerTimeListener =
* Start counting up. This does not affect the base as set from
* {@link #setBase}, just the view display.
* MyChronometer works by regularly scheduling messages to the handler, even
* when the Widget is not visible. To make sure resource leaks do not occur,
* the user should make sure that each start() call has a reciprocal call to
* {@link #stop}.
public void start() {
mStarted = true;
updateRunning();
* Stop counting up. This does not affect the base as set from
* {@link #setBase}, just the view display.
* This stops the messages to the handler, effectively releasing resources
* that would be held as the MyChronometer is running, via {@link #start}.
public void stop() {
mStarted = false;
updateRunning();
now_time /= 10;
if (OnMyChronometerTimeListener != null) {
OnMyChronometerTimeListener.OnMyChronometerTimeListener((int) now_time);
protected void onDetachedFromWindow() {
super.onDetachedFromWindow();
mVisible = false;
updateRunning();
protected void onWindowVisibilityChanged(int visibility) {
super.onWindowVisibilityChanged(visibility);
mVisible = visibility == VISIBLE;
updateRunning();
private synchronized void updateText(long now) {
long seconds = now - mB
seconds /= 10;
now_time =
int time_m = (int) (seconds / 100);
if (mOnMyChronometerTickListener != null) {
mOnMyChronometerTickListener.onMyChronometerTick(time_m);
int time_s = (int) (seconds % 100);
setText(time_m + "");
private void updateRunning() {
boolean running = mVisible && mS
if (running != mRunning) {
if (running) {
updateText(SystemClock.elapsedRealtime());
mHandler.sendMessageDelayed(Message.obtain(mHandler, TICK_WHAT), 1000);
mHandler.removeMessages(TICK_WHAT);
mRunning =
private Handler mHandler = new Handler() {
public void handleMessage(Message m) {
if (mRunning) {
updateText(SystemClock.elapsedRealtime());
sendMessageDelayed(Message.obtain(this, TICK_WHAT), 1000);
@SuppressLint("NewApi")
public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
super.onInitializeAccessibilityEvent(event);
event.setClassName(MyChronometer.class.getName());
@SuppressLint("NewApi")
public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
super.onInitializeAccessibilityNodeInfo(info);
info.setClassName(MyChronometer.class.getName());
4、创建工具类
package com.chen.
import android.M
import android.content.C
import android.content.pm.PackageM
import android.os.E
import android.support.v4.content.ContextC
import android.widget.T
import java.io.F
import java.text.SimpleDateF
import java.util.ArrayL
public class Utils {
* SD卡下语音目录
public static final String IMAGE_SDCARD_MADER = Environment
.getExternalStorageDirectory()
+ "/chen/voice/";
* 检查录音权限6.0
public static boolean checkVoice(Context context) {
if (ContextCompat.checkSelfPermission(context, Manifest.permission.RECORD_AUDIO) != PackageManager.PERMISSION_GRANTED) {
return false;
return true;
} catch (Exception e) {
return true;
private static T
* 单例吐司
public static void showToast(Context context, String msg) {
if (toast == null) {
toast = Toast.makeText(context, msg, Toast.LENGTH_SHORT);
toast.setText(msg);
toast.show();
* 获取指定文件夹下的所有文件路径
* root 指定文件夹路径
* 指定文件夹下的所有文件
public static ArrayList&String& getVideoFiles(String root) {
if (root == null || root == "")
return null;
ArrayList&String& list = new ArrayList&&();
File file = new File(root);
File[] fileList = file.listFiles();
for (File f : fileList) {
list.add(f.getPath());
* 获取声音文件名字
* 假如当前录制声音时间是号14点30分30秒。得到的文件名字就是30.这样保证文件名的唯一性
public static String getVoiceFileName() {
long getNowTimeLong = System.currentTimeMillis();
SimpleDateFormat time = new SimpleDateFormat("yyyyMMddHHmmss");
String result = time.format(getNowTimeLong);
5、MainActivity
package com.chen.
import android.app.A
import android.graphics.drawable.AnimationD
import android.graphics.drawable.ColorD
import android.media.MediaP
import android.os.B
import android.os.SystemC
import android.util.L
import android.view.LayoutI
import android.view.MotionE
import android.view.V
import android.view.ViewG
import android.view.W
import android.view.WindowM
import android.widget.AdapterV
import android.widget.BaseA
import android.widget.ImageV
import android.widget.ListV
import android.widget.PopupW
import android.widget.TextV
import java.io.F
import java.util.ArrayL
import java.util.L
public class MainActivity extends Activity implements View.OnTouchListener, AdapterView.OnItemClickListener {
* 开始录音按钮
private TextV
* 用于定位。使录音时展示的popupwindow,展示在该控件 的下面
private TextView voice_
* 展示指定文件夹下所有录制的声音文件
private TextView show_voice_
* 展示目标文件夹下,所有已录制的声音路径
private ListView show_voices_
private List&String& voiceL
* 停止播放声音
private TextView stop_show_
* 播放声音时,动的图片
private ImageView voice_
* 系统播放器
private MediaPlayer mediaP
private Boolean flag = true;
private float int_x = 0;
private float int_y = 0;
* 用于限制最大录音时常。单位是秒。意义是:最大录60秒的音频,到了60秒的是,自动停止
private int maxRecordTime = 60;
* 用于显示频繁操作时间间隔。单位是毫秒。意义是:500毫秒内再次操作,就算是频频操作,做相应处理
private int oftenOperationTime = 500;
private MyAdapter myA
private AnimationD
* 录音popup
private PopupWindow voice_popupW
* 录音时声音变化
private ImageView voice_
* 录音计时器
private MyChron
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_NO_TITLE);
setContentView(R.layout.activity_main);
voiceList = new ArrayList&String&();
voice = (TextView) findViewById(R.id.voice);
voice_popup = (TextView) findViewById(R.id.voice_popup);
voice_anim = (ImageView) findViewById(R.id.voice_anim);
voice_anim.setImageResource(R.drawable.lcs_voice_receive);
show_voice_list = (TextView) findViewById(R.id.show_voice_list);
show_voice_list.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
voiceList = Utils.getVideoFiles(Utils.IMAGE_SDCARD_MADER);
if (voiceList.size()&0){
myAdapter.notifyDataSetChanged();
Utils.showToast(MainActivity.this, "没有文件");
show_voices_listview = (ListView) findViewById(R.id.show_voices);
show_voices_listview.setOnItemClickListener(this);
myAdapter = new MyAdapter();
stop_show_voice = (TextView) findViewById(R.id.stop_show_voice);
* 停止播放的监听器
stop_show_voice.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
Log.e("chen", "点击了停止播放按钮");
if (mediaPlayer != null) {
if (mediaPlayer.isPlaying()) {
mediaPlayer.release();
mediaPlayer = null;
if (animation != null && animation.isRunning()) {
animation.stop();
voice_anim.setImageResource(R.drawable.lcs_voice_receive);
show_voices_listview.setAdapter(myAdapter);
voice.setOnTouchListener(this);
* 声音文件列表的item点击事件,播放对应声音文件
* position
public void onItemClick(AdapterView&?& parent, View view, int position, long id) {
mediaPlayer = new MediaPlayer();
* 播放过程中展示的动画
mediaPlayer.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
public void onPrepared(MediaPlayer mp) {
if (mp != null) {
mp.start();
voice_anim.setImageResource(R.drawable.voice_anim);
播放完成监听
mediaPlayer.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
public void onCompletion(MediaPlayer mp) {
if (mp.isPlaying()) {
mp.release();
animation = (AnimationDrawable) voice_anim.getDrawable();
if (animation != null && animation.isRunning()) {
animation.stop();
voice_anim.setImageResource(R.drawable.lcs_voice_receive);
mediaPlayer.setDataSource(voiceList.get(position));
mediaPlayer.prepare();
} catch (Exception e) {
Utils.showToast(MainActivity.this, "语音异常,加载失败");
* 展示声音列表的adapter
class MyAdapter extends BaseAdapter {
public int getCount() {
return voiceList.size() == 0 ? 0 : voiceList.size();
public Object getItem(int position) {
return null;
public long getItemId(int position) {
public View getView(int position, View convertView, ViewGroup parent) {
TextView tv = new TextView(MainActivity.this);
tv.setText(voiceList.get(position));
tv.setTextSize(20);
* 开始录制按钮的onTouch事件
public boolean onTouch(View v, MotionEvent event) {
if (v.getId() == R.id.voice) {
if (!Utils.checkVoice(this)) {
if (event.getAction() == MotionEvent.ACTION_DOWN) {
Utils.showToast(this, "录音权限未打开,请打开录音权限!");
return true;
if (!getTimeTF(SystemClock.elapsedRealtime()) && event.getAction() == MotionEvent.ACTION_DOWN) {
Utils.showToast(this, "操作过于频繁");
return true;
if (event.getAction() == MotionEvent.ACTION_DOWN) {
setTime(SystemClock.elapsedRealtime());
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
int_x = event.getRawX();
int_y = event.getRawY();
VoicePopupWindow();
mychronometer.setBase(SystemClock.elapsedRealtime());
mychronometer.start();
MediaRecorderUtils.getInstence(voice_shengyin).MediaRecorderStart();
flag = true;
mychronometer.setOnMyChronometerTickListener(new MyChronometer.OnMyChronometerTickListener() {
public void onMyChronometerTick(int time) {
if (time == maxRecordTime || time & maxRecordTime) {
mychronometer.setText("60");
setVoiceToUp();
case MotionEvent.ACTION_MOVE:
if (flag) {
if (Math.abs(int_y) - Math.abs(event.getRawY()) & 100.0 && flag) {
voice_popupWindow.dismiss();
mychronometer.stop();
MediaRecorderUtils.getInstence(voice_shengyin).MediaRecorderStop();
MediaRecorderUtils.getInstence(voice_shengyin).MediaRecorderDelete();
flag = false;
case MotionEvent.ACTION_CANCEL:
if (flag) {
voice_popupWindow.dismiss();
mychronometer.stop();
MediaRecorderUtils.getInstence(voice_shengyin).MediaRecorderStop();
case MotionEvent.ACTION_UP:
if (flag) {
setVoiceToUp();
return true;
return false;
private long base_time = 0;
private void setTime(long time) {
base_time =
private boolean getTimeTF(long time) {
int data = (int) (time - base_time) / oftenOperationT
if (data & 1) {
return true;
return false;
* 声音popupwindow
public void VoicePopupWindow() {
View view = LayoutInflater.from(this).inflate(R.layout.voice_popupwindow, null);
voice_popupWindow = new PopupWindow(this);
voice_popupWindow.setWidth(WindowManager.LayoutParams.MATCH_PARENT);
voice_popupWindow.setHeight(WindowManager.LayoutParams.MATCH_PARENT);
voice_shengyin = (ImageView) view.findViewById(R.id.voice_shengyin);
mychronometer = (MyChronometer) view.findViewById(R.id.mychronometer);
voice_popupWindow.setContentView(view);
voice_popupWindow.setFocusable(true);
ColorDrawable dw = new ColorDrawable(0x);
voice_popupWindow.setBackgroundDrawable(dw);
voice_popupWindow.showAsDropDown(voice_popup);
private void setVoiceToUp() {
flag = false;
voice_popupWindow.dismiss();
mychronometer.stop();
MediaRecorderUtils.getInstence(voice_shengyin).MediaRecorderStop();
int time = Integer.parseInt(mychronometer.getText().toString());
if (time != 0) {
File file = new File(MediaRecorderUtils.getInstence(voice_shengyin).getPath());
if (file.length() & 0) {
voiceList = Utils.getVideoFiles(Utils.IMAGE_SDCARD_MADER);
myAdapter.notifyDataSetChanged();
Utils.showToast(this, "录音失败,请检查权限");
Utils.showToast(this, "录音时间太短");
6、activity_main布局
&?xml version="1.0" encoding="utf-8"?&
xmlns:android="/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:id="@+id/voice_popup"
android:layout_width="match_parent"
android:layout_height="1dip"/&
android:id="@+id/show_voices"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"/&
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"&
android:id="@+id/voice_anim"
android:layout_width="60dp"
android:layout_height="30dp"
android:layout_centerVertical="true"
android:layout_marginLeft="30dp"
android:background="#00ff00"/&
android:id="@+id/stop_show_voice"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:layout_centerVertical="true"
android:layout_gravity="center_horizontal"
android:layout_marginBottom="20dp"
android:layout_marginRight="20dp"
android:background="#00ff00"
android:padding="10dp"
android:text="停止播放"
android:textColor="#000000"
android:textSize="20sp"
android:id="@+id/show_voice_list"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:layout_gravity="center_horizontal"
android:layout_marginBottom="20dp"
android:layout_marginRight="20dp"
android:layout_toLeftOf="@id/stop_show_voice"
android:background="#00ff00"
android:padding="10dp"
android:text="列表"
android:textColor="#000000"
android:textSize="20sp"
android:id="@+id/voice"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:background="#00ff00"
android:padding="10dp"
android:text="开始录音"
android:textColor="#000000"
android:textSize="25sp"/&
7、voice_popupwindow布局代码:录音的时候,会出现以下图片中的popupwindow
&?xml version="1.0" encoding="utf-8"?&
xmlns:android="/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"&
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:background="@android:color/black"
android:orientation="vertical"
android:paddingBottom="40dip"
android:paddingLeft="60dip"
android:paddingRight="60dip"
android:paddingTop="40dip"&
android:id="@+id/voice_shengyin"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:src="@drawable/rc_ic_volume_1"/&
android:id="@+id/mychronometer"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:layout_gravity="center_horizontal"
android:textColor="@android:color/white"/&
8、还有一个动画布局,播放声音的时候,有个动画效果
&?xml version="1.0" encoding="utf-8"?&
xmlns:android="/apk/res/android"
android:id="@+id/volume_animation"
android:oneshot="false" &
android:drawable="@drawable/rc_ic_voice_receive_play1"
android:duration="100"/&
android:drawable="@drawable/rc_ic_voice_receive_play2"
android:duration="200"/&
android:drawable="@drawable/rc_ic_voice_receive_play3"
android:duration="300"/&
附录:用到的图片资源说明:如果手上没有这样的图片,可以随便用其他图片代替,有效果,就算成功
&&相关文章推荐
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
访问:71214次
积分:1443
积分:1443
排名:千里之外
原创:69篇
评论:19条
(3)(2)(1)(1)(4)(3)(2)(4)(9)(6)(2)(7)(1)(6)(3)(14)(6)(5)

我要回帖

更多关于 javaweb做android接口 的文章

 

随机推荐