Android自定义UI实现微信语音
作者:Saflyer 时间:2022-04-18 10:47:41
本文实例为大家分享了java获取不同路径的方法,供大家参考,具体内容如下
思路:
自定义Button
获取DialogManager、AudioManager setOnLongClickListener长按事件--做好AudioManager的录音准备工作
AudioManager.setOnAudioStateListener(this)实现录音准备完毕的接口回调方法,方法中去发送MSG_AUDIO_PREPARE消息代表录音准备工作完毕
在mHandler中接收消息,开始开启线程录音,并且计时,计时的过程中,去发送MSG_VOICE_CHANGED消息,去updateVoiceLeve更新音量图标,另外在MotionEvent.ACTION_UP事件中,去定义audioFinishRecorderListener.onFinish接口方法,方法是录音完毕的回调方法,在MainActivity中,mAudioRecorderButton.setAudioFinishRecorderListener实现录音完毕回调方法去更新listViewUi界面
点击listViewItem条目播放动画, MediaManager.playSound并且播放音频
activity_main.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context="com.example.imooc_recorder.MainActivity" >
<ListView
android:id="@+id/id_listview"
android:layout_width="fill_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:background="#ebebeb"
android:divider="@null"
android:dividerHeight="10dp" >
</ListView>
<FrameLayout
android:layout_width="fill_parent"
android:layout_height="wrap_content" >
<View
android:background="#ccc"
android:layout_width="fill_parent"
android:layout_height="1dp" />
<com.example.recorder_view.AudioRecorderButton
android:id="@+id/id_recorder_button"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="7dp"
android:layout_marginLeft="50dp"
android:layout_marginRight="50dp"
android:layout_marginTop="6dp"
android:background="@drawable/button_recorder_normal"
android:gravity="center"
android:minHeight="0dp"
android:padding="5dp"
android:text="@string/str_recorder_nomal"
android:textColor="#727272" >
</com.example.recorder_view.AudioRecorderButton>
</FrameLayout>
</LinearLayout>
dialog_recorder.xml---dialog布局文件
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@drawable/dialog_loading_bg"
android:gravity="center"
android:orientation="vertical"
android:padding="20dp" >
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal" >
<ImageView
android:id="@+id/id_recorder_dialog_icon"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/recorder"
android:visibility="visible" />
<ImageView
android:id="@+id/id_recorder_dialog_voice"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/v1"
android:visibility="visible" />
</LinearLayout>
<TextView
android:id="@+id/id_recorder_dialog_label"
android:layout_marginTop="5dp"
android:text="手指上滑,取消发送"
android:textColor="#ffffff"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
</LinearLayout>
item_recorder.xml--listview中的每个item
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:background="#eeeeee"
android:layout_height="60dp"
android:layout_marginTop="5dp" >
<ImageView
android:id="@+id/id_icon"
android:layout_width="40dp"
android:layout_height="40dp"
android:layout_alignParentRight="true"
android:layout_centerVertical="true"
android:layout_marginRight="5dp"
android:src="@drawable/icon" />
<FrameLayout
android:id="@+id/id_recorder_length"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:layout_toLeftOf="@id/id_icon"
android:background="@drawable/chatto_bg_focused" >
<View
android:id="@+id/id_recorder_anim"
android:layout_width="25dp"
android:layout_height="25dp"
android:layout_gravity="center_vertical|right"
android:background="@drawable/adj" />
</FrameLayout>
<TextView
android:id="@+id/id_recorder_time"
android:layout_centerVertical="true"
android:layout_toLeftOf="@id/id_recorder_length"
android:layout_marginRight="3dp"
android:textColor="#ff777777"
android:text=""
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</RelativeLayout>
styles.xml
<style name="Theme_AudioDialog" parent="@android:Theme.Dialog">
<item name="android:windowBackground">@android:color/transparent</item>
<item name="android:windowFrame">@null</item>
<item name="android:windowIsFloating">true</item>
<item name="android:windowIsTranslucent">true</item>
<item name="android:backgroundDimEnabled">false</item>
</style>
button_recorder_normal.xml
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle" >
<solid android:color="#ffffff"/>
<stroke android:width="1px" android:color="#9b9b9b"/>
<corners android:radius="3dp"/>
</shape>
button_recorder_recording.xml
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle" >
<solid android:color="#ffffff"/>
<stroke android:width="1px" android:color="#eeeeee"/>
<corners android:radius="3dp"/>
</shape>
play_anim.xml
<?xml version="1.0" encoding="utf-8"?>
<animation-list xmlns:android="http://schemas.android.com/apk/res/android" >
<item
android:drawable="@drawable/v_anim1"
android:duration="300">
</item>
<item
android:drawable="@drawable/v_anim2"
android:duration="300">
</item>
<item
android:drawable="@drawable/v_anim3"
android:duration="300">
</item>
</animation-list>
MainActivity
package com.example.imooc_recorder;
import java.util.ArrayList;
import java.util.List;
import com.example.recorder_view.AudioRecorderButton;
import com.example.recorder_view.AudioRecorderButton.AudioFinishRecorderListener;
import com.example.recorder_view.MediaManager;
import android.app.Activity;
import android.graphics.drawable.AnimationDrawable;
import android.media.MediaPlayer;
import android.os.Bundle;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.ListView;
public class MainActivity extends Activity {
private ListView mListView;
private ArrayAdapter<Recorder> mAdapter;
private List<Recorder> mDatas = new ArrayList<MainActivity.Recorder>();
private AudioRecorderButton mAudioRecorderButton;
private View animView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mListView = (ListView) findViewById(R.id.id_listview);
mAudioRecorderButton = (AudioRecorderButton) findViewById(R.id.id_recorder_button);
//录音完毕,回调
mAudioRecorderButton.setAudioFinishRecorderListener(new AudioFinishRecorderListener() {
@Override
public void onFinish(float seconds, String filePath) {
// TODO Auto-generated method stub
Recorder recorder = new Recorder(seconds, filePath);
mDatas.add(recorder);
mAdapter.notifyDataSetChanged();
mListView.setSelection(mDatas.size()-1);
}
});
mAdapter = new RecorderAdapter(this, mDatas);
mListView.setAdapter(mAdapter);
mListView.setOnItemClickListener(new OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> parent, View view,
int position, long id) {
if (animView !=null) {
animView.setBackgroundResource(R.drawable.adj);
animView = null;
}
//播放动画
animView = view.findViewById(R.id.id_recorder_anim);
animView.setBackgroundResource(R.drawable.play_anim);
AnimationDrawable anim = (AnimationDrawable) animView.getBackground();
anim.start();
//播放音频
MediaManager.playSound(mDatas.get(position).filePath,new MediaPlayer.OnCompletionListener() {
@Override
public void onCompletion(MediaPlayer mp) {
// TODO Auto-generated method stub
animView.setBackgroundResource(R.drawable.adj);
}
});
}
});
}
@Override
protected void onPause() {
// TODO Auto-generated method stub
super.onPause();
MediaManager.pause();
}
@Override
protected void onResume() {
// TODO Auto-generated method stub
super.onResume();
MediaManager.resume();
}
@Override
protected void onDestroy() {
// TODO Auto-generated method stub
super.onDestroy();
MediaManager.release();
}
/**内部类*/
class Recorder{
float time;
String filePath;
public Recorder(float time, String filePath) {
super();
this.time = time;
this.filePath = filePath;
}
public float getTime() {
return time;
}
public void setTime(float time) {
this.time = time;
}
public String getFilePath() {
return filePath;
}
public void setFilePath(String filePath) {
this.filePath = filePath;
}
}
}
RecorderAdapter
package com.example.imooc_recorder;
import java.util.List;
import com.example.imooc_recorder.MainActivity.Recorder;
import android.content.Context;
import android.util.DisplayMetrics;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.WindowManager;
import android.widget.ArrayAdapter;
import android.widget.BaseAdapter;
import android.widget.LinearLayout;
import android.widget.TextView;
public class RecorderAdapter extends ArrayAdapter<Recorder> {
// private List<Recorder> mDatas;
// private Context mContext;
private int mMinItemWidth;
private int mMaxItemWidth;
private LayoutInflater mInflater;
public RecorderAdapter(Context context, List<Recorder> datas) {
super(context, -1, datas);
// mContext = context;
// mDatas = datas;
WindowManager wm = (WindowManager) context
.getSystemService(Context.WINDOW_SERVICE);
DisplayMetrics outMetrics = new DisplayMetrics();
wm.getDefaultDisplay().getMetrics(outMetrics);
mMaxItemWidth = (int) (outMetrics.widthPixels * 0.7f);
mMinItemWidth = (int) (outMetrics.widthPixels * 0.15f);
mInflater = LayoutInflater.from(context);
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
// TODO Auto-generated method stub
ViewHolder holder = null;
if (convertView == null) {
convertView = mInflater.inflate(R.layout.item_recorder, parent,
false);
holder = new ViewHolder();
holder.seconds = (TextView) convertView.findViewById(R.id.id_recorder_time);
holder.length = convertView.findViewById(R.id.id_recorder_length);
convertView.setTag(holder);
}else {
holder = (ViewHolder) convertView.getTag();
}
holder.seconds.setText(Math.round(getItem(position).time)+"\"");
ViewGroup.LayoutParams lp = holder.length.getLayoutParams();
lp.width = (int) (mMinItemWidth +(mMaxItemWidth/60f*getItem(position).time));
return convertView;
}
private class ViewHolder {
private TextView seconds;
private View length;
}
}
AudioRecorderButton
package com.example.recorder_view;
import com.example.imooc_recorder.R;
import com.example.recorder_view.AudioManager.AudioStateListener;
import android.content.Context;
import android.os.Environment;
import android.os.Handler;
import android.telephony.SignalStrength;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.widget.Button;
public class AudioRecorderButton extends Button implements AudioStateListener {
private static final int DISTANCE_Y_CANCEL = 50;
/** 提示框管理工具类 */
private DialogManager mDialogManager;
/** 正常、松开手指取消发送、手指上滑取消发送 */
private static final int STATE_NORMAL = 1;
private static final int STATE_RECORDING = 2;
private static final int STATE_WANT_TO_CANCEL = 3;
/** 初始状态:正常 */
private int mCurState = STATE_NORMAL;
/** 当前是否正在录音 */
private boolean isRecording = false;
/** 录音工具类 */
private AudioManager mAudioManager;
/** 录音总时间 */
private float mTime;
/** 是否触发button */
private boolean mReady;
/** *****************自定义Button************************************* */
public AudioRecorderButton(Context context) {
super(context);
}
public AudioRecorderButton(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
public AudioRecorderButton(Context context, AttributeSet attrs) {
super(context, attrs);
mDialogManager = new DialogManager(getContext());
String dir = Environment.getExternalStorageDirectory()
+ "/imooc_recorder_audios";
mAudioManager = AudioManager.getInstance(dir);
mAudioManager.setOnAudioStateListener(this);
/** 监听回调 */
setOnLongClickListener(new OnLongClickListener() {
@Override
public boolean onLongClick(View v) {
mReady = true;
// 录音准备--准备好后,有实现接口方法
mAudioManager.prepareAndio();
return false;
}
});
}
/******************************************************
* 完成录音准备的回调函数
*/
@Override
public void wellPrepared() {
// 准备完毕,发送消息
mHandler.sendEmptyMessage(MSG_AUDIO_PREPARE);
}
private Handler mHandler = new Handler() {
public void handleMessage(android.os.Message msg) {
switch (msg.what) {
case MSG_AUDIO_PREPARE:
mDialogManager.showRecordingDailog();
isRecording = true;
new Thread(new Runnable() {
@Override
public void run() {
// TODO Auto-generated method stub
while (isRecording) {
try {
Thread.sleep(100);
// 录音计时
mTime += 0.1f;
mHandler.sendEmptyMessage(MSG_VOICE_CHANGED);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}).start();
break;
case MSG_VOICE_CHANGED:
mDialogManager.updateVoiceLeve(mAudioManager.getVoiceLevel(7));
break;
case MSG_DIALOG_DIMISS:
mDialogManager.dimissDailog();
break;
default:
break;
}
super.handleMessage(msg);
};
};
private static final int MSG_AUDIO_PREPARE = 0x110;
private static final int MSG_VOICE_CHANGED = 0x111;
private static final int MSG_DIALOG_DIMISS = 0x112;
@Override
public boolean onTouchEvent(MotionEvent event) {
// TODO Auto-generated method stub
int action = event.getAction();
int x = (int) event.getX();
int y = (int) event.getY();
switch (action) {
case MotionEvent.ACTION_DOWN:
changeState(STATE_RECORDING);
break;
case MotionEvent.ACTION_MOVE:
if (isRecording) {
// 根据xy坐标。判断是否想要取消
if (wantToCancel(x, y)) {
changeState(STATE_WANT_TO_CANCEL);
} else {
changeState(STATE_RECORDING);
}
}
break;
case MotionEvent.ACTION_UP:
if (!mReady) {
// 没有触发onclick
reset();
return super.onTouchEvent(event);
}
if (!isRecording || mTime < 0.6f) {
// prapred没有完成已经up了
mDialogManager.tooShort();
mAudioManager.cancel();
mHandler.sendEmptyMessageDelayed(MSG_DIALOG_DIMISS, 1300);
} else if (mCurState == STATE_RECORDING) {
mDialogManager.dimissDailog();
mAudioManager.release();
if (audioFinishRecorderListener != null) {
audioFinishRecorderListener.onFinish(mTime,
mAudioManager.getCurrentFilePath());
}
} else if (mCurState == STATE_WANT_TO_CANCEL) {
mDialogManager.dimissDailog();
mAudioManager.cancel();
}
// 恢复标志位
reset();
break;
default:
break;
}
return super.onTouchEvent(event);
}
private void reset() {
// TODO Auto-generated method stub
mReady = false;
mTime = 0;
isRecording = false;
changeState(STATE_NORMAL);
}
private boolean wantToCancel(int x, int y) {
// x<0代表来到按钮的范围以外
if (x < 0 || x > getWidth()) {
return true;
}
if (y < -DISTANCE_Y_CANCEL || y > getHeight() + DISTANCE_Y_CANCEL) {
return true;
}
return false;
}
private void changeState(int state) {
// TODO Auto-generated method stub
if (mCurState != state) {
mCurState = state;
switch (state) {
case STATE_NORMAL:
setBackgroundResource(R.drawable.button_recorder_normal);
setText(R.string.str_recorder_nomal);
break;
case STATE_RECORDING:
setBackgroundResource(R.drawable.button_recorder_recording);
setText(R.string.str_recorder_recording);
if (isRecording) {
// to do
mDialogManager.recording();
}
break;
case STATE_WANT_TO_CANCEL:
setBackgroundResource(R.drawable.button_recorder_recording);
setText(R.string.str_recorder_want_cancel);
mDialogManager.wantToCancle();
break;
default:
break;
}
}
}
/** 完成录音回调 */
public interface AudioFinishRecorderListener {
void onFinish(float seconds, String filePath);
}
private AudioFinishRecorderListener audioFinishRecorderListener;
public void setAudioFinishRecorderListener(
AudioFinishRecorderListener audioFinishRecorderListener) {
this.audioFinishRecorderListener = audioFinishRecorderListener;
}
}
DialogManager
package com.example.recorder_view;
import com.example.imooc_recorder.R;
import android.app.Dialog;
import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.ImageView;
import android.widget.TextView;
public class DialogManager {
private Dialog mDialog;
private ImageView mIcon;
private ImageView mVoice;
private TextView mLabel;
private Context mContext;
public DialogManager(Context context) {
this.mContext = context;
}
public void showRecordingDailog() {
mDialog = new Dialog(mContext, R.style.Theme_AudioDialog);
LayoutInflater inflater = LayoutInflater.from(mContext);
View view = inflater.inflate(R.layout.dialog_recorder, null);
mDialog.setContentView(view);
mIcon = (ImageView) mDialog.findViewById(R.id.id_recorder_dialog_icon);
mVoice = (ImageView) mDialog
.findViewById(R.id.id_recorder_dialog_voice);
mLabel = (TextView) mDialog.findViewById(R.id.id_recorder_dialog_label);
mDialog.show();
}
public void recording() {
if (mDialog != null && mDialog.isShowing()) {
mIcon.setVisibility(View.VISIBLE);
mVoice.setVisibility(View.VISIBLE);
mLabel.setVisibility(View.VISIBLE);
mIcon.setImageResource(R.drawable.recorder);
mLabel.setText("手指上滑,取消发送");
}
}
public void wantToCancle() {
if (mDialog != null && mDialog.isShowing()) {
mIcon.setVisibility(View.VISIBLE);
mVoice.setVisibility(View.GONE);
mLabel.setVisibility(View.VISIBLE);
mIcon.setImageResource(R.drawable.cancel);
mLabel.setText("松开手指,取消发送");
}
}
public void tooShort() {
if (mDialog != null && mDialog.isShowing()) {
mIcon.setVisibility(View.VISIBLE);
mVoice.setVisibility(View.GONE);
mLabel.setVisibility(View.VISIBLE);
mIcon.setImageResource(R.drawable.voice_to_short);
mLabel.setText("录音时间过短");
}
}
public void dimissDailog() {
if (mDialog != null && mDialog.isShowing()) {
mDialog.dismiss();
mDialog = null;
}
}
public void updateVoiceLeve(int level) {
if (mDialog != null && mDialog.isShowing()) {
// mIcon.setVisibility(View.VISIBLE);
// mVoice.setVisibility(View.VISIBLE);
// mLabel.setVisibility(View.VISIBLE);
// 通过方法名字,找到资源
int resId = mContext.getResources().getIdentifier("v" + level,
"drawable", mContext.getPackageName());
mVoice.setImageResource(resId);
}
}
}
AudioManager
package com.example.recorder_view;
import java.io.File;
import java.io.IOException;
import java.util.UUID;
import javax.security.auth.PrivateCredentialPermission;
import android.R.integer;
import android.media.MediaRecorder;
public class AudioManager {
private MediaRecorder mMediaRecorder;
private String mDir;
private String mCurrentFilepath;
private static AudioManager mInstance;
private boolean isPrepare;
/** 接口回调 */
public interface AudioStateListener {
void wellPrepared();
}
public AudioStateListener mAudioStateListener;
public void setOnAudioStateListener(AudioStateListener audioStateListener) {
mAudioStateListener = audioStateListener;
}
private AudioManager(String dir) {
this.mDir = dir;
}
public static AudioManager getInstance(String dir) {
if (mInstance == null) {
synchronized (AudioManager.class) {
if (mInstance == null) {
mInstance = new AudioManager(dir);
}
}
}
return mInstance;
}
public void prepareAndio() {
isPrepare = false;
File dir = new File(mDir);
if (!dir.exists()) {
dir.mkdirs();
}
String fileName = geneFileName();
File file = new File(dir, fileName);
mCurrentFilepath = file.getAbsolutePath();
mMediaRecorder = new MediaRecorder();
// 设置输出文件
mMediaRecorder.setOutputFile(file.getAbsolutePath());
// 音频源头
mMediaRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);
// 设置音频格式
mMediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.RAW_AMR);
// 设置音频编码
mMediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB);
try {
mMediaRecorder.prepare();
mMediaRecorder.start();
isPrepare = true;
if (mAudioStateListener != null) {
mAudioStateListener.wellPrepared();
}
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
private String geneFileName() {
return UUID.randomUUID().toString() + ".amr";
}
public int getVoiceLevel(int maxLevel) {
if (isPrepare) {
try {
// mMediaRecorder.getMaxAmplitude() 1~32767
// return maxlevel*mMediaRecorder.getMaxAmplitude()/32768+1;
return maxLevel * new MediaRecorder().getMaxAmplitude() / 32768
+ 1;
} catch (Exception e) {
}
}
return 1;
}
public void release() {
mMediaRecorder.release();
mMediaRecorder = null;
}
public void cancel() {
release();
if (mCurrentFilepath != null) {
File file = new File(mCurrentFilepath);
file.delete();
mCurrentFilepath = null;
}
}
public String getCurrentFilePath() {
// TODO Auto-generated method stub
return mCurrentFilepath;
}
}
MediaManager
package com.example.recorder_view;
import java.io.IOException;
import android.media.AudioManager;
import android.media.MediaPlayer;
import android.media.MediaPlayer.OnCompletionListener;
import android.media.MediaPlayer.OnErrorListener;
public class MediaManager {
private static MediaPlayer mediaPlayer;
private static boolean isPause;
public static void playSound(String filePath,
OnCompletionListener onCompletionListener) {
if (mediaPlayer == null) {
mediaPlayer = new MediaPlayer();
mediaPlayer.setOnErrorListener(new OnErrorListener() {
@Override
public boolean onError(MediaPlayer mp, int what, int extra) {
mediaPlayer.reset();
return false;
}
});
}else {
mediaPlayer.reset();
}
try {
mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
mediaPlayer.setOnCompletionListener(onCompletionListener);
mediaPlayer.setDataSource(filePath);
mediaPlayer.prepare();
} catch (Exception e) {
e.printStackTrace();
}
mediaPlayer.start();
}
public static void pause(){
if (mediaPlayer!=null && mediaPlayer.isPlaying()) {
mediaPlayer.pause();
isPause = true;
}
}
public static void resume(){
if (mediaPlayer!=null && isPause) {
mediaPlayer.start();
isPause = false;
}
}
public static void release(){
if (mediaPlayer!=null) {
mediaPlayer.release();
mediaPlayer = null;
}
}
}
本文已被整理到了《Android微信开发教程汇总》,欢迎大家学习阅读。