最近做了一套及時通訊軟體,其中很多功能和微信是相仿的,下面詳細介紹一下具體實作。
做及時通訊肯定要用xmpp協定,微信和一些及時通訊軟體也是用的這套協定,隻是縱向開發深度不同。
1.複寫語音按鈕
@SuppressLint("NewApi")
public class RecordButton extends Button {
public RecordButton(Context context) {
super(context);
init();
}
public RecordButton(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
public RecordButton(Context context, AttributeSet attrs) {
super(context, attrs);
public void setSavePath(String path) {
mFileName = path;
public void setOnFinishedRecordListener(OnFinishedRecordListener listener) {
finishedListener = listener;
private String mFileName = null;
private OnFinishedRecordListener finishedListener;
private static final int MIN_INTERVAL_TIME = 2000;// 2s
private long startTime;
/**
* 取消語音發送
*/
private Dialog recordIndicator;
private static int[] res = { R.drawable.mic_2, R.drawable.mic_3,
R.drawable.mic_4, R.drawable.mic_5 };
private static ImageView view;
private MediaRecorder recorder;
private ObtainDecibelThread thread;
private Handler volumeHandler;
public final static int MAX_TIME =60;//一分鐘
private void init() {
volumeHandler = new ShowVolumeHandler();
@Override
public boolean onTouchEvent(MotionEvent event) {
if (mFileName == null)
return false;
int action = event.getAction();
switch (action) {
case MotionEvent.ACTION_DOWN:
setText("松開發送");
initDialogAndStartRecord();
break;
case MotionEvent.ACTION_UP:
this.setText("按住錄音");
finishRecord();
case MotionEvent.ACTION_CANCEL:// 當手指移動到view外面,會cancel
cancelRecord();
Toast.makeText(getContext(), "cancel", 1).show();
return true;
private void initDialogAndStartRecord() {
startTime = System.currentTimeMillis();
recordIndicator = new Dialog(getContext(),
R.style.like_toast_dialog_style);
view = new ImageView(getContext());
view.setImageResource(R.drawable.mic_2);
recordIndicator.setContentView(view, new LayoutParams(
ViewGroup.LayoutParams.WRAP_CONTENT,
ViewGroup.LayoutParams.WRAP_CONTENT));
recordIndicator.setOnDismissListener(onDismiss);
LayoutParams lp = recordIndicator.getWindow().getAttributes();
lp.gravity = Gravity.CENTER;
startRecording();
recordIndicator.show();
private void finishRecord() {
stopRecording();
recordIndicator.dismiss();
long intervalTime = System.currentTimeMillis() - startTime;
if (intervalTime < MIN_INTERVAL_TIME) {
Toast.makeText(getContext(), "時間太短!", Toast.LENGTH_SHORT).show();
File file = new File(mFileName);
file.delete();
return;
if (finishedListener != null)
finishedListener.onFinishedRecord(mFileName,(int) (intervalTime/1000));
private void cancelRecord() {
Toast.makeText(getContext(), "取消錄音!", Toast.LENGTH_SHORT).show();
private void startRecording() {
recorder = new MediaRecorder();
recorder.setAudioSource(MediaRecorder.AudioSource.MIC);
recorder.setAudioChannels(1);
recorder.setAudioEncodingBitRate(4000);
recorder.setOutputFormat(MediaRecorder.OutputFormat.AMR_NB);
recorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB);
//recorder.setVideoFrameRate(4000);
recorder.setOutputFile(mFileName);
try {
recorder.prepare();
} catch (IOException e) {
e.printStackTrace();
recorder.start();
thread = new ObtainDecibelThread();
thread.start();
private void stopRecording() {
if (thread != null) {
thread.exit();
thread = null;
if (recorder != null) {
recorder.stop();
recorder.release();
recorder = null;
private class ObtainDecibelThread extends Thread {
private volatile boolean running = true;
public void exit() {
running = false;
public void run() {
while (running) {
Thread.sleep(200);
} catch (InterruptedException e) {
if (recorder == null || !running) {
int x = recorder.getMaxAmplitude();
if (x != 0) {
int f = (int) (10 * Math.log(x) / Math.log(10));
if (f < 26)
volumeHandler.sendEmptyMessage(0);
else if (f < 32)
volumeHandler.sendEmptyMessage(1);
else if (f < 38)
volumeHandler.sendEmptyMessage(2);
else
volumeHandler.sendEmptyMessage(3);
private OnDismissListener onDismiss = new OnDismissListener() {
public void onDismiss(DialogInterface dialog) {
};
static class ShowVolumeHandler extends Handler {
public void handleMessage(Message msg) {
view.setImageResource(res[msg.what]);
public interface OnFinishedRecordListener {
public void onFinishedRecord(String audioPath,int time);
//private boolean
2.文字,語音,檔案發送接收
public class ChatActivity extends Activity {
private String userChat = "";// 目前聊天 userChat
private String userChatSendFile = "";// 給誰發檔案
private ChatListAdapter adapter;
private List<Msg> listMsg = new LinkedList<Msg>();
private String pUSERID;// 自己的user
private String pFRIENDID;// 視窗的 名稱
private EditText msgText;
private TextView chat_name;
private NotificationManager mNotificationManager;
private ChatManager cm;
private RecordButton mRecordButton;
// 發送檔案
private OutgoingFileTransfer sendTransfer;
public static String FILE_ROOT_PATH = Environment
.getExternalStorageDirectory().getPath() + "/chat/file";
public static String RECORD_ROOT_PATH = Environment
.getExternalStorageDirectory().getPath() + "/chat/record";
Chat newchat;
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_NO_TITLE);
setContentView(R.layout.chat_client);
mRecordButton = (RecordButton) findViewById(R.id.record_button);
String path = RECORD_ROOT_PATH;
File file = new File(path);
file.mkdirs();
path += "/" + System.currentTimeMillis() + ".amr";
mRecordButton.setSavePath(path);
mRecordButton
.setOnFinishedRecordListener(new OnFinishedRecordListener() {
public void onFinishedRecord(String audioPath, int time) {
Log.i("RECORD!!!", "finished!!!!!!!!!! save to "
+ audioPath);
if (audioPath != null) {
// 自己顯示消息
Msg myChatMsg = new Msg(pUSERID, time + "”語音消息",
TimeRender.getDate(), Msg.FROM_TYPE[1],
Msg.TYPE[0], Msg.STATUS[3], time + "",
audioPath);
listMsg.add(myChatMsg);
String[] pathStrings = audioPath.split("/"); // 檔案名
//發送 對方的消息
String fileName = null ;
if (pathStrings!=null && pathStrings.length>0) {
fileName = pathStrings[pathStrings.length-1];
Msg sendChatMsg = new Msg(pUSERID, time + "”語音消息",
TimeRender.getDate(), Msg.FROM_TYPE[0],
fileName);
// 重新整理擴充卡
adapter.notifyDataSetChanged();
// 發送消息
newchat.sendMessage(Msg.toJson(sendChatMsg));
sendFile(audioPath, myChatMsg);//
} catch (Exception e) {
} else {
Toast.makeText(ChatActivity.this, "發送失敗",
Toast.LENGTH_SHORT).show();
});
mNotificationManager = (NotificationManager) this
.getSystemService(Service.NOTIFICATION_SERVICE);
// 擷取Intent傳過來的使用者名
this.pUSERID = getIntent().getStringExtra("USERID");
this.userChat = getIntent().getStringExtra("user");/*
userChatSendFile = userChat + "/" + FriendListActivity.MY_RESOUCE_NAME;
this.pFRIENDID = getIntent().getStringExtra("FRIENDID");
chat_name = (TextView) findViewById(R.id.chat_name);
chat_name.setText(pFRIENDID);
ListView listview = (ListView) findViewById(R.id.formclient_listview);
listview.setTranscriptMode(ListView.TRANSCRIPT_MODE_ALWAYS_SCROLL);
this.adapter = new ChatListAdapter(this, listMsg);
listview.setAdapter(adapter);
// 擷取文本資訊
this.msgText = (EditText) findViewById(R.id.formclient_text);
// 消息監聽
cm = XmppConnection.getConnection().getChatManager();
// 傳回按鈕
Button mBtnBack = (Button) findViewById(R.id.chat_back);
mBtnBack.setOnClickListener(new OnClickListener() {
public void onClick(View arg0) {
finish();
receivedMsg();// 接收消息
sendMsg();// 發送消息
receivedFile();// 接收檔案
* 接收消息
public void receivedMsg() {
cm.addChatListener(new ChatManagerListener() {
public void chatCreated(Chat chat, boolean able) {
chat.addMessageListener(new MessageListener() {
public void processMessage(Chat chat2, Message message) {
// 收到來自pc伺服器的消息(擷取自己好友發來的資訊)
if (message.getFrom().contains(userChat)) {
// Msg.analyseMsgBody(message.getBody(),userChat);
// 擷取使用者、消息、時間、IN
// 在handler裡取出來顯示消息
android.os.Message msg = handler.obtainMessage();
System.out.println("伺服器發來的消息是 chat:"
+ message.getBody());
msg.what = 1;
msg.obj = message.getBody();
msg.sendToTarget();
* 發送消息
*
* @author Administrator
public void sendMsg() {
Button btsend = (Button) findViewById(R.id.formclient_btsend);
// 發送消息給pc伺服器的好友(擷取自己的伺服器,和好友)
newchat = cm.createChat(userChat, null);
btsend.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
// 擷取text文本
final String msg = msgText.getText().toString();
if (msg.length() > 0) {
Msg chatMsg = new Msg(pUSERID, msg, TimeRender.getDate(),
Msg.FROM_TYPE[1]);
listMsg.add(chatMsg);
//發送對方
Msg sendChatMsg = new Msg(pUSERID, msg, TimeRender.getDate(),
Msg.FROM_TYPE[0]);
Toast.makeText(ChatActivity.this, "發送資訊不能為空",
// 清空text
msgText.setText("");
* 接收檔案
public void receivedFile() {
// Create the file transfer manager
final FileTransferManager manager = new FileTransferManager(
XmppConnection.getConnection());
// Create the listener
manager.addFileTransferListener(new FileTransferListener() {
public void fileTransferRequest(FileTransferRequest request) {
// Check to see if the request should be accepted
Log.d("receivedFile ", " receive file");
if (shouldAccept(request)) {
// Accept it
IncomingFileTransfer transfer = request.accept();
System.out.println(request.getFileName());
File file = new File(RECORD_ROOT_PATH
+ request.getFileName());
transfer.recieveFile(file);
Msg msgInfo = queryMsgForListMsg(file.getName());
msgInfo.setFilePath(file.getPath());//更新 filepath
new MyFileStatusThread(transfer, msgInfo).start();
} catch (XMPPException e) {
// Reject it
request.reject();
String[] args = new String[] { userChat,
request.getFileName(), TimeRender.getDate(), "IN",
Msg.TYPE[0], Msg.STATUS[1] };
Msg msgInfo = new Msg(args[0], "redio", args[2], args[3],
Msg.TYPE[0], Msg.STATUS[1]);
msg.what = 5;
msg.obj = msgInfo;
handler.sendMessage(msg);
* 發送檔案
* @param path
public void sendFile(String path, Msg msg) {
FileTransferManager sendFilemanager = new FileTransferManager(
// Create the outgoing file transfer
sendTransfer = sendFilemanager
.createOutgoingFileTransfer(userChatSendFile);
// Send the file
sendTransfer.sendFile(new java.io.File(path), "send file");
new MyFileStatusThread(sendTransfer, msg).start();
* 監聽
class MyFileStatusThread extends Thread {
private FileTransfer transfer;
private Msg msg;
public MyFileStatusThread(FileTransfer tf, Msg msg) {
transfer = tf;
this.msg = msg;
System.out.println(transfer.getStatus());
System.out.println(transfer.getProgress());
android.os.Message message = new android.os.Message();// handle
message.what = 3;
while (!transfer.isDone()) {
Thread.sleep(1000);
// TODO Auto-generated catch block
if (transfer.getStatus().equals(Status.error)) {
msg.setReceive(Msg.STATUS[2]);
} else if (transfer.getStatus().equals(Status.refused)) {
msg.setReceive(Msg.STATUS[1]);
msg.setReceive(Msg.STATUS[0]);// 成功
handler.sendMessage(message);
/*
* System.out.println(transfer.getStatus());
* System.out.println(transfer.getProgress());
private Handler handler = new Handler() {
public void handleMessage(android.os.Message msg) {
switch (msg.what) {
case 1:
Msg chatMsg = Msg.analyseMsgBody(msg.obj.toString());
if (chatMsg != null) {
listMsg.add(chatMsg);// 添加到聊天消息
case 2: // 發送檔案
case 3: // 更新檔案發送狀态
case 5: // 接收檔案
Msg msg2 = (Msg) msg.obj;
System.out.println(msg2.getFrom());
listMsg.add(msg2);
default:
public void onBackPressed() {
super.onBackPressed();
// XmppConnection.closeConnection();
System.exit(0);
protected void setNotiType(int iconId, String s) {
Intent intent = new Intent();
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
PendingIntent appIntent = PendingIntent.getActivity(this, 0, intent, 0);
Notification myNoti = new Notification();
myNoti.icon = iconId;
myNoti.tickerText = s;
myNoti.defaults = Notification.DEFAULT_SOUND;
myNoti.flags |= Notification.FLAG_AUTO_CANCEL;
myNoti.setLatestEventInfo(this, "QQ消息", s, appIntent);
mNotificationManager.notify(0, myNoti);
* 是否接收
* @param request
* @return
private boolean shouldAccept(FileTransferRequest request) {
final boolean isAccept[] = new boolean[1];
protected void dialog() {
* init file
static {
File root = new File(FILE_ROOT_PATH);
root.mkdirs();// 沒有根目錄建立根目錄
root = new File(RECORD_ROOT_PATH);
root.mkdirs();
* 從list 中取出 分揀名稱相同的 Msg
private Msg queryMsgForListMsg(String filePath){
Msg msg = null;
for (int i = listMsg.size()-1; i>=0; i--) {
msg = listMsg.get(i);
if (filePath!=null && filePath.contains(msg.getFilePath()) ) {// 對方傳過來的隻是檔案的名稱
return msg;
如需整個項目的留下郵箱位址。
轉載注明出處。