仿微信语音语音聊天
来源:互联网 发布:四川人怎么样知乎 编辑:程序博客网 时间:2024/04/29 09:20
仿微信语音聊天,录音结束后显示在listview上,点击item可进行播放。
一。效果图
二、代码
2.1 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" >
<ListView
android:id="@+id/listview"
android:layout_width="fill_parent"
android:layout_height="0dp"
android:layout_weight="1" >
</ListView>
<Button
android:id="@+id/button"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="测试录音" />
</LinearLayout>
2.2 mainactivity.java的编写
package com.recorderactivity;
import java.util.ArrayList;
import java.util.List;
import com.recorderactivity.AudioManager.AudioStateListener;
import android.R.integer;
import android.annotation.SuppressLint;
import android.app.Activity;
import android.graphics.drawable.AnimationDrawable;
import android.media.MediaPlayer;
import android.os.Bundle;
import android.os.Environment;
import android.os.Handler;
import android.os.Message;
import android.provider.ContactsContract.CommonDataKinds.Event;
import android.view.Menu;
import android.view.MenuItem;
import android.view.MotionEvent;
import android.view.View;
import android.view.View.OnLongClickListener;
import android.view.View.OnTouchListener;
import android.view.Window;
import android.widget.Adapter;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.Button;
import android.widget.ListView;
@SuppressLint({ "ClickableViewAccessibility", "HandlerLeak" })
public class MainActivity extends Activity {
private Button button;
private ListView listview;
private ListViewAdapter mAdapter;
private List<RecorderEntity> mDatas = new ArrayList<RecorderEntity>();
/**
* 录音的三种状态
*/
private static final int STATE_NORMAL = 1;// 默认状态
private static final int STATE_RECORDING = 2;// 录音状态
private static final int STATE_CANCEL = 3;// 取消状态
private int mCurState = STATE_NORMAL;// 默认状态
private boolean isRecording = false;// 判断是否录音
private static final int DISTANCE_TO_CANCEL = 50;
private DialogManager mDialogManager;
private AudioManager mAudioManager;
private String dir = Environment.getExternalStorageDirectory()
+ "/RecordActivity";
private static final int MSG_AUDIO_PREPARED = 0X110;
private static final int MSG_VOICE_CHANGED = 0X111;
private static final int MSG_DIALOG_DIMISS = 0X112;
private Handler mHandler;
private float mTime;
private boolean mReady;// 是否触发onlongclick
private View mAnimView;// 播放动画view
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_NO_TITLE);
setContentView(R.layout.activity_main);
initView();
initDate();
setListener();
}
/**
* @Description:初始化控件
* @author :吴雄峰
*
*/
private void initView() {
// TODO 自动生成的方法存根
button = (Button) findViewById(R.id.button);
listview = (ListView) findViewById(R.id.listview);
}
/**
* @Description:初始化数据
* @author :吴雄峰
*
*/
private void initDate() {
mDialogManager = new DialogManager(MainActivity.this);
mAudioManager = AudioManager.getInstance(dir);
mAdapter = new ListViewAdapter(getApplicationContext(), mDatas);
listview.setAdapter(mAdapter);
mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
// TODO 自动生成的方法存根
switch (msg.what) {
case MSG_AUDIO_PREPARED:
mDialogManager.showRecordingDialog();
isRecording = true;
new Thread(mGetVoiceLevelRunnable).start();
break;
case MSG_VOICE_CHANGED:
mDialogManager.updateVoiceLevel(mAudioManager
.getVoiceLevel(7));
break;
case MSG_DIALOG_DIMISS:
mDialogManager.dimissDialog();
break;
default:
break;
}
}
};
}
/**
* @Description:设置监听
* @author :吴雄峰
*
*/
private void setListener() {
// TODO 自动生成的方法存根
button.setOnLongClickListener(new OnLongClickListener() {
@Override
public boolean onLongClick(View v) {
mReady = true;
mAudioManager.prepareAudio();
return false;
}
});
button.setOnTouchListener(new OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
// TODO 自动生成的方法存根
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) {// 已经开始录音
// 根据x,y的坐标判断是否想要取消
if (wantToCancel(x, y)) {
changeState(STATE_CANCEL);
} else {
changeState(STATE_RECORDING);
}
}
break;
case MotionEvent.ACTION_UP:
if (!mReady) {
reSet();
return onTouchEvent(event);
}
if (!isRecording || mTime < 0.6f) {// 时间过短,录音还没开始
mDialogManager.tooShort();
mAudioManager.cancel();
mHandler.sendEmptyMessageDelayed(MSG_DIALOG_DIMISS,
1300);
} else if (mCurState == STATE_RECORDING) {// 正常结束
mDialogManager.dimissDialog();
mAudioManager.release();
// 添加数据到listview中
RecorderEntity mEntity = new RecorderEntity(mTime,
mAudioManager.getCurrentFilePath());
mDatas.add(mEntity);
mAdapter.notifyDataSetChanged();
listview.setSelection(mDatas.size() - 1);
} else if (mCurState == STATE_CANCEL) {// 取消
mDialogManager.dimissDialog();
mAudioManager.cancel();
}
reSet();
break;
default:
break;
}
return false;
}
});
mAudioManager.setOnAudioStateListener(new AudioStateListener() {
@Override
public void wellPrepared() {
// TODO 自动生成的方法存根
mHandler.sendEmptyMessage(MSG_AUDIO_PREPARED);
}
});
listview.setOnItemClickListener(new OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> parent, View view,
int position, long id) {
if (mAnimView != null) {
mAnimView.setBackgroundResource(R.drawable.adj);
mAnimView = null;
}
// 播放动画,帧动画去实现
mAnimView = view.findViewById(R.id.id_recorder_anim);
mAnimView.setBackgroundResource(R.drawable.play_anim);
AnimationDrawable animationDrawable = (AnimationDrawable) mAnimView
.getBackground();
animationDrawable.start();
// 播放音频
MediaManager.playSound(mDatas.get(position).getmFilePath(),
new MediaPlayer.OnCompletionListener() {
@Override
public void onCompletion(MediaPlayer mp) {
mAnimView.setBackgroundResource(R.drawable.adj);
}
});
}
});
}
@Override
protected void onPause() {
// TODO 自动生成的方法存根
super.onPause();
MediaManager.pause();
}
@Override
protected void onResume() {
// TODO 自动生成的方法存根
super.onResume();
MediaManager.resume();
}
@Override
protected void onDestroy() {
// TODO 自动生成的方法存根
super.onDestroy();
MediaManager.release();
}
/**
* 获取音量大小的runnable
*/
private Runnable mGetVoiceLevelRunnable = new Runnable() {
@Override
public void run() {
// TODO 自动生成的方法存根
while (isRecording) {
try {
Thread.sleep(100);
mTime += 0.1f;
mHandler.sendEmptyMessage(MSG_VOICE_CHANGED);
} catch (Exception e) {
// TODO: handle exception
}
}
}
};
/**
* @Description: 恢复状态及标志位
* @author :吴雄峰
*
*/
protected void reSet() {
// TODO 自动生成的方法存根
isRecording = false;
changeState(STATE_NORMAL);
mTime = 0;
mReady = false;
}
/**
* @Description: 根据坐标判断是否要取消
* @author :吴雄峰
*
*/
protected boolean wantToCancel(int x, int y) {
if (x < 0 || x > button.getWidth()) {
return true;
}
if (y < -DISTANCE_TO_CANCEL
|| y > button.getHeight() + DISTANCE_TO_CANCEL) {
return true;
}
return false;
}
/**
* @Description:改变状态
* @author :吴雄峰
*
*/
protected void changeState(int state) {
// TODO 自动生成的方法存根
if (mCurState != state) {
mCurState = state;
switch (state) {
case STATE_NORMAL:
button.setBackgroundResource(R.drawable.button_recorder_normal);
button.setText(R.string.str_recorder_normal);
break;
case STATE_RECORDING:
button.setBackgroundResource(R.drawable.button_recorder_recording);
button.setText(R.string.str_recorder_recordering);
if (isRecording) {
mDialogManager.recording();
}
break;
case STATE_CANCEL:
button.setBackgroundResource(R.drawable.button_recorder_recording);
button.setText(R.string.str_recorder_want_to_cancel);
if (isRecording) {
mDialogManager.wantToCancel();
}
break;
default:
break;
}
}
}
}
2.3 dialogManager.java的编写
package com.recorderactivity;
import android.app.Dialog;
import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.ImageView;
import android.widget.TextView;
/**
* @ClassName:DialogManager
* @Description:TODO
* @author :吴雄峰
* @Date :2015 2015年5月22日 下午8:34:32
*
*/
public class DialogManager {
private Dialog dialog;
private ImageView mIcon, mVoice;
private TextView mLable;
@SuppressWarnings("unused")
private Context mContext;
/**
* @Description:TODO
* @author :吴雄峰
* @Date : 2015年5月22日 下午9:16:24
*
*/
public DialogManager(Context context) {
this.mContext = context;
}
public void showRecordingDialog() {
dialog = new Dialog(mContext, R.style.Theme_AudioDialog);
LayoutInflater mInflater = LayoutInflater.from(mContext);
View view = mInflater.inflate(R.layout.dialog_recorder, null);
dialog.setContentView(view);
mIcon = (ImageView) dialog.findViewById(R.id.id_dialog_recorder_icon);
mVoice = (ImageView) dialog.findViewById(R.id.id_dialog_recorder_voice);
mLable = (TextView) dialog.findViewById(R.id.id_recorder_dialog_label);
dialog.show();
}
public void recording() {
if (dialog != null && dialog.isShowing()) {
mIcon.setVisibility(View.VISIBLE);
mVoice.setVisibility(View.VISIBLE);
mLable.setVisibility(View.VISIBLE);
mIcon.setImageResource(R.drawable.recorder);
mLable.setText("手指上滑,取消发送");
}
}
public void wantToCancel() {
if (dialog != null && dialog.isShowing()) {
mIcon.setVisibility(View.VISIBLE);
mVoice.setVisibility(View.GONE);
mLable.setVisibility(View.VISIBLE);
mIcon.setImageResource(R.drawable.cancel);
mLable.setText("松开手指,取消发送");
}
}
public void tooShort() {
if (dialog != null && dialog.isShowing()) {
mIcon.setVisibility(View.VISIBLE);
mVoice.setVisibility(View.GONE);
mLable.setVisibility(View.VISIBLE);
mIcon.setImageResource(R.drawable.voice_to_short);
mLable.setText("录音时间过短");
}
}
public void dimissDialog() {
if (dialog != null && dialog.isShowing()) {
dialog.dismiss();
dialog = null;
}
}
/**
*
* @Description:通过Level去更新图片
* @author :吴雄峰
*
*/
public void updateVoiceLevel(int level) {
if (dialog != null && dialog.isShowing()) {
int resId = mContext.getResources().getIdentifier("v" + level,
"drawable", mContext.getPackageName());
mVoice.setImageResource(resId);
}
}
}
2.4 AudioManager.java 的编写
package com.recorderactivity;
import java.io.File;
import java.io.IOException;
import java.util.UUID;
import android.media.MediaRecorder;
/**
* @ClassName:AudioManager
* @Description:TODO
* @author :吴雄峰
* @Date :2015 2015年5月23日 下午10:17:45
*
*/
public class AudioManager {
private MediaRecorder mMediaRecorder;
private String mDir;
private String mCurrentPath;
private static AudioManager mInstance;
private String mCurrentFilePath;// 文件路径
private boolean isPrepared;
public AudioManager(String dir) {
mDir = dir;
}
public interface AudioStateListener {
void wellPrepared();
}
public AudioStateListener mListener;
public void setOnAudioStateListener(AudioStateListener listener) {
mListener = listener;
}
public static AudioManager getInstance(String dir) {
if (mInstance == null) {
synchronized (AudioManager.class) {
if (mInstance == null) {
mInstance = new AudioManager(dir);
}
}
}
return mInstance;
}
// 准备
public void prepareAudio() {
try {
isPrepared = false;
// 创建文件夹
File dir = new File(mDir);
if (!dir.exists()) {
dir.mkdirs();
}
String fileName = generateFileName();
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.AMR_NB);
// 设置音频编码
mMediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB);
mMediaRecorder.prepare();
mMediaRecorder.start();
// 准备结束
isPrepared = true;
if (mListener != null) {
mListener.wellPrepared();
}
} catch (IllegalStateException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* @Description:随机生成文件的名称
* @author :吴雄峰
*
*/
private String generateFileName() {
return UUID.randomUUID().toString() + ".amr";
}
public int getVoiceLevel(int maxLevel) {
if (isPrepared) {
try {
// getMaxAmplitude() 1-32767
return maxLevel * mMediaRecorder.getMaxAmplitude() / 32768 + 1;
} catch (Exception e) {
// TODO: handle exception
}
}
return 1;
}
public void release() {
mMediaRecorder.stop();
mMediaRecorder.release();
mMediaRecorder = null;
}
public void cancel() {
release();
if (mCurrentFilePath != null) {
File file = new File(mCurrentFilePath);
file.delete();
mCurrentFilePath = null;
}
}
/**
* @Description:
* @author :吴雄峰
*
*/
public String getCurrentFilePath() {
// TODO 自动生成的方法存根
return mCurrentFilePath;
}
}
2.5 MediaManager.java的编写
package com.recorderactivity;
import java.io.IOException;
import android.media.AudioManager;
import android.media.MediaPlayer;
import android.media.MediaPlayer.OnCompletionListener;
import android.media.MediaPlayer.OnErrorListener;
/**
* @ClassName:MediaManager
* @Description:TODO
* @author :吴雄峰
* @Date :2015 2015年5月24日 下午3:20:14
*
*/
public class MediaManager {
private static MediaPlayer mMediaPlayer;
private static boolean isPause;
public static void playSound(String filePath, OnCompletionListener listener) {
if (mMediaPlayer == null) {
mMediaPlayer = new MediaPlayer();
mMediaPlayer.setOnErrorListener(new OnErrorListener() {
@Override
public boolean onError(MediaPlayer mp, int what, int extra) {
mMediaPlayer.reset();
return false;
}
});
} else {
mMediaPlayer.reset();
}
try {
mMediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
mMediaPlayer.setOnCompletionListener(listener);
mMediaPlayer.setDataSource(filePath);
mMediaPlayer.prepare();
mMediaPlayer.start();
} catch (IllegalArgumentException e) {
// TODO 自动生成的 catch 块
e.printStackTrace();
} catch (SecurityException e) {
// TODO 自动生成的 catch 块
e.printStackTrace();
} catch (IllegalStateException e) {
// TODO 自动生成的 catch 块
e.printStackTrace();
} catch (IOException e) {
// TODO 自动生成的 catch 块
e.printStackTrace();
}
}
// 暂停
public static void pause() {
if (mMediaPlayer != null && mMediaPlayer.isPlaying()) {
mMediaPlayer.pause();
isPause = true;
}
}
// 继续播放
public static void resume() {
if (mMediaPlayer != null && isPause) {
mMediaPlayer.start();
isPause = false;
}
}
// 释放
public static void release() {
if (mMediaPlayer != null) {
mMediaPlayer.release();
mMediaPlayer = null;
}
}
}
2.6 ViewHolder.java 的编写 (封装了,直接贴起来用,不用每次在adapter中写viewholder)
package com.recorderactivity;
import android.R.integer;
import android.content.Context;
import android.graphics.Bitmap;
import android.util.SparseArray;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;
/**
* @ClassName:Viewholder
* @Description:TODO viewholder基类
* @author :吴雄峰
* @Date :2015 2015年5月14日 下午3:51:59
*
*/
public class Viewholder {
private SparseArray<View> mViews;
private int mPosition;
private View mConvertView;
/**
* @Description:TODO
* @author :吴雄峰
* @Date : 2015年5月14日 下午3:53:37
*
*/
public Viewholder(Context context, ViewGroup parent, int layoutId,
int position) {
this.mPosition = position;
this.mViews = new SparseArray<View>();
mConvertView = LayoutInflater.from(context).inflate(layoutId, parent,
false);
mConvertView.setTag(this);
}
/**
*
* @Description:初始化viewholder
* @author :吴雄峰
*
*/
public static Viewholder get(Context context, View convertView,
ViewGroup parent, int layoutId, int position) {
if (convertView == null) {
return new Viewholder(context, parent, layoutId, position);
} else {
Viewholder viewholder = (Viewholder) convertView.getTag();
viewholder.mPosition=position;
return viewholder;
}
}
/**
*
* @Description:通过viewID获取控件
* @author :吴雄峰
*
*/
public <T extends View> T getView(int viewId) {
View view = mViews.get(viewId);
if (view == null) {
view = mConvertView.findViewById(viewId);
mViews.put(viewId, view);
}
return (T) view;
}
public View getConvertView() {
return mConvertView;
}
/**
*
* @Description:设置textview 的值
* @author :吴雄峰
*
*/
public Viewholder setText(int viewId,String text)
{
TextView tv=getView(viewId);
tv.setText(text);
return this;
}
/**
*
* @Description:用setImageResource方法设置imageview的图片
* @author :吴雄峰
*
*/
public Viewholder setImageViewResource(int viewId,int resId)
{
ImageView imageView=getView(viewId);
imageView.setImageResource(resId);
return this;
}
/**
*
* @Description:用setImageBitmap方法设置imageview的图片
* @author :吴雄峰
*
*/
public Viewholder setImageViewBitmap(int viewId,Bitmap bitmap)
{
ImageView imageView=getView(viewId);
imageView.setImageBitmap(bitmap);
return this;
}
public Viewholder setImageViewUri(int viewId,String uri)
{
ImageView imageView=getView(viewId);
//imageLoader.getInstance().load(view,uri);
return this;
}
}
2.7 listview 适配器的编写
package com.recorderactivity;
import java.util.List;
import android.content.Context;
import android.util.DisplayMetrics;
import android.view.View;
import android.view.ViewGroup;
import android.view.Window;
import android.view.WindowManager;
import android.widget.BaseAdapter;
/**
* @ClassName:ListViewAdapter
* @Description:TODO
* @author :吴雄峰
* @Date :2015 2015年5月24日 下午1:12:35
*
*/
public class ListViewAdapter extends BaseAdapter {
private int mMinItemWidth;
private int mMaxItemWidth;
private Context mContext;
private List<RecorderEntity> mDatas;
public ListViewAdapter(Context context, List<RecorderEntity> data) {
// TODO 自动生成的构造函数存根
this.mContext = context;
this.mDatas = data;
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);
}
/**
* @author :吴雄峰
* @Date : 2015年5月24日 下午2:33:19
* @Description:
*/
@Override
public int getCount() {
// TODO 自动生成的方法存根
return mDatas.size();
}
/**
* @author :吴雄峰
* @Date : 2015年5月24日 下午2:33:19
* @Description:
*/
@Override
public Object getItem(int position) {
// TODO 自动生成的方法存根
return mDatas.get(position);
}
/**
* @author :吴雄峰
* @Date : 2015年5月24日 下午2:33:19
* @Description:
*/
@Override
public long getItemId(int position) {
// TODO 自动生成的方法存根
return position;
}
/**
* @author :吴雄峰
* @Date : 2015年5月24日 下午2:33:19
* @Description:
*/
@Override
public View getView(int position, View convertView, ViewGroup parent) {
Viewholder viewholder = new Viewholder(mContext, parent,
R.layout.item_recorder, position);
viewholder.setText(R.id.id_recorder_time,
Math.round(mDatas.get(position).getmTime()) + "\"");
//控制长度
View lengh = viewholder.getView(R.id.id_recorder_lengh);
ViewGroup.LayoutParams lp = lengh.getLayoutParams();
lp.width = (int) (mMinItemWidth + mMaxItemWidth / 60f
* mDatas.get(position).getmTime());
return viewholder.getConvertView();
}
}
2.8 listview的实体类编写
package com.recorderactivity;
/**
* @ClassName:RecorderEntity
* @Description:TODO
* @author :吴雄峰
* @Date :2015 2015年5月24日 下午1:07:53
*
*/
public class RecorderEntity {
private float mTime;
private String mFilePath;
/**
* @Description:TODO
* @author :吴雄峰
* @Date : 2015年5月24日 下午1:09:12
*
*/
public RecorderEntity(float time, String filePath) {
super();
this.mFilePath = filePath;
this.mTime = time;
}
/**
* @return mTime
*/
public float getmTime() {
return mTime;
}
/**
* @param mTime
* 要设置的 mTime
*/
public void setmTime(float mTime) {
this.mTime = mTime;
}
/**
* @return mFilePath
*/
public String getmFilePath() {
return mFilePath;
}
/**
* @param mFilePath
* 要设置的 mFilePath
*/
public void setmFilePath(String mFilePath) {
this.mFilePath = mFilePath;
}
}
dialog的布局编写
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
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_dialog_recorder_icon"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/recorder"
android:visibility="visible" />
<ImageView
android:id="@+id/id_dialog_recorder_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_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="5dp"
android:text="手指上滑,取消发送"
android:textColor="#FFFFFF"
android:visibility="visible" />
</LinearLayout>
listview的item编写
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/RelativeLayout1"
android:layout_width="match_parent"
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_lengh"
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_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:layout_marginRight="3dp"
android:layout_toLeftOf="@id/id_recorder_lengh"
android:text=""
android:textColor="#777777" />
</RelativeLayout>
话说源代码怎么上传。有需要的朋友找我要把,QQ: 540886047
- 仿微信语音语音聊天
- Android 仿微信语音聊天
- Android仿微信语音聊天
- Android仿微信语音聊天
- 仿微信语音聊天
- 仿微信语音聊天2
- android 仿微信语音聊天
- 仿微信语音聊天
- 仿微信语音聊天
- 语音聊天
- 语音聊天
- 语音聊天
- Android仿微信语音聊天demo
- Android仿微信语音聊天功能
- Android仿微信语音聊天界面
- Android仿微信语音聊天界面
- Android录音播放工具类,仿微信语音聊天。
- Android仿微信语音聊天(一)
- 解决dns没问题,能上qq不能上网的问题
- 将Excel表数据导入Sqlite数据库表
- 欧拉函数模板
- 一种排序
- 在ios平台上编译DCMTK静态库
- 仿微信语音语音聊天
- [Unity基础]让物体朝摄像机观察方向移动(一)
- UVA 10791 Minimum Sum LCM(素因子)
- 微信企业号之新增用户
- linux命令大全
- uva 10341 - Solve It(二分)
- JavaScript几个比较事件触发方式的记录
- 多线程通信之优化升级+JDK1.5升级版后的新方案
- findstr()与strfind()的区别