1. 程式人生 > >Android 實現單聊功能_At_Swim

Android 實現單聊功能_At_Swim

這個單聊特別特別好用

在這裡分享給大家


專案的 Application類,做一些專案的初始化操作,比如sdk的初始化等

public class ECApplication extends Application {

    // 上下文選單
    private Context mContext;

    // 記錄是否已經初始化
    private boolean isInit = false;

    @Override
    public void onCreate() {
        super.onCreate();
        mContext = this
; // 初始化環信SDK initEasemob(); } /** * */ private void initEasemob() { // 獲取當前程序 id 並取得程序名 int pid = android.os.Process.myPid(); String processAppName = getAppName(pid); /** * 如果app啟用了遠端的service,此application:onCreate會被呼叫2次 * 為了防止環信SDK被初始化2次,加此判斷會保證SDK被初始化1次
* 預設的app會在以包名為預設的process name下執行,如果查到的process name不是app的process name就立即返回 */ if (processAppName == null || !processAppName.equalsIgnoreCase(mContext.getPackageName())) { // 則此application的onCreate 是被service 呼叫的,直接返回 return; } if (isInit) { return
; } // 呼叫初始化方法初始化sdk EMClient.getInstance().init(mContext, initOptions()); // 設定開啟debug模式 EMClient.getInstance().setDebugMode(true); // 設定初始化已經完成 isInit = true; } /** * SDK初始化的一些配置 * 關於 EMOptions 可以參考官方的 API 文件 * http://www.easemob.com/apidoc/android/chat3.0/classcom_1_1hyphenate_1_1chat_1_1_e_m_options.html */ private EMOptions initOptions() { EMOptions options = new EMOptions(); // 設定Appkey,如果配置檔案已經配置,這裡可以不用設定 // options.setAppKey("lzan13#hxsdkdemo"); // 設定自動登入 options.setAutoLogin(true); // 設定是否需要傳送已讀回執 options.setRequireAck(true); // 設定是否需要傳送回執, options.setRequireDeliveryAck(true); // 設定是否需要伺服器收到訊息確認 options.setRequireServerAck(true); // 設定是否根據伺服器時間排序,預設是true options.setSortMessageByServerTime(false); // 收到好友申請是否自動同意,如果是自動同意就不會收到好友請求的回撥,因為sdk會自動處理,預設為true options.setAcceptInvitationAlways(false); // 設定是否自動接收加群邀請,如果設定了當收到群邀請會自動同意加入 options.setAutoAcceptGroupInvitation(false); // 設定(主動或被動)退出群組時,是否刪除群聊聊天記錄 options.setDeleteMessagesAsExitGroup(false); // 設定是否允許聊天室的Owner 離開並刪除聊天室的會話 options.allowChatroomOwnerLeave(true); // 設定google GCM推送id,國內可以不用設定 // options.setGCMNumber(MLConstants.ML_GCM_NUMBER); // 設定整合小米推送的appid和appkey // options.setMipushConfig(MLConstants.ML_MI_APP_ID, MLConstants.ML_MI_APP_KEY); return options; } /** * 根據Pid獲取當前程序的名字,一般就是當前app的包名 * * @param pid 程序的id * @return 返回程序的名字 */ private String getAppName(int pid) { String processName = null; ActivityManager activityManager = (ActivityManager) mContext.getSystemService(Context.ACTIVITY_SERVICE); List list = activityManager.getRunningAppProcesses(); Iterator i = list.iterator(); while (i.hasNext()) { ActivityManager.RunningAppProcessInfo info = (ActivityManager.RunningAppProcessInfo) (i.next()); try { if (info.pid == pid) { // 根據程序的資訊獲取當前程序的名字 processName = info.processName; // 返回當前程序名 return processName; } } catch (Exception e) { e.printStackTrace(); } } // 沒有匹配的項,返回為null return null; } }


public class ECChatActivity extends AppCompatActivity implements EMMessageListener {

    // 聊天資訊輸入框
    private EditText mInputEdit;
    // 傳送按鈕
    private Button mSendBtn;

    // 顯示內容的 TextView
    private TextView mContentText;

    // 訊息監聽器
    private EMMessageListener mMessageListener;
    // 當前聊天的 ID
    private String mChatId;
    // 當前會話物件
    private EMConversation mConversation;


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_chat);

        // 獲取當前會話的username(如果是群聊就是群id)
        mChatId = getIntent().getStringExtra("ec_chat_id");
        mMessageListener = this;

        initView();
        initConversation();
    }

    /**
     * 初始化介面
     */
    private void initView() {
        mInputEdit = (EditText) findViewById(R.id.ec_edit_message_input);
        mSendBtn = (Button) findViewById(R.id.ec_btn_send);
        mContentText = (TextView) findViewById(R.id.ec_text_content);
        // 設定textview可滾動,需配合xml佈局設定
        mContentText.setMovementMethod(new ScrollingMovementMethod());

        // 設定傳送按鈕的點選事件
        mSendBtn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                String content = mInputEdit.getText().toString().trim();
                if (!TextUtils.isEmpty(content)) {
                    mInputEdit.setText("");
                    // 建立一條新訊息,第一個引數為訊息內容,第二個為接受者username
                    EMMessage message = EMMessage.createTxtSendMessage(content, mChatId);
                    // 將新的訊息內容和時間加入到下邊
                    mContentText.setText(mContentText.getText() + "\n傳送:" + content + " - time: " + message.getMsgTime());
                    // 呼叫傳送訊息的方法
                    EMClient.getInstance().chatManager().sendMessage(message);
                    // 為訊息設定回撥
                    message.setMessageStatusCallback(new EMCallBack() {
                        @Override
                        public void onSuccess() {
                            // 訊息傳送成功,列印下日誌,正常操作應該去重新整理ui
                            Log.i("lzan13", "send message on success");
                        }

                        @Override
                        public void onError(int i, String s) {
                            // 訊息傳送失敗,列印下失敗的資訊,正常操作應該去重新整理ui
                            Log.i("lzan13", "send message on error " + i + " - " + s);
                        }

                        @Override
                        public void onProgress(int i, String s) {
                            // 訊息傳送進度,一般只有在傳送圖片和檔案等訊息才會有回撥,txt不回撥
                        }
                    });
                }
            }
        });
    }

    /**
     * 初始化會話物件,並且根據需要載入更多訊息
     */
    private void initConversation() {

        /**
         * 初始化會話物件,這裡有三個引數麼,
         * 第一個表示會話的當前聊天的 useranme 或者 groupid
         * 第二個是繪畫型別可以為空
         * 第三個表示如果會話不存在是否建立
         */
        mConversation = EMClient.getInstance().chatManager().getConversation(mChatId, null, true);
        // 設定當前會話未讀數為 0
        mConversation.markAllMessagesAsRead();
        int count = mConversation.getAllMessages().size();
        if (count < mConversation.getAllMsgCount() && count < 20) {
            // 獲取已經在列表中的最上邊的一條訊息id
            String msgId = mConversation.getAllMessages().get(0).getMsgId();
            // 分頁載入更多訊息,需要傳遞已經載入的訊息的最上邊一條訊息的id,以及需要載入的訊息的條數
            mConversation.loadMoreMsgFromDB(msgId, 20 - count);
        }
        // 開啟聊天介面獲取最後一條訊息內容並顯示
        if (mConversation.getAllMessages().size() > 0) {
            EMMessage messge = mConversation.getLastMessage();
            EMTextMessageBody body = (EMTextMessageBody) messge.getBody();
            // 將訊息內容和時間顯示出來
            mContentText.setText("聊天記錄:" + body.getMessage() + " - time: " + mConversation.getLastMessage().getMsgTime());
        }
    }

    /**
     * 自定義實現Handler,主要用於重新整理UI操作
     */
    Handler mHandler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
            case 0:
                EMMessage message = (EMMessage) msg.obj;
                // 這裡只是簡單的demo,也只是測試文字訊息的收發,所以直接將body轉為EMTextMessageBody去獲取內容
                EMTextMessageBody body = (EMTextMessageBody) message.getBody();
                // 將新的訊息內容和時間加入到下邊
                mContentText.setText(mContentText.getText() + "\n接收:" + body.getMessage() + " - time: " + message.getMsgTime());
                break;
            }
        }
    };

    @Override
    protected void onResume() {
        super.onResume();
        // 新增訊息監聽
        EMClient.getInstance().chatManager().addMessageListener(mMessageListener);
    }

    @Override
    protected void onStop() {
        super.onStop();
        // 移除訊息監聽
        EMClient.getInstance().chatManager().removeMessageListener(mMessageListener);
    }
    /**
     * --------------------------------- Message Listener -------------------------------------
     * 環信訊息監聽主要方法
     */
    /**
     * 收到新訊息
     *
     * @param list 收到的新訊息集合
     */
    @Override
    public void onMessageReceived(List<EMMessage> list) {
        // 迴圈遍歷當前收到的訊息
        for (EMMessage message : list) {
            if (message.getFrom().equals(mChatId)) {
                // 設定訊息為已讀
                mConversation.markMessageAsRead(message.getMsgId());

                // 因為訊息監聽回撥這裡是非ui執行緒,所以要用handler去更新ui
                Message msg = mHandler.obtainMessage();
                msg.what = 0;
                msg.obj = message;
                mHandler.sendMessage(msg);
            } else {
                // 如果訊息不是當前會話的訊息傳送通知欄通知
            }
        }
    }

    /**
     * 收到新的 CMD 訊息
     *
     * @param list
     */
    @Override
    public void onCmdMessageReceived(List<EMMessage> list) {
        for (int i = 0; i < list.size(); i++) {
            // 透傳訊息
            EMMessage cmdMessage = list.get(i);
            EMCmdMessageBody body = (EMCmdMessageBody) cmdMessage.getBody();
            Log.i("lzan13", body.action());
        }
    }

    /**
     * 收到新的已讀回執
     *
     * @param list 收到訊息已讀回執
     */
    @Override
    public void onMessageReadAckReceived(List<EMMessage> list) {
    }

    /**
     * 收到新的傳送回執
     * TODO 無效 暫時有bug
     *
     * @param list 收到傳送回執的訊息集合
     */
    @Override
    public void onMessageDeliveryAckReceived(List<EMMessage> list) {
    }

    /**
     * 訊息的狀態改變
     *
     * @param message 發生改變的訊息
     * @param object  包含改變的訊息
     */
    @Override
    public void onMessageChanged(EMMessage message, Object object) {
    }
}

對應的佈局

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
    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:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    tools:context="net.melove.demo.easechat.ECChatActivity">

    <!--輸入框-->
    <RelativeLayout
        android:id="@+id/ec_layout_input"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_alignParentBottom="true">

        <Button
            android:id="@+id/ec_btn_send"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_alignParentRight="true"
            android:text="Send"/>

        <EditText
            android:id="@+id/ec_edit_message_input"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_alignParentLeft="true"
            android:layout_toLeftOf="@id/ec_btn_send"/>
    </RelativeLayout>

    <!--展示訊息內容-->
    <TextView
        android:id="@+id/ec_text_content"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_above="@id/ec_layout_input"
        android:maxLines="15"
        android:scrollbars="vertical"/>
</RelativeLayout>
public class ECLoginActivity extends AppCompatActivity {

    // 彈出框
    private ProgressDialog mDialog;

    // username 輸入框
    private EditText mUsernameEdit;
    // 密碼輸入框
    private EditText mPasswordEdit;

    // 註冊按鈕
    private Button mSignUpBtn;
    // 登入按鈕
    private Button mSignInBtn;


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_login);

        initView();
    }

    /**
     * 初始化介面控制元件
     */
    private void initView() {
        mUsernameEdit = (EditText) findViewById(R.id.ec_edit_username);
        mPasswordEdit = (EditText) findViewById(R.id.ec_edit_password);

        mSignUpBtn = (Button) findViewById(R.id.ec_btn_sign_up);
        mSignUpBtn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                signUp();
            }
        });

        mSignInBtn = (Button) findViewById(R.id.ec_btn_sign_in);
        mSignInBtn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                signIn();
            }
        });
    }

    /**
     * 註冊方法
     */
    private void signUp() {
        // 註冊是耗時過程,所以要顯示一個dialog來提示下使用者
        mDialog = new ProgressDialog(this);
        mDialog.setMessage("註冊中,請稍後...");
        mDialog.show();

        new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    String username = mUsernameEdit.getText().toString().trim();
                    String password = mPasswordEdit.getText().toString().trim();
                    EMClient.getInstance().createAccount(username, password);
                    runOnUiThread(new Runnable() {
                        @Override
                        public void run() {
                            if (!ECLoginActivity.this.isFinishing()) {
                                mDialog.dismiss();
                            }
                            Toast.makeText(ECLoginActivity.this, "註冊成功", Toast.LENGTH_LONG).show();
                        }
                    });
                } catch (final HyphenateException e) {
                    e.printStackTrace();
                    runOnUiThread(new Runnable() {
                        @Override
                        public void run() {
                            if (!ECLoginActivity.this.isFinishing()) {
                                mDialog.dismiss();
                            }
                            /**
                             * 關於錯誤碼可以參考官方api詳細說明
                             * http://www.easemob.com/apidoc/android/chat3.0/classcom_1_1hyphenate_1_1_e_m_error.html
                             */
                            int errorCode = e.getErrorCode();
                            String message = e.getMessage();
                            Log.d("lzan13", String.format("sign up - errorCode:%d, errorMsg:%s", errorCode, e.getMessage()));
                            switch (errorCode) {
                                // 網路錯誤
                                case EMError.NETWORK_ERROR:
                                    Toast.makeText(ECLoginActivity.this, "網路錯誤 code: " + errorCode + ", message:" + message, Toast.LENGTH_LONG).show();
                                    break;
                                // 使用者已存在
                                case EMError.USER_ALREADY_EXIST:
                                    Toast.makeText(ECLoginActivity.this, "使用者已存在 code: " + errorCode + ", message:" + message, Toast.LENGTH_LONG).show();
                                    break;
                                // 引數不合法,一般情況是username 使用了uuid導致,不能使用uuid註冊
                                case EMError.USER_ILLEGAL_ARGUMENT:
                                    Toast.makeText(ECLoginActivity.this, "引數不合法,一般情況是username 使用了uuid導致,不能使用uuid註冊 code: " + errorCode + ", message:" + message, Toast.LENGTH_LONG).show();
                                    break;
                                // 伺服器未知錯誤
                                case EMError.SERVER_UNKNOWN_ERROR:
                                    Toast.makeText(ECLoginActivity.this, "伺服器未知錯誤 code: " + errorCode + ", message:" + message, Toast.LENGTH_LONG).show();
                                    break;
                                case EMError.USER_REG_FAILED:
                                    Toast.makeText(ECLoginActivity.this, "賬戶註冊失敗 code: " + errorCode + ", message:" + message, Toast.LENGTH_LONG).show();
                                    break;
                                default:
                                    Toast.makeText(ECLoginActivity.this, "ml_sign_up_failed code: " + errorCode + ", message:" + message, Toast.LENGTH_LONG).show();
                                    break;
                            }
                        }
                    });
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }).start();
    }

    /**
     * 登入方法
     */
    private void signIn() {

        mDialog = new ProgressDialog(this);
        mDialog.setMessage("正在登陸,請稍後...");
        mDialog.show();
        String username = mUsernameEdit.getText().toString().trim();
        String password = mPasswordEdit.getText().toString().trim();
        if (TextUtils.isEmpty(username) || TextUtils.isEmpty(password)) {
            Toast.makeText(ECLoginActivity.this, "使用者名稱和密碼不能為空", Toast.LENGTH_LONG).show();
            return;
        }
        EMClient.getInstance().login(username, password, new EMCallBack() {
            /**
             * 登陸成功的回撥
             */
            @Override
            public void onSuccess() {
                runOnUiThread(new Runnable() {
                    @Override
                    public void run() {
                        mDialog.dismiss();

                        // 載入所有會話到記憶體
                        EMClient.getInstance().chatManager().loadAllConversations();
                        // 載入所有群組到記憶體,如果使用了群組的話
                        // EMClient.getInstance().groupManager().loadAllGroups();

                        // 登入成功跳轉介面
                        Intent intent = new Intent(ECLoginActivity.this, ECMainActivity.class);
                        startActivity(intent);
                        finish();
                    }
                });
            }

            /**
             * 登陸錯誤的回撥
             * @param i
             * @param s
             */
            @Override
            public void onError(final int i, final String s) {
                runOnUiThread(new Runnable() {
                    @Override
                    public void run() {
                        mDialog.dismiss();
                        Log.d("lzan13", "登入失敗 Error code:" + i + ", message:" + s);
                        /**
                         * 關於錯誤碼可以參考官方api詳細說明
                         * http://www.easemob.com/apidoc/android/chat3.0/classcom_1_1hyphenate_1_1_e_m_error.html
                         */
                        switch (i) {
                        // 網路異常 2
                        case EMError.NETWORK_ERROR:
                            Toast.makeText(ECLoginActivity.this, "網路錯誤 code: " + i + ", message:" + s, Toast.LENGTH_LONG).show();
                            break;
                        // 無效的使用者名稱 101
                        case EMError.INVALID_USER_NAME:
                            Toast.makeText(ECLoginActivity.this, "無效的使用者名稱 code: " + i + ", message:" + s, Toast.LENGTH_LONG).show();
                            break;
                        // 無效的密碼 102
                        case EMError.INVALID_PASSWORD:
                            Toast.makeText(ECLoginActivity.this, "無效的密碼 code: " + i + ", message:" + s, Toast.LENGTH_LONG).show();
                            break;
                        // 使用者認證失敗,使用者名稱或密碼錯誤 202
                        case EMError.USER_AUTHENTICATION_FAILED:
                            Toast.makeText(ECLoginActivity.this, "使用者認證失敗,使用者名稱或密碼錯誤 code: " + i + ", message:" + s, Toast.LENGTH_LONG).show();
                            break;
                        // 使用者不存在 204
                        case EMError.USER_NOT_FOUND:
                            Toast.makeText(ECLoginActivity.this, "使用者不存在 code: " + i + ", message:" + s, Toast.LENGTH_LONG).show();
                            break;
                        // 無法訪問到伺服器 300
                        case EMError.SERVER_NOT_REACHABLE:
                            Toast.makeText(ECLoginActivity.this, "無法訪問到伺服器 code: " + i + ", message:" + s, Toast.LENGTH_LONG).show();
                            break;
                        // 等待伺服器響應超時 301
                        case EMError.SERVER_TIMEOUT:
                            Toast.makeText(ECLoginActivity.this, "等待伺服器響應超時 code: " + i + ", message:" + s, Toast.LENGTH_LONG).show();
                            break;
                        // 伺服器繁忙 302
                        case EMError.SERVER_BUSY:
                            Toast.makeText(ECLoginActivity.this, "伺服器繁忙 code: " + i + ", message:" + s, Toast.LENGTH_LONG).show();
                            break;
                        // 未知 Server 異常 303 一般斷網會出現這個錯誤
                        case EMError.SERVER_UNKNOWN_ERROR:
                            Toast.makeText(ECLoginActivity.this, "未知的伺服器異常 code: " + i + ", message:" + s, Toast.LENGTH_LONG).show();
                            break;
                        default:
                            Toast.makeText(ECLoginActivity.this, "ml_sign_in_failed code: " + i + ", message:" + s, Toast.LENGTH_LONG).show();
                            break;
                        }
                    }
                });
            }

            @Override
            public void onProgress(int i, String s) {

            }
        });
    }
}
對應佈局

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
    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:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    tools:context="net.melove.demo.easechat.ECLoginActivity">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical">

        <EditText
            android:id="@+id/ec_edit_username"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:hint="username"/>

        <EditText
            android:id="@+id/ec_edit_password"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:hint="password"/>

        <Button
            android:id="@+id/ec_btn_sign_up"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="註冊"/>

        <Button
            android:id="@+id/ec_btn_sign_in"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="登入"/>
    </LinearLayout>
</RelativeLayout>
public class ECMainActivity extends AppCompatActivity {

    // 發起聊天 username 輸入框
    private EditText mChatIdEdit;
    // 發起聊天
    private Button mStartChatBtn;
    // 退出登入
    private Button mSignOutBtn;


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        // 判斷sdk是否登入成功過,並沒有退出和被踢,否則跳轉到登陸介面
        if (!EMClient.getInstance().isLoggedInBefore()) {
            Intent intent = new Intent(ECMainActivity.this, ECLoginActivity.class);
            startActivity(intent);
            finish();
            return;
        }

        setContentView(R.layout.activity_main);

        initView();
    }

    /**
     * 初始化介面
     */
    private void initView() {

        mChatIdEdit = (EditText) findViewById(R.id.ec_edit_chat_id);

        mStartChatBtn = (Button) findViewById(R.id.ec_btn_start_chat);
        mStartChatBtn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                // 獲取我們發起聊天的者的username
                String chatId = mChatIdEdit.getText().toString().trim();
                if (!TextUtils.isEmpty(chatId)) {
                    // 獲取當前登入使用者的 username
                    String currUsername = EMClient.getInstance().getCurrentUser();
                    if (chatId.equals(currUsername)) {
                        Toast.makeText(ECMainActivity.this, "不能和自己聊天", Toast.LENGTH_SHORT).show();
                        return;
                    }
                    // 跳轉到聊天介面,開始聊天
                    Intent intent = new Intent(ECMainActivity.this, ECChatActivity.class);
                    intent.putExtra("ec_chat_id", chatId);
                    startActivity(intent);
                } else {
                    Toast.makeText(ECMainActivity.this, "Username 不能為空", Toast.LENGTH_LONG).show();
                }
            }
        });

        mSignOutBtn = (Button) findViewById(R.id.ec_btn_sign_out);
        mSignOutBtn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                signOut();
            }
        });
    }

    /**
     * 退出登入
     */
    private void signOut() {
        // 呼叫sdk的退出登入方法,第一個引數表示是否解綁推送的token,沒有使用推送或者被踢都要傳false
        EMClient.getInstance().logout(false, new EMCallBack() {
            @Override
            public void onSuccess() {
                Log.i("lzan13", "logout success");
                // 呼叫退出成功,結束app
                finish();
            }

            @Override
            public void onError(int i, String s) {
                Log.i("lzan13", "logout error " + i + " - " + s);
            }

            @Override
            public void onProgress(int i, String s) {

            }
        });
    }
}

對應佈局

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
    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:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    tools:context="net.melove.demo.easechat.ECMainActivity">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical">

        <EditText
            android:id="@+id/ec_edit_chat_id"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:hint="對方的username"/>

        <Button
            android:id="@+id/ec_btn_start_chat"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="發起聊天"/>

        <Button
            android:id="@+id/ec_btn_sign_out"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="退出登入"/>
    </LinearLayout>
</RelativeLayout>
這是一個完整的單聊專案,感覺的話請評論