reference) {
131 | this.reference = reference;
132 | }
133 |
134 | @Override
135 | public void handleMessage(Message msg) {
136 | LoginActivity activity = reference.get();
137 | if (activity != null && msg != null && msg.obj != null && msg.obj instanceof Protocol) {
138 | Protocol protocol = (Protocol)msg.obj;
139 | if (protocol.getOrder() == Protocol.LOGIN && activity.time <= protocol.getTime()) {
140 | // 登录指令且消息时间有效
141 | JSONArray content = protocol.getContent();
142 | // 关闭等待进度条
143 | LoadingDialogUtils.closeDialog(activity.dialog);
144 | // 处理登录信息
145 | try {
146 | int stateCode = content.getInt(0);
147 | switch (stateCode) {
148 | case Protocol.LOGIN_ALREADY_LOGIN: { // 已登录
149 | showToast("该用户已经登录");
150 | break;
151 | }
152 | case Protocol.LOGIN_NO_USERNAME: { // 非法用户名
153 | showToast("用户名不存在");
154 | break;
155 | }
156 | case Protocol.LOGIN_SUCCESS: { // 登录成功
157 | // 保存用户数据
158 | int id = content.getInt(1);
159 | String username = content.getString(2);
160 | String nickname = content.getString(3);
161 | UserController.getInstance().setUser(new User(id, username, "*", nickname));
162 | // 显示信息
163 | showToast("登录成功");
164 | // 界面跳转
165 | Intent intent = new Intent(activity, MainActivity.class);
166 | activity.startActivity(intent);
167 | activity.finish();
168 | break;
169 | }
170 | case Protocol.LOGIN_UNKNOW_PRO: { // 未知错误
171 | showToast("遇到未知错误");
172 | break;
173 | }
174 | case Protocol.LOGIN_WRONG_PASSWORD: { // 密码错误
175 | showToast("密码错误");
176 | break;
177 | }
178 | }
179 | } catch (JSONException e) {
180 | MyLog.e(TAG, Log.getStackTraceString(e));
181 | showToast("协议内容解析错误");
182 | }
183 | } else if (protocol.getOrder() == Protocol.LOGIN_TIME_OUT_PUSH) {
184 | // 登录过期时,关闭所有界面,重新打开登录界面
185 | MyLog.d(TAG, "login_time_out!");
186 | if (activity.isFinishing()) {
187 | BaseActivity.removeAllActivity();
188 | Intent intent = new Intent(activity, LoginActivity.class);
189 | activity.startActivity(intent);
190 | showToast("由于网络异常,您已下线,请重新登录");
191 | }
192 | }
193 | }
194 | }
195 |
196 | private void showToast(String msg) {
197 | Toast.makeText(reference.get(), msg, Toast.LENGTH_SHORT).show();
198 | }
199 | }
200 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/app/superxlcr/mypaintboard/controller/CommunicationController.java:
--------------------------------------------------------------------------------
1 | package com.app.superxlcr.mypaintboard.controller;
2 |
3 | import android.app.Activity;
4 | import android.content.Context;
5 | import android.net.ConnectivityManager;
6 | import android.util.Log;
7 | import android.widget.Toast;
8 |
9 | import com.app.superxlcr.mypaintboard.model.Protocol;
10 | import com.app.superxlcr.mypaintboard.utils.MyLog;
11 | import com.app.superxlcr.mypaintboard.utils.ProtocolListener;
12 |
13 | import org.json.JSONArray;
14 | import org.json.JSONException;
15 |
16 | import java.io.BufferedReader;
17 | import java.io.BufferedWriter;
18 | import java.io.IOException;
19 | import java.io.InputStreamReader;
20 | import java.io.OutputStreamWriter;
21 | import java.net.Socket;
22 | import java.util.List;
23 | import java.util.Timer;
24 | import java.util.TimerTask;
25 | import java.util.concurrent.CopyOnWriteArrayList;
26 |
27 | /**
28 | * Created by superxlcr on 2017/1/6.
29 | *
30 | * 通讯控制模块
31 | */
32 |
33 | public class CommunicationController {
34 |
35 | // 服务器IP
36 | public static final String SERVER_IP = "123.207.118.193";
37 | // public static final String SERVER_IP = "192.168.191.1";
38 |
39 | private static String TAG = CommunicationController.class.getSimpleName();
40 |
41 | private static CommunicationController instance = null;
42 |
43 | public static CommunicationController getInstance(Context context) {
44 | if (instance == null) {
45 | synchronized (CommunicationController.class) {
46 | if (instance == null) {
47 | instance = new CommunicationController(context);
48 | }
49 | }
50 | }
51 | return instance;
52 | }
53 |
54 | private Context context;
55 | private List listenerList; // 监听器列表
56 | private Socket socket; // 通信对象,若为null则无连接
57 | private SocketLock socketLock; // 通信锁
58 | private Timer timer; // 心跳包计时器任务
59 | private BufferedWriter writer;
60 | private BufferedReader reader;
61 |
62 | private CommunicationController(Context context) {
63 | this.context = context.getApplicationContext();
64 | this.listenerList = new CopyOnWriteArrayList<>();
65 | this.socketLock = new SocketLock();
66 | }
67 |
68 | /**
69 | * 连接服务器
70 | */
71 | public synchronized void connectServer() {
72 | // 已连接到服务器
73 | if (socket != null) {
74 | return;
75 | }
76 | // 检查是否连接wifi或流量
77 | ConnectivityManager connectivityManager = (ConnectivityManager) context.getSystemService(Activity.CONNECTIVITY_SERVICE);
78 | boolean wifi = connectivityManager.getNetworkInfo(ConnectivityManager.TYPE_WIFI).isConnectedOrConnecting();
79 | boolean internet = connectivityManager.getNetworkInfo(ConnectivityManager.TYPE_MOBILE).isConnectedOrConnecting();
80 | if (!wifi && !internet) {
81 | Toast.makeText(context, "您已断开网络连接,请检查网络状况!", Toast.LENGTH_LONG).show();
82 | return;
83 | }
84 |
85 | // 不能在主线程打开网络连接
86 | new Thread(new Runnable() {
87 | @Override
88 | public void run() {
89 | // 连接服务器
90 | try {
91 | synchronized (socketLock) {
92 | if (socket == null) {
93 | socket = new Socket(SERVER_IP, Protocol.PORT);
94 | writer = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream(), "UTF-8"));
95 | MyLog.d(TAG, "连接服务器成功");
96 |
97 | // 定时器子线程,开始发送心跳包
98 | timer = new Timer();
99 | timer.schedule(new TimerTask() {
100 | @Override
101 | public void run() {
102 | try {
103 | synchronized (writer) {
104 | if (socket != null) {
105 | // 发送心跳信息
106 | JSONArray jsonArray = new JSONArray();
107 | Protocol sendProtocol = new Protocol(Protocol.HEART_BEAT, System.currentTimeMillis(), jsonArray);
108 | writer.write(sendProtocol.getJsonStr());
109 | writer.newLine();
110 | writer.flush();
111 | } else {
112 | // 连接中断终止任务
113 | timer.cancel();
114 | }
115 | }
116 | } catch (IOException e) {
117 | // 出现错误,终止定时器,关闭连接
118 | MyLog.e(TAG, Log.getStackTraceString(e));
119 | clearSocket();
120 | timer.cancel();
121 | }
122 | }
123 | }, 0, Protocol.HEART_BEAT_PERIOD);
124 | MyLog.d(TAG, "心跳包定时器设置成功");
125 |
126 | // 开始监听信息
127 | try {
128 | reader = new BufferedReader(new InputStreamReader(socket.getInputStream(), "UTF-8"));
129 | while (socket != null) {
130 | String jsonString = null;
131 | while ((jsonString = reader.readLine()) != null) {
132 | try {
133 | Protocol protocol = new Protocol(jsonString);
134 | // 打印非心跳包的信息
135 | if (protocol.getOrder() != Protocol.HEART_BEAT) {
136 | MyLog.d(TAG, "Receive\nlen :" + jsonString.length() + "\n" + protocol.toString());
137 | }
138 | // 分发处理协议内容
139 | for (ProtocolListener listener : listenerList) {
140 | if (listener.onReceive(protocol)) { // 返回true则终止传递
141 | break;
142 | }
143 | }
144 | } catch (JSONException e) {
145 | // 协议解析错误,丢弃内容
146 | MyLog.e(TAG, Log.getStackTraceString(e));
147 | }
148 | }
149 | }
150 | } catch (IOException e) {
151 | MyLog.e(TAG, Log.getStackTraceString(e));
152 | clearSocket();
153 | }
154 | }
155 | }
156 | } catch (IOException e) {
157 | MyLog.e(TAG, Log.getStackTraceString(e));
158 | socket = null;
159 | }
160 | }
161 | }).start();
162 | }
163 |
164 | /**
165 | * 清理并关闭连接
166 | */
167 | public void clearSocket() {
168 | MyLog.d(TAG, "已清除socket连接");
169 | if (socket != null) {
170 | try {
171 | // 关闭输入输出流
172 | writer.close();
173 | reader.close();
174 | socket.shutdownInput();
175 | socket.shutdownOutput();
176 | // 关闭连接
177 | socket.close();
178 | } catch (IOException e) {
179 | MyLog.e(TAG, Log.getStackTraceString(e));
180 | }
181 | socket = null;
182 | }
183 | // 重置状态
184 | // 清空用户状态
185 | UserController.getInstance().setUser(null);
186 | // 清空房间状态
187 | RoomController.getInstance().setRoom(null);
188 | RoomController.getInstance().setList(null);
189 | }
190 |
191 | /**
192 | * 添加监听器
193 | *
194 | * @param listener 监听器
195 | * @return 是否添加成功
196 | */
197 | public boolean registerListener(ProtocolListener listener) {
198 | if (!listenerList.contains(listener)) {
199 | return listenerList.add(listener);
200 | }
201 | return false;
202 | }
203 |
204 | /**
205 | * 移除监听器
206 | *
207 | * @param listener 监听器
208 | * @return 是否移除成功
209 | */
210 | public boolean removeListener(ProtocolListener listener) {
211 | return listenerList.remove(listener);
212 | }
213 |
214 | /**
215 | * 发送消息
216 | *
217 | * @param protocol 协议内容
218 | * @return 是否发送成功
219 | */
220 | public boolean sendProtocol(Protocol protocol) {
221 | // 尝试连接服务器
222 | connectServer();
223 | // 判断是否连接上服务器
224 | if (socket == null) {
225 | return false;
226 | }
227 | try {
228 | synchronized (writer) {
229 | writer.write(protocol.getJsonStr());
230 | writer.newLine();
231 | writer.flush();
232 | // 打印非心跳包的信息
233 | if (protocol.getOrder() != Protocol.HEART_BEAT) {
234 | MyLog.d(TAG, "Send\nlen :" + protocol.getJsonStr().length() + "\n" + protocol.toString());
235 | }
236 | }
237 | return true;
238 | } catch (IOException e) {
239 | MyLog.e(TAG, Log.getStackTraceString(e));
240 | clearSocket();
241 | return false;
242 | }
243 | }
244 |
245 | class SocketLock {
246 | }
247 | }
248 |
--------------------------------------------------------------------------------
/app/src/main/java/com/app/superxlcr/mypaintboard/model/Protocol.java:
--------------------------------------------------------------------------------
1 | package com.app.superxlcr.mypaintboard.model;
2 |
3 | import org.json.*;
4 |
5 | /**
6 | * 协议
7 | *
8 | * @author superxlcr
9 | *
10 | */
11 | public class Protocol {
12 |
13 | // 服务器端口
14 | public static final int PORT = 29999;
15 | // 心跳包间隔
16 | public static long HEART_BEAT_PERIOD = 5000;
17 |
18 | // 指令类型
19 |
20 | // 登录
21 | // c to s: username + password
22 | // s to c: stateCode + (id + username + nickname)(if success)
23 | public static final int LOGIN = 0;
24 | public static final int LOGIN_SUCCESS = 0; // 成功
25 | public static final int LOGIN_NO_USERNAME = LOGIN_SUCCESS + 1; // 非法用户名
26 | public static final int LOGIN_WRONG_PASSWORD = LOGIN_NO_USERNAME + 1; // 错误密码
27 | public static final int LOGIN_UNKNOW_PRO = LOGIN_WRONG_PASSWORD + 1; // 未知错误
28 | public static final int LOGIN_ALREADY_LOGIN = LOGIN_UNKNOW_PRO + 1; // 用户已在线
29 |
30 | // 注册
31 | // c to s: username + password + nickname
32 | // s to c: stateCode + (id + username + nickname)(if success)
33 | public static final int REGISTER = LOGIN + 1;
34 | public static final int REGISTER_SUCCESS = 0; // 成功
35 | public static final int REGISTER_REPEAT_USERNAME = REGISTER_SUCCESS + 1; // 重复用户名
36 | public static final int REGISTER_REPEAT_NICKNAME = REGISTER_REPEAT_USERNAME + 1; // 重复昵称
37 | public static final int REGISTER_UNKNOW_PRO = REGISTER_REPEAT_NICKNAME + 1; // 未知错误
38 |
39 | // 修改资料
40 | // c to s: id + username + password(为空表示不作修改) + nickname
41 | // s to c: stateCode
42 | public static final int EDIT_INFO = REGISTER + 1;
43 | public static final int EDIT_INFO_SUCCESS = 0; // 成功
44 | public static final int EDIT_INFO_UNKNOW_PRO = EDIT_INFO_SUCCESS + 1; // 未知错误
45 | public static final int EDIT_INFO_REPEAT_NICKNAME = EDIT_INFO_UNKNOW_PRO + 1; // 昵称重复
46 |
47 | // 获取房间列表
48 | // c to s:
49 | // s to c: roomNumber + (roomId + roomName + roomMemberNumber)(per room)
50 | public static final int GET_ROOM_LIST = EDIT_INFO + 1;
51 |
52 | // 创建房间
53 | // c to s: roomName
54 | // s to c: roomId + roomName
55 | public static final int CREATE_ROOM = GET_ROOM_LIST + 1;
56 |
57 | // 加入房间
58 | // c to s: roomId
59 | // s to c: stateCode + (roomId + roomName) (if success)
60 | public static final int JOIN_ROOM = CREATE_ROOM + 1;
61 | public static final int JOIN_ROOM_SUCCESS = 0; // 成功
62 | public static final int JOIN_ROOM_INVALID_ROOMID = JOIN_ROOM_SUCCESS + 1; // 非法房间id
63 | public static final int JOIN_ROOM_UNKNOW_PRO = JOIN_ROOM_INVALID_ROOMID + 1; // 未知错误
64 | public static final int JOIN_ROOM_ALREADY_IN = JOIN_ROOM_UNKNOW_PRO + 1; // 用户已在该房间
65 |
66 | // 退出房间
67 | // c to s:
68 | // s to c: stateCode
69 | public static final int EXIT_ROOM = JOIN_ROOM + 1;
70 | public static final int EXIT_ROOM_SUCCESS = 0; // 成功
71 | public static final int EXIT_ROOM_NOT_IN = EXIT_ROOM_SUCCESS + 1; // 用户不在任何房间
72 | public static final int EXIT_ROOM_UNKNOW_PRO = EXIT_ROOM_NOT_IN + 1; // 未知错误
73 |
74 | // 获取房间成员
75 | // c to s: roomId
76 | // s to c: stateCode + roomId + memberNumber + (username +
77 | // nickname + isAdmin)(per user)
78 | // push: stateCode + roomId + memberNumber + (username +
79 | // nickname + isAdmin)(per user)
80 | public static final int GET_ROOM_MEMBER = EXIT_ROOM + 1;
81 | public static final int GET_ROOM_MEMBER_SUCCESS = 0; // 成功才有后续内容
82 | public static final int GET_ROOM_MEMBER_WRONG_ROOM_ID = GET_ROOM_MEMBER_SUCCESS + 1; // 房间id错误
83 | public static final int GET_ROOM_MEMBER_UNKNOW_PRO = GET_ROOM_MEMBER_WRONG_ROOM_ID + 1; // 未知错误
84 |
85 | // 聊天消息
86 | // c to s: roomId + message
87 | // s to c: stateCode
88 | public static final int MESSAGE = GET_ROOM_MEMBER + 1;
89 | public static final int MESSAGE_SUCCESS = 0;
90 | public static final int MESSAGE_WRONG_ROOM_ID = MESSAGE_SUCCESS + 1; // 错误的房间id
91 | public static final int MESSAGE_UNKNOW_PRO = MESSAGE_WRONG_ROOM_ID + 1; // 未知错误
92 |
93 | // 聊天消息推送
94 | // push: roomId + nickname + message
95 | public static final int MESSAGE_PUSH = MESSAGE + 1;
96 |
97 | // 绘制消息
98 | // c to s: roomId + line (pointNumber + point (x , y) +
99 | // color + paintWidth +
100 | // isEraser + width + height)
101 | // s to c: stateCode
102 | public static final int DRAW = MESSAGE_PUSH + 1;
103 | public static final int DRAW_SUCCESS = 0;
104 | public static final int DRAW_WRONG_ROOM_ID = DRAW_SUCCESS + 1; // 错误的房间id
105 | public static final int DRAW_UNKNOW_PRO = DRAW_WRONG_ROOM_ID + 1; // 未知错误
106 |
107 | // 绘制消息推送
108 | // push: roomId + username + line (pointNumber + point (x ,
109 | // y) +
110 | // color + width + isEraser + width + height)
111 | public static final int DRAW_PUSH = DRAW + 1;
112 |
113 | // 同步绘制消息
114 | // c to s: roomId
115 | // s to c: stateCode (if success, client will get draw_push)
116 | public static final int GET_DRAW_LIST = DRAW_PUSH + 1;
117 | public static final int GET_DRAW_LIST_SUCCESS = 0;
118 | public static final int GET_DRAW_LIST_WRONG_ROOM_ID = GET_DRAW_LIST_SUCCESS + 1; // 错误的房间id
119 | public static final int GET_DRAW_LIST_UNKNOW_PRO = GET_DRAW_LIST_WRONG_ROOM_ID + 1; // 未知错误
120 |
121 | // 登录过期推送
122 | // push:
123 | public static final int LOGIN_TIME_OUT_PUSH = GET_DRAW_LIST + 1;
124 |
125 | // 心跳包
126 | // c to s:
127 | // s to c:
128 | public static final int HEART_BEAT = LOGIN_TIME_OUT_PUSH + 1;
129 |
130 | // 图片上传
131 | // c to s: ask
132 | // s to c: respondCode
133 | // c to s (2): stateCode + ( len + file )(if continue)
134 | public static final int UPLOAD_PIC = HEART_BEAT + 1;
135 | // ask
136 | public static final int UPLOAD_PIC_ASK = 0; // 请求传输
137 | // respondCode
138 | public static final int UPLOAD_PIC_OK = UPLOAD_PIC_ASK + 1; // 允许传输
139 | public static final int UPLOAD_PIC_FAIL = UPLOAD_PIC_OK + 1; // 禁止传输
140 | // stateCode + len + file
141 | public static final int UPLOAD_PIC_CONTINUE = UPLOAD_PIC_FAIL + 1; // 继续传输
142 | public static final int UPLOAD_PIC_FINISH = UPLOAD_PIC_CONTINUE + 1; // 完成传输
143 |
144 | // 背景图片推送
145 | // push: ask
146 | // c to s: respondCode
147 | // push (2): stateCode + ( len + file )(if continue)
148 | public static final int BG_PIC_PUSH = UPLOAD_PIC + 1;
149 | // ask
150 | public static final int BG_PIC_PUSH_ASK = 0; // 请求传输
151 | // respondCode
152 | public static final int BG_PIC_PUSH_OK = BG_PIC_PUSH_ASK + 1; // 允许传输
153 | public static final int BG_PIC_PUSH_FAIL = BG_PIC_PUSH_OK + 1; // 禁止传输
154 | // stateCode + len + file
155 | public static final int BG_PIC_PUSH_CONTINUE = BG_PIC_PUSH_FAIL + 1; // 继续传输
156 | public static final int BG_PIC_PUSH_FINISH = BG_PIC_PUSH_CONTINUE + 1; // 完成传输
157 |
158 | // 清空画板
159 | // c to s:
160 | // s to c: stateCode
161 | public static final int CLEAR_DRAW = BG_PIC_PUSH + 1;
162 | // stateCode
163 | public static final int CLEAR_DRAW_SUCCESS = 0; // 成功
164 | public static final int CLEAR_DRAW_NOT_ADMIN = CLEAR_DRAW_SUCCESS + 1; // 非管理员
165 | public static final int CLEAR_DRAW_WRONG_ROOM_ID = CLEAR_DRAW_NOT_ADMIN + 1; // 房间id错误
166 |
167 | // 清空画板推送
168 | // push: roomId
169 | public static final int CLEAR_DRAW_PUSH = CLEAR_DRAW + 1;
170 |
171 | // 协议:指令 + 时间戳(防重复功能) + 内容
172 |
173 | public static final String ORDER = "order";
174 | public static final String TIME = "time";
175 | public static final String CONTENT = "content";
176 |
177 | // 指令
178 | private int order;
179 | // 时间戳
180 | // 客户端查询:客户端时间为准
181 | // 服务器推送:服务器时间为准
182 | private long time;
183 | // 内容
184 | private JSONArray content;
185 |
186 | // json字符串
187 | private String jsonStr;
188 |
189 | public Protocol(int order, long time, JSONArray content) {
190 | this.order = order;
191 | this.time = time;
192 | this.content = content;
193 | JSONObject jsonObject = new JSONObject();
194 | try {
195 | jsonObject.put(ORDER, order);
196 | jsonObject.put(TIME, time);
197 | jsonObject.put(CONTENT, content.toString());
198 | } catch (JSONException e) {
199 | e.printStackTrace();
200 | }
201 | this.jsonStr = jsonObject.toString();
202 | }
203 |
204 | public Protocol(String jsonStr) throws JSONException {
205 | this.jsonStr = jsonStr;
206 | JSONObject jsonObject = new JSONObject(jsonStr);
207 | this.order = jsonObject.getInt(ORDER);
208 | this.time = jsonObject.getLong(TIME);
209 | String contentStr = jsonObject.getString(CONTENT);
210 | this.content = new JSONArray(contentStr);
211 | }
212 |
213 | public int getOrder() {
214 | return order;
215 | }
216 |
217 | public String getOrderStr() {
218 | String orderStr = "unknow";
219 | switch (order) {
220 | case LOGIN:
221 | orderStr = "LOGIN";
222 | break;
223 | case REGISTER:
224 | orderStr = "REGISTER";
225 | break;
226 | case EDIT_INFO:
227 | orderStr = "EDIT_INFO";
228 | break;
229 | case GET_ROOM_LIST:
230 | orderStr = "GET_ROOM_LIST";
231 | break;
232 | case CREATE_ROOM:
233 | orderStr = "CREATE_ROOM";
234 | break;
235 | case JOIN_ROOM:
236 | orderStr = "JOIN_ROOM";
237 | break;
238 | case EXIT_ROOM:
239 | orderStr = "EXIT_ROOM";
240 | break;
241 | case GET_ROOM_MEMBER:
242 | orderStr = "GET_ROOM_MEMBER";
243 | break;
244 | case MESSAGE:
245 | orderStr = "MESSAGE";
246 | break;
247 | case MESSAGE_PUSH:
248 | orderStr = "MESSAGE_PUSH";
249 | break;
250 | case DRAW:
251 | orderStr = "DRAW";
252 | break;
253 | case DRAW_PUSH:
254 | orderStr = "DRAW_PUSH";
255 | break;
256 | case GET_DRAW_LIST:
257 | orderStr = "GET_DRAW_LIST";
258 | break;
259 | case LOGIN_TIME_OUT_PUSH:
260 | orderStr = "LOGIN_TIME_OUT_PUSH";
261 | break;
262 | case HEART_BEAT:
263 | orderStr = "HEART_BEAT";
264 | break;
265 | case UPLOAD_PIC:
266 | orderStr = "UPLOAD_PIC";
267 | break;
268 | case BG_PIC_PUSH:
269 | orderStr = "BG_PIC_PUSH";
270 | break;
271 | case CLEAR_DRAW:
272 | orderStr = "CLEAR_DRAW";
273 | break;
274 | case CLEAR_DRAW_PUSH:
275 | orderStr = "CLEAR_DRAW_PUSH";
276 | break;
277 | default:
278 | break;
279 | }
280 | return orderStr;
281 | }
282 |
283 | public void setOrder(int order) {
284 | this.order = order;
285 | }
286 |
287 | public long getTime() {
288 | return time;
289 | }
290 |
291 | public void setTime(long time) {
292 | this.time = time;
293 | }
294 |
295 | public JSONArray getContent() {
296 | return content;
297 | }
298 |
299 | public void setContent(JSONArray content) {
300 | this.content = content;
301 | }
302 |
303 | public String getJsonStr() {
304 | return jsonStr;
305 | }
306 |
307 | public void setJsonStr(String jsonStr) {
308 | this.jsonStr = jsonStr;
309 | }
310 |
311 | @Override
312 | public String toString() {
313 | StringBuilder sb = new StringBuilder();
314 | sb.append("order :" + getOrderStr() + "\r\n");
315 | sb.append("time :" + time + "\r\n");
316 | sb.append("content :" + content + "\r\n");
317 | return sb.toString();
318 | }
319 |
320 | }
321 |
--------------------------------------------------------------------------------
/app/src/main/java/com/app/superxlcr/mypaintboard/controller/DrawController.java:
--------------------------------------------------------------------------------
1 | package com.app.superxlcr.mypaintboard.controller;
2 |
3 | import android.content.Context;
4 | import android.os.Handler;
5 | import android.os.Message;
6 |
7 | import com.app.superxlcr.mypaintboard.model.Line;
8 | import com.app.superxlcr.mypaintboard.model.Point;
9 | import com.app.superxlcr.mypaintboard.model.Protocol;
10 | import com.app.superxlcr.mypaintboard.utils.ProtocolListener;
11 |
12 | import org.json.JSONArray;
13 | import org.json.JSONException;
14 |
15 | /**
16 | * Created by superxlcr on 2017/1/21.
17 | * 绘画控制模块
18 | */
19 |
20 | public class DrawController {
21 |
22 | private static DrawController instance;
23 |
24 | public static DrawController getInstance() {
25 | if (instance == null) {
26 | synchronized (DrawController.class) {
27 | if (instance == null) {
28 | instance = new DrawController();
29 | }
30 | }
31 | }
32 | return instance;
33 | }
34 |
35 | private ProtocolListener sendDrawListener; // 发送绘制条目用监听器
36 | private ProtocolListener receiveDrawListener; // 接收绘制条目用监听器
37 | private ProtocolListener getDrawListListener; // 获取绘制条目用监听器
38 | private ProtocolListener uploadPicListener; // 上传图片用监听器
39 | private ProtocolListener receiveBgPicListener; // 接收背景图片推送监听器
40 | private ProtocolListener clearDrawListener; // 清除线段绘制监听器
41 | private ProtocolListener clearDrawPushListener; // 清除线段绘制推送监听器
42 |
43 | private DrawController() {
44 | sendDrawListener = null;
45 | receiveDrawListener = null;
46 | }
47 |
48 | /**
49 | * 发送绘制条目
50 | * @param context 上下文
51 | * @param handler 用于回调消息
52 | * @param time 发送时间
53 | * @param roomId 房间id
54 | * @param line 绘制条目
55 | * @return 是否发送成功
56 | */
57 | public boolean sendDraw(final Context context, final Handler handler, long time, int roomId, Line line) {
58 | try {
59 | JSONArray jsonArray = new JSONArray();
60 | jsonArray.put(roomId);
61 | // line (pointNumber + point (x , y) + color + width + isEraser + width + height)
62 | jsonArray.put(line.getPointList().size());
63 | for (Point point : line.getPointList()) {
64 | jsonArray.put(point.getX());
65 | jsonArray.put(point.getY());
66 | }
67 | jsonArray.put(line.getColor());
68 | jsonArray.put(line.getPaintWidth());
69 | jsonArray.put(line.isEraser());
70 | jsonArray.put(line.getWidth());
71 | jsonArray.put(line.getHeight());
72 | Protocol sendProtocol = new Protocol(Protocol.DRAW, time, jsonArray);
73 | // 注册监听器
74 | sendDrawListener = new ProtocolListener() {
75 | @Override
76 | public boolean onReceive(Protocol protocol) {
77 | int order = protocol.getOrder();
78 | if (order == Protocol.DRAW) {
79 | // 通过handler返回协议信息
80 | Message message = handler.obtainMessage();
81 | message.obj = protocol;
82 | handler.sendMessage(message);
83 | // 移除监听器
84 | CommunicationController.getInstance(context).removeListener(sendDrawListener);
85 | return true;
86 | }
87 | return false;
88 | }
89 | };
90 | CommunicationController.getInstance(context).registerListener(sendDrawListener);
91 | // 发送信息
92 | return CommunicationController.getInstance(context).sendProtocol(sendProtocol);
93 | } catch (JSONException e) {
94 | e.printStackTrace();
95 | }
96 | return false;
97 | }
98 |
99 | /**
100 | * 设置接收绘制推送监听器
101 | * @param context 上下文
102 | * @param handler 用于接收回调消息
103 | */
104 | public void setReceiveDrawHandler(Context context, final Handler handler) {
105 | // 清除旧监听器
106 | if (receiveDrawListener != null) {
107 | CommunicationController.getInstance(context).removeListener(receiveDrawListener);
108 | }
109 | // 连接服务器
110 | CommunicationController.getInstance(context).connectServer();
111 | // 注册监听器
112 | receiveDrawListener = new ProtocolListener() {
113 | @Override
114 | public boolean onReceive(Protocol protocol) {
115 | int order = protocol.getOrder();
116 | if (order == Protocol.DRAW_PUSH) {
117 | // 通过handler返回协议信息
118 | Message message = handler.obtainMessage();
119 | message.obj = protocol;
120 | handler.sendMessage(message);
121 | return true;
122 | }
123 | return false;
124 | }
125 | };
126 | CommunicationController.getInstance(context).registerListener(receiveDrawListener);
127 | }
128 |
129 | /**
130 | * 获取绘制条目列表
131 | * @param context 上下文
132 | * @param handler 用于回调消息
133 | * @param time 发送时间
134 | * @param roomId 房间id
135 | * @return 是否发送成功
136 | */
137 | public boolean getDrawList(final Context context, final Handler handler, long time, int roomId) {
138 | JSONArray jsonArray = new JSONArray();
139 | jsonArray.put(roomId);
140 | Protocol sendProtocol = new Protocol(Protocol.GET_DRAW_LIST, time, jsonArray);
141 | // 注册监听器
142 | getDrawListListener = new ProtocolListener() {
143 | @Override
144 | public boolean onReceive(Protocol protocol) {
145 | int order = protocol.getOrder();
146 | if (order == Protocol.GET_DRAW_LIST) {
147 | // 通过handler返回协议信息
148 | Message message = handler.obtainMessage();
149 | message.obj = protocol;
150 | handler.sendMessage(message);
151 | // 移除监听器
152 | CommunicationController.getInstance(context).removeListener(getDrawListListener);
153 | return true;
154 | }
155 | return false;
156 | }
157 | };
158 | CommunicationController.getInstance(context).registerListener(getDrawListListener);
159 | // 发送信息
160 | return CommunicationController.getInstance(context).sendProtocol(sendProtocol);
161 | }
162 |
163 | /**
164 | * 请求传输图片
165 | * @param context 上下文
166 | * @param handler 用于回调消息
167 | * @return 是否发送成功
168 | */
169 | public boolean askUploadPic(final Context context, final Handler handler) {
170 | JSONArray jsonArray = new JSONArray();
171 | jsonArray.put(Protocol.UPLOAD_PIC_ASK);
172 | Protocol sendProtocol = new Protocol(Protocol.UPLOAD_PIC, System.currentTimeMillis(), jsonArray);
173 | // 注册监听器
174 | uploadPicListener = new ProtocolListener() {
175 | @Override
176 | public boolean onReceive(Protocol protocol) {
177 | int order = protocol.getOrder();
178 | if (order == Protocol.UPLOAD_PIC) {
179 | // 通过handler返回协议信息
180 | Message message = handler.obtainMessage();
181 | message.obj = protocol;
182 | handler.sendMessage(message);
183 | // 移除监听器
184 | CommunicationController.getInstance(context).removeListener(uploadPicListener);
185 | return true;
186 | }
187 | return false;
188 | }
189 | };
190 | CommunicationController.getInstance(context).registerListener(uploadPicListener);
191 | // 发送信息
192 | return CommunicationController.getInstance(context).sendProtocol(sendProtocol);
193 | }
194 |
195 | /**
196 | * 设置接收背景图片推送监听器
197 | * @param context 上下文
198 | * @param handler 用于回调消息处理器
199 | */
200 | public void setReceiveBgPicHandler(Context context, final Handler handler) {
201 | // 清除旧监听器
202 | if (receiveBgPicListener != null) {
203 | CommunicationController.getInstance(context).removeListener(receiveBgPicListener);
204 | }
205 | // 连接服务器
206 | CommunicationController.getInstance(context).connectServer();
207 | // 注册监听器
208 | receiveBgPicListener = new ProtocolListener() {
209 | @Override
210 | public boolean onReceive(Protocol protocol) {
211 | int order = protocol.getOrder();
212 | if (order == Protocol.BG_PIC_PUSH) {
213 | // 通过handler返回协议信息
214 | Message message = handler.obtainMessage();
215 | message.obj = protocol;
216 | handler.sendMessage(message);
217 | return true;
218 | }
219 | return false;
220 | }
221 | };
222 | CommunicationController.getInstance(context).registerListener(receiveBgPicListener);
223 | }
224 |
225 | /**
226 | * 清除房间绘制线段
227 | * @param context 上下文
228 | * @param handler 回调处理器
229 | * @return 是否发送成功
230 | */
231 | public boolean clearDraw(final Context context, final Handler handler) {
232 | JSONArray jsonArray = new JSONArray();
233 | Protocol sendProtocol = new Protocol(Protocol.CLEAR_DRAW, System.currentTimeMillis(), jsonArray);
234 | // 注册监听器
235 | clearDrawListener = new ProtocolListener() {
236 | @Override
237 | public boolean onReceive(Protocol protocol) {
238 | int order = protocol.getOrder();
239 | if (order == Protocol.CLEAR_DRAW) {
240 | // 通过handler返回协议信息
241 | Message message = handler.obtainMessage();
242 | message.obj = protocol;
243 | handler.sendMessage(message);
244 | // 移除监听器
245 | CommunicationController.getInstance(context).removeListener(clearDrawListener);
246 | return true;
247 | }
248 | return false;
249 | }
250 | };
251 | CommunicationController.getInstance(context).registerListener(clearDrawListener);
252 | // 发送信息
253 | return CommunicationController.getInstance(context).sendProtocol(sendProtocol);
254 | }
255 |
256 | /**
257 | * 设置清除绘制线段推送回调器
258 | * @param context 上下文
259 | * @param handler 回调器
260 | */
261 | public void setClearDrawPushHandler(Context context, final Handler handler) {
262 | // 清除旧监听器
263 | if (clearDrawPushListener != null) {
264 | CommunicationController.getInstance(context).removeListener(clearDrawPushListener);
265 | }
266 | // 连接服务器
267 | CommunicationController.getInstance(context).connectServer();
268 | // 注册监听器
269 | clearDrawPushListener = new ProtocolListener() {
270 | @Override
271 | public boolean onReceive(Protocol protocol) {
272 | int order = protocol.getOrder();
273 | if (order == Protocol.CLEAR_DRAW_PUSH) {
274 | // 通过handler返回协议信息
275 | Message message = handler.obtainMessage();
276 | message.obj = protocol;
277 | handler.sendMessage(message);
278 | return true;
279 | }
280 | return false;
281 | }
282 | };
283 | CommunicationController.getInstance(context).registerListener(clearDrawPushListener);
284 | }
285 | }
286 |
--------------------------------------------------------------------------------
/app/src/main/java/com/app/superxlcr/mypaintboard/view/MyPaintView.java:
--------------------------------------------------------------------------------
1 | package com.app.superxlcr.mypaintboard.view;
2 |
3 | import android.content.Context;
4 | import android.graphics.Bitmap;
5 | import android.graphics.Canvas;
6 | import android.graphics.Color;
7 | import android.graphics.Paint;
8 | import android.graphics.Path;
9 | import android.graphics.PorterDuff;
10 | import android.graphics.PorterDuffXfermode;
11 | import android.os.Handler;
12 | import android.util.AttributeSet;
13 | import android.util.Log;
14 | import android.view.MotionEvent;
15 | import android.view.View;
16 | import android.widget.Toast;
17 |
18 | import com.app.superxlcr.mypaintboard.controller.DrawController;
19 | import com.app.superxlcr.mypaintboard.model.Line;
20 | import com.app.superxlcr.mypaintboard.model.Point;
21 | import com.app.superxlcr.mypaintboard.utils.MyLog;
22 |
23 | import java.util.ArrayList;
24 | import java.util.LinkedHashMap;
25 | import java.util.List;
26 | import java.util.Map;
27 |
28 | /**
29 | * Created by superxlcr on 2017/1/22.
30 | * 绘制画板View
31 | */
32 |
33 | public class MyPaintView extends View {
34 |
35 | private static final String TAG = MyPaintView.class.getSimpleName();
36 |
37 | private static final int POINT_ARRAY_SIZE = 5; // 每组发送的点数大小
38 | private static final int REPEAT_TIMES = 5; // 发送失败重新发送次数
39 | private static final int FAIL_RELAX_TIME = 100; // 发送失败间隔时间
40 | private static final int ERASER_WIDTH = 40; // 橡皮擦宽度
41 |
42 | // 画板大小
43 | private int width = 0, height = 0;
44 |
45 | // 画笔颜色
46 | private int paintColor;
47 | // 画笔宽度
48 | private float paintWidth;
49 |
50 | // 图片缓冲内存
51 | private Bitmap cacheBitmap;
52 | // 画布缓冲区
53 | private Canvas cacheCanvas;
54 |
55 | // 本地未确认线段缓存区
56 | private Map localLineList;
57 | // 图片缓冲内存
58 | private Bitmap localLineCacheBitmap;
59 | // 画布缓冲区
60 | private Canvas localLineCacheCanvas;
61 |
62 | // 是否擦除模式
63 | private boolean isEraser;
64 |
65 | // 点列表
66 | private List pointList;
67 | // 房间id
68 | private int roomId;
69 | // 用于回调消息
70 | private Handler handler = null;
71 |
72 | /**
73 | * 清空屏幕
74 | */
75 | public void clearDraw() {
76 | // 设置背景为白色
77 | setBackgroundColor(Color.WHITE);
78 | // 清空笔画
79 | Paint paint = new Paint();
80 | paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
81 | cacheCanvas.drawPaint(paint);
82 | // 清空本地未确认线段
83 | localLineList.clear();
84 | // 刷新画面
85 | invalidate();
86 | }
87 |
88 | /**
89 | * 绘制线段
90 | *
91 | * @param line 线段
92 | */
93 | public void drawLine(Line line) {
94 | MyLog.d(TAG, "draw line to cache!");
95 | Paint linePaint = new Paint(Paint.DITHER_FLAG);
96 | linePaint.setColor(line.getColor()); // 颜色
97 | linePaint.setStyle(Paint.Style.STROKE); // 实心
98 | linePaint.setStrokeWidth((float) line.getPaintWidth()); // 长度
99 | // 反锯齿
100 | linePaint.setAntiAlias(true);
101 | linePaint.setDither(true);
102 | if (line.isEraser()) {
103 | // 透明色,橡皮擦固定大小
104 | linePaint.setColor(Color.TRANSPARENT);
105 | linePaint.setStrokeWidth(ERASER_WIDTH);
106 | linePaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
107 | }
108 | List list = line.getPointList();
109 | Path linePath = new Path();
110 | int lineWidth = line.getWidth();
111 | int lineHeight = line.getHeight();
112 | float lineLastX = ((float) list.get(0).getX() * width) / lineWidth;
113 | float lineLastY = ((float) list.get(0).getY() * height) / lineHeight;
114 | linePath.moveTo(lineLastX, lineLastY);
115 | for (int i = 1; i < list.size(); i++) {
116 | float x = ((float) list.get(i).getX() * width) / lineWidth;
117 | float y = ((float) list.get(i).getY() * height) / lineHeight;
118 | linePath.quadTo(lineLastX, lineLastY, x, y);
119 | lineLastX = x;
120 | lineLastY = y;
121 | }
122 | // 绘制到缓冲区
123 | cacheCanvas.drawPath(linePath, linePaint);
124 | // 刷新视图
125 | invalidate();
126 | }
127 |
128 | /**
129 | * 设置画笔颜色
130 | *
131 | * @param color 颜色
132 | */
133 | public void setPaintColor(int color) {
134 | paintColor = color;
135 | }
136 |
137 | /**
138 | * 设置画笔宽度
139 | *
140 | * @param width 宽度
141 | */
142 | public void setPaintWidth(float width) {
143 | paintWidth = width;
144 | }
145 |
146 | /**
147 | * 确认本地线段
148 | *
149 | * @param time 线段协议发送时间
150 | * @param draw 是否绘制到缓存区
151 | */
152 | public void confirmLocalLine(long time, boolean draw) {
153 | List deleteTimeList = new ArrayList<>();
154 | for (Long keyTime : localLineList.keySet()) {
155 | if (keyTime <= time) {
156 | // 绘制到缓存区
157 | if (draw) {
158 | drawLine(localLineList.get(keyTime));
159 | }
160 | // 已确认线段
161 | deleteTimeList.add(keyTime);
162 | }
163 | }
164 | // 移除相应线段
165 | for (Long ketTime : deleteTimeList) {
166 | localLineList.remove(ketTime);
167 | }
168 | }
169 |
170 | public boolean isEraser() {
171 | return isEraser;
172 | }
173 |
174 | public void setEraser(boolean eraser) {
175 | isEraser = eraser;
176 | }
177 |
178 | public int getRoomId() {
179 | return roomId;
180 | }
181 |
182 | public void setRoomId(int roomId) {
183 | this.roomId = roomId;
184 | }
185 |
186 | @Override
187 | public Handler getHandler() {
188 | return handler;
189 | }
190 |
191 | public void setHandler(Handler handler) {
192 | this.handler = handler;
193 | }
194 |
195 | @Override
196 | protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
197 | if (cacheBitmap == null) {
198 | // 设置白色背景
199 | setBackgroundColor(Color.WHITE);
200 | // 创建view大小的画布
201 | width = right - left;
202 | height = bottom - top;
203 | cacheBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
204 | // 初始化画布
205 | cacheCanvas = new Canvas();
206 | cacheCanvas.setBitmap(cacheBitmap);
207 | // 初始化本地线段有序map
208 | localLineList = new LinkedHashMap<>();
209 | // 初始化本地线段画布
210 | localLineCacheBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
211 | localLineCacheCanvas = new Canvas();
212 | localLineCacheCanvas.setBitmap(localLineCacheBitmap);
213 | // 初始化画笔
214 | paintColor = Color.BLACK;
215 | paintWidth = 1;
216 | // 画笔模式
217 | isEraser = false;
218 | // 初始化点数列表
219 | pointList = new ArrayList<>();
220 | }
221 | }
222 |
223 | @Override
224 | public boolean onTouchEvent(MotionEvent event) {
225 | float x = event.getX(), y = event.getY();
226 | switch (event.getAction()) {
227 | case MotionEvent.ACTION_DOWN: { // 第一次点击用于记录坐标
228 | // 清空并添加新的点
229 | pointList.clear();
230 | pointList.add(new Point(x, y));
231 | break;
232 | }
233 | case MotionEvent.ACTION_MOVE: { // 移动的同时绘制线段
234 | // 添加新的点,判断是否需要发送
235 | pointList.add(new Point(x, y));
236 | // 发送绘制线段
237 | if (pointList.size() == POINT_ARRAY_SIZE) {
238 | if (handler == null) {
239 | Toast.makeText(getContext(), "还没有设置Handler!", Toast.LENGTH_SHORT).show();
240 | }
241 | Point points[] = new Point[POINT_ARRAY_SIZE];
242 | for (int i = 0; i < pointList.size(); i++) {
243 | points[i] = pointList.get(i);
244 | }
245 | Line line = new Line(points, paintColor, paintWidth, isEraser, width, height);
246 | // 断线重发机制
247 | int counter = 0;
248 | long time = System.currentTimeMillis();
249 | while (!DrawController.getInstance().sendDraw(getContext(), handler, time, roomId, line) && counter < REPEAT_TIMES) {
250 | counter++;
251 | try {
252 | Thread.sleep(FAIL_RELAX_TIME);
253 | } catch (InterruptedException e) {
254 | e.printStackTrace();
255 | }
256 | }
257 | // 列表只留下最后一个点
258 | for (int i = 0; i < POINT_ARRAY_SIZE - 1; i++) {
259 | pointList.remove(0);
260 | }
261 | // 本地线段添加
262 | localLineList.put(time, line);
263 | // 刷新界面
264 | invalidate();
265 | }
266 | break;
267 | }
268 | case MotionEvent.ACTION_UP: { // 把线段绘制到缓存中
269 | // 绘制剩余的线段
270 | if (handler == null) {
271 | Toast.makeText(getContext(), "还没有设置Handler!", Toast.LENGTH_SHORT).show();
272 | }
273 | Point points[] = new Point[pointList.size()];
274 | for (int i = 0; i < pointList.size(); i++) {
275 | points[i] = pointList.get(i);
276 | }
277 | Line line = new Line(points, paintColor, paintWidth, isEraser, width, height);
278 | // 断线重发机制
279 | int counter = 0;
280 | long time = System.currentTimeMillis();
281 | while (!DrawController.getInstance().sendDraw(getContext(), handler, time, roomId, line) && counter < REPEAT_TIMES) {
282 | counter++;
283 | try {
284 | Thread.sleep(FAIL_RELAX_TIME);
285 | } catch (InterruptedException e) {
286 | e.printStackTrace();
287 | }
288 | }
289 | // 清空列表
290 | pointList.clear();
291 | // 本地线段添加
292 | localLineList.put(time, line);
293 | // 刷新界面
294 | invalidate();
295 | break;
296 | }
297 | }
298 | return true;
299 | }
300 |
301 | @Override
302 | protected void onDraw(Canvas canvas) {
303 | // 清空本地线段缓存
304 | Paint clearPaint = new Paint();
305 | clearPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
306 | localLineCacheCanvas.drawPaint(clearPaint);
307 | // 绘制本地未确认线段
308 | for (Line line : localLineList.values()) {
309 | Paint linePaint = new Paint(Paint.DITHER_FLAG);
310 | linePaint.setColor(line.getColor()); // 颜色
311 | linePaint.setStyle(Paint.Style.STROKE); // 实心
312 | linePaint.setStrokeWidth((float) line.getPaintWidth()); // 长度
313 | // 反锯齿
314 | linePaint.setAntiAlias(true);
315 | linePaint.setDither(true);
316 | if (line.isEraser()) {
317 | linePaint.setColor(Color.TRANSPARENT);
318 | linePaint.setStrokeWidth(ERASER_WIDTH);
319 | linePaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
320 | }
321 | List list = line.getPointList();
322 | Path linePath = new Path();
323 | // 本地线段不需要缩放
324 | float lineLastX = (float) list.get(0).getX();
325 | float lineLastY = (float) list.get(0).getY();
326 | linePath.moveTo(lineLastX, lineLastY);
327 | for (int i = 1; i < list.size(); i++) {
328 | float x = (float) list.get(i).getX();
329 | float y = (float) list.get(i).getY();
330 | linePath.quadTo(lineLastX, lineLastY, x, y);
331 | lineLastX = x;
332 | lineLastY = y;
333 | }
334 | // 绘制到缓冲区
335 | localLineCacheCanvas.drawPath(linePath, linePaint);
336 | }
337 | Paint bmpPaint = new Paint();
338 | // 绘制之前的缓存轨迹
339 | canvas.drawBitmap(cacheBitmap, 0, 0, bmpPaint);
340 | // 绘制本地线段缓存
341 | canvas.drawBitmap(localLineCacheBitmap, 0, 0, bmpPaint);
342 | }
343 |
344 | public MyPaintView(Context context) {
345 | super(context);
346 | }
347 |
348 | public MyPaintView(Context context, AttributeSet attrs) {
349 | super(context, attrs);
350 | }
351 |
352 | public MyPaintView(Context context, AttributeSet attrs, int defStyleAttr) {
353 | super(context, attrs, defStyleAttr);
354 | }
355 | }
356 |
--------------------------------------------------------------------------------
/app/src/main/java/com/app/superxlcr/mypaintboard/view/MainActivity.java:
--------------------------------------------------------------------------------
1 | package com.app.superxlcr.mypaintboard.view;
2 |
3 | import android.app.Dialog;
4 | import android.content.Context;
5 | import android.content.DialogInterface;
6 | import android.content.Intent;
7 | import android.content.res.Configuration;
8 | import android.os.Bundle;
9 | import android.os.Handler;
10 | import android.os.Message;
11 | import android.support.v7.app.AlertDialog;
12 | import android.support.v7.app.AppCompatActivity;
13 | import android.util.Log;
14 | import android.view.LayoutInflater;
15 | import android.view.Menu;
16 | import android.view.MenuInflater;
17 | import android.view.MenuItem;
18 | import android.view.View;
19 | import android.view.ViewGroup;
20 | import android.widget.AdapterView;
21 | import android.widget.ArrayAdapter;
22 | import android.widget.Button;
23 | import android.widget.EditText;
24 | import android.widget.ListView;
25 | import android.widget.TextView;
26 | import android.widget.Toast;
27 |
28 | import com.app.superxlcr.mypaintboard.R;
29 | import com.app.superxlcr.mypaintboard.controller.CommunicationController;
30 | import com.app.superxlcr.mypaintboard.controller.RoomController;
31 | import com.app.superxlcr.mypaintboard.controller.UserController;
32 | import com.app.superxlcr.mypaintboard.model.Protocol;
33 | import com.app.superxlcr.mypaintboard.model.Room;
34 | import com.app.superxlcr.mypaintboard.model.User;
35 | import com.app.superxlcr.mypaintboard.utils.LoadingDialogUtils;
36 | import com.app.superxlcr.mypaintboard.utils.MyLog;
37 |
38 | import org.json.JSONArray;
39 | import org.json.JSONException;
40 |
41 | import java.lang.ref.SoftReference;
42 | import java.util.ArrayList;
43 | import java.util.List;
44 |
45 | /**
46 | * Created by superxlcr on 2017/1/13.
47 | * 房间列表主界面
48 | */
49 |
50 | public class MainActivity extends BaseActivity {
51 |
52 | private static String TAG = MainActivity.class.getSimpleName();
53 |
54 | private static MyHandler handler = new MyHandler();
55 |
56 | private Button createRoomBtn;
57 | private Button updateRoomListBtn;
58 |
59 | private ListView roomListView;
60 | private List roomList;
61 | private MyAdapter adapter;
62 |
63 | private Dialog dialog; // 进度条
64 | private long time = 0; // 发送消息时间
65 |
66 | @Override
67 | protected void onCreate(Bundle savedInstanceState) {
68 | super.onCreate(savedInstanceState);
69 | setContentView(R.layout.activity_main);
70 |
71 | // 设置昵称
72 | if (UserController.getInstance().getUser() == null) {
73 | // 没有user说明没有登录,重新进行登录
74 | MyLog.d(TAG, "获取登录用户失败!");
75 | showToast("您还没有进行登录,请进行登录");
76 | Intent intent = new Intent(this, LoginActivity.class);
77 | startActivity(intent);
78 | finish();
79 | }
80 |
81 | // 初始化Handler
82 | handler.setReference(new SoftReference(this));
83 |
84 | // 初始化列表
85 | roomListView = (ListView) findViewById(R.id.room_list);
86 | roomList = new ArrayList<>();
87 | adapter = new MyAdapter(this, R.layout.item_room, roomList);
88 | roomListView.setAdapter(adapter);
89 | // listView item点击事件
90 | roomListView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
91 | @Override
92 | public void onItemClick(AdapterView> parent, View view, int position, long id) {
93 | // 进入房间
94 | Room room = roomList.get(position);
95 | time = System.currentTimeMillis();
96 | dialog = LoadingDialogUtils.showDialog(MainActivity.this, "正在加入房间...", true);
97 | dialog.setOnCancelListener(new DialogInterface.OnCancelListener() {
98 | @Override
99 | public void onCancel(DialogInterface dialogInterface) {
100 | // 关闭进度条
101 | LoadingDialogUtils.closeDialog(dialog);
102 | // 更新时间,使过去协议失效
103 | time += 1;
104 | // 退出房间
105 | RoomController.getInstance().exitRoom(MainActivity.this, handler, System.currentTimeMillis());
106 | }
107 | });
108 | RoomController.getInstance().joinRoom(MainActivity.this, handler, time, room.getId());
109 | }
110 | });
111 |
112 | // 初始化view
113 | createRoomBtn = (Button) findViewById(R.id.create_room);
114 | createRoomBtn.setOnClickListener(new View.OnClickListener() {
115 | @Override
116 | public void onClick(View v) {
117 | // 创建房间
118 | final EditText et = new EditText(MainActivity.this);
119 | new AlertDialog.Builder(MainActivity.this).setTitle("请输入房间名称").setView(et).setPositiveButton("确定", new DialogInterface.OnClickListener() {
120 | @Override
121 | public void onClick(DialogInterface dialogInterface, int which) {
122 | if (et.getText().toString().isEmpty()) {
123 | showToast("请输入房间名称");
124 | } else {
125 | // 创建房间
126 | String roomName = et.getText().toString();
127 | time = System.currentTimeMillis();
128 | dialog = LoadingDialogUtils.showDialog(MainActivity.this, "正在创建房间...", true);
129 | dialog.setOnCancelListener(new DialogInterface.OnCancelListener() {
130 | @Override
131 | public void onCancel(DialogInterface dialogInterface) {
132 | // 关闭进度条
133 | LoadingDialogUtils.closeDialog(dialog);
134 | // 更新时间,使过去协议失效
135 | time += 1;
136 | // 退出房间
137 | RoomController.getInstance().exitRoom(MainActivity.this, handler, System.currentTimeMillis());
138 | }
139 | });
140 | if (!RoomController.getInstance().createRoom(MainActivity.this, handler, time, roomName)) {
141 | // 没发送成功,关闭进度条
142 | LoadingDialogUtils.closeDialog(dialog);
143 | }
144 | }
145 | }
146 | }).setNegativeButton("取消", null).show();
147 | }
148 | });
149 | updateRoomListBtn = (Button) findViewById(R.id.update_room_list);
150 | updateRoomListBtn.setOnClickListener(new View.OnClickListener() {
151 | @Override
152 | public void onClick(View v) {
153 | // 更新房间列表
154 | updateRoomList();
155 | }
156 | });
157 |
158 | // 初次更新房间列表
159 | updateRoomList();
160 | }
161 |
162 | @Override
163 | protected void onResume() {
164 | super.onResume();
165 | User user = UserController.getInstance().getUser();
166 | String username = user.getUsername();
167 | String nickname = user.getNickname();
168 | setTitle(nickname + "(" + username + ")");
169 | }
170 |
171 | @Override
172 | public void onBackPressed() {
173 | // 断线,清除登录状态
174 | CommunicationController.getInstance(this).clearSocket();
175 | super.onBackPressed();
176 | }
177 |
178 | @Override
179 | public boolean onCreateOptionsMenu(Menu menu) {
180 | MenuInflater inflater = getMenuInflater();
181 | inflater.inflate(R.menu.menu_main_activity, menu);
182 | return true;
183 | }
184 |
185 | @Override
186 | public boolean onOptionsItemSelected(MenuItem item) {
187 | switch (item.getItemId()) {
188 | case R.id.edit_info: {
189 | // 编辑个人信息界面
190 | Intent intent = new Intent(MainActivity.this, EditInfoActivity.class);
191 | startActivity(intent);
192 | break;
193 | }
194 | case R.id.logout: {
195 | // 登录注销
196 | // 关闭与服务器连接
197 | CommunicationController.getInstance(MainActivity.this).clearSocket();
198 | // 返回登录界面
199 | Intent intent = new Intent(MainActivity.this, LoginActivity.class);
200 | startActivity(intent);
201 | finish();
202 | break;
203 | }
204 | }
205 | return true;
206 | }
207 |
208 | private void showToast(String msg) {
209 | Toast.makeText(this, msg, Toast.LENGTH_SHORT).show();
210 | }
211 |
212 | // 获取房间列表
213 | private void updateRoomList() {
214 | time = System.currentTimeMillis();
215 | dialog = LoadingDialogUtils.showDialog(this, "更新列表中...", true);
216 | dialog.setOnCancelListener(new DialogInterface.OnCancelListener() {
217 | @Override
218 | public void onCancel(DialogInterface dialogInterface) {
219 | // 关闭进度条
220 | LoadingDialogUtils.closeDialog(dialog);
221 | // 更新时间,使过去协议失效
222 | time += 1;
223 | }
224 | });
225 | RoomController.getInstance().getRoomList(this, handler, time);
226 | }
227 |
228 | static class MyHandler extends Handler {
229 |
230 | private SoftReference reference;
231 |
232 | public MyHandler() {
233 | }
234 |
235 | public void setReference(SoftReference reference) {
236 | this.reference = reference;
237 | }
238 |
239 | @Override
240 | public void handleMessage(Message msg) {
241 | // 处理指令
242 | MainActivity activity = reference.get();
243 | if (activity != null && msg != null && msg.obj != null && msg.obj instanceof Protocol) {
244 | Protocol protocol = (Protocol) msg.obj;
245 | int order = protocol.getOrder();
246 | long time = protocol.getTime();
247 | JSONArray content = protocol.getContent();
248 | if (time >= activity.time) { // 协议消息没有过期
249 | // 关闭进度条
250 | LoadingDialogUtils.closeDialog(activity.dialog);
251 | try {
252 | switch (order) {
253 | case Protocol.GET_ROOM_LIST: { // 获取房间列表
254 | List list = activity.roomList;
255 | int index = 0;
256 | int roomNumber = content.getInt(index++);
257 | // 刷新房间列表
258 | list.clear();
259 | for (int i = 0; i < roomNumber; i++) {
260 | int roomId = content.getInt(index++);
261 | String roomName = content.getString(index++);
262 | int roomMemberNumber = content.getInt(index++);
263 | Room room = new Room(null, roomId, roomName);
264 | room.setRoomMemberNumber(roomMemberNumber);
265 | list.add(room);
266 | }
267 | // 更新listView
268 | activity.adapter.notifyDataSetChanged();
269 | // 更新RoomController
270 | RoomController.getInstance().setList(new ArrayList(list));
271 | // 显示Toast
272 | showToast("房间列表更新成功");
273 | break;
274 | }
275 | case Protocol.CREATE_ROOM: { // 创建房间
276 | int roomId = content.getInt(0);
277 | String roomName = content.getString(1);
278 | // 保存房间
279 | Room room = new Room(UserController.getInstance().getUser(), roomId, roomName);
280 | RoomController.getInstance().setRoom(room);
281 | showToast("房间创建成功");
282 | // 跳转至房间界面
283 | Intent intent = new Intent(activity, RoomActivity.class);
284 | activity.startActivity(intent);
285 | break;
286 | }
287 | case Protocol.JOIN_ROOM: { // 加入房间
288 | int stateCode = content.getInt(0);
289 | switch (stateCode) {
290 | case Protocol.JOIN_ROOM_ALREADY_IN: { // 用户已在该房间
291 | showToast("您已在该房间中");
292 | break;
293 | }
294 | case Protocol.JOIN_ROOM_INVALID_ROOMID: { // 房间id错误
295 | showToast("该房间已失效,请更新房间列表");
296 | // 更新房间列表
297 | activity.updateRoomList();
298 | break;
299 | }
300 | case Protocol.JOIN_ROOM_SUCCESS: { // 加入房间成功
301 | int roomId = content.getInt(1);
302 | String roomName = content.getString(2);
303 | // 保存房间
304 | Room room = new Room(UserController.getInstance().getUser(), roomId, roomName);
305 | RoomController.getInstance().setRoom(room);
306 | showToast("加入房间成功");
307 | // 跳转至房间界面
308 | Intent intent = new Intent(activity, RoomActivity.class);
309 | activity.startActivity(intent);
310 | break;
311 | }
312 | case Protocol.JOIN_ROOM_UNKNOW_PRO: { // 未知错误
313 | showToast("出现未知错误");
314 | break;
315 | }
316 | }
317 | break;
318 | }
319 | }
320 | } catch (JSONException e) {
321 | MyLog.e(TAG, Log.getStackTraceString(e));
322 | showToast("协议内容解析错误");
323 | }
324 | }
325 | }
326 | }
327 |
328 | private void showToast(String msg) {
329 | if (reference != null && reference.get() != null) {
330 | Toast.makeText(reference.get(), msg, Toast.LENGTH_SHORT).show();
331 | }
332 | }
333 | }
334 |
335 | static class MyAdapter extends ArrayAdapter {
336 |
337 | private LayoutInflater inflater;
338 | private int resourceId;
339 | private List list;
340 |
341 | MyAdapter(Context context, int resource, List objects) {
342 | super(context, resource, objects);
343 | inflater = LayoutInflater.from(context);
344 | resourceId = resource;
345 | list = objects;
346 | }
347 |
348 | @Override
349 | public View getView(int position, View convertView, ViewGroup parent) {
350 | View view;
351 | TextView roomIdTV;
352 | TextView roomNameTV;
353 | TextView roomMemberNumberTV;
354 | // 初始化view
355 | if (convertView == null) {
356 | view = inflater.inflate(resourceId, parent, false);
357 | roomIdTV = (TextView) view.findViewById(R.id.room_id);
358 | roomNameTV = (TextView) view.findViewById(R.id.room_name);
359 | roomMemberNumberTV = (TextView) view.findViewById(R.id.room_member_number);
360 | view.setTag(new ViewHolder(roomIdTV, roomNameTV, roomMemberNumberTV));
361 | } else {
362 | view = convertView;
363 | ViewHolder holder = (ViewHolder) view.getTag();
364 | roomIdTV = holder.roomIdTV;
365 | roomNameTV = holder.roomNameTV;
366 | roomMemberNumberTV = holder.roomMemberNumberTV;
367 | }
368 | // 设置内容
369 | Room room = list.get(position);
370 | roomIdTV.setText(room.getId() + "");
371 | roomNameTV.setText(room.getRoomName());
372 | roomMemberNumberTV.setText(room.getRoomMemberNumber() + "");
373 | return view;
374 | }
375 |
376 | class ViewHolder {
377 | TextView roomIdTV;
378 | TextView roomNameTV;
379 | TextView roomMemberNumberTV;
380 |
381 | ViewHolder(TextView roomIdTV, TextView roomNameTV, TextView roomMemberNumberTV) {
382 | this.roomIdTV = roomIdTV;
383 | this.roomNameTV = roomNameTV;
384 | this.roomMemberNumberTV = roomMemberNumberTV;
385 | }
386 | }
387 | }
388 | }
389 |
--------------------------------------------------------------------------------