一、前言:
一般情况下从TCP服务器读取数据是放在一个线程里读的,但是刷新界面又不得不放在线程外面,所以需要用消息传递把线程里从TCP里获得的数据传送出来,然后根据数据对页面进行相应的刷新。
二、业务逻辑:
这里包含2个layout,第一个用于登陆的(即输入服务器对应的IP和端口号),点击确定进行跳转到相应的监控界面,监控界面包括加热、关闭、和显示温度3个按钮,以及一个用于绘制温度的SurfaceView。
三、详细介绍:
3-1、2个activity介绍:
登陆页面对应的activity,从上面的代码可以看出:29~31行使用intent进行页面跳转,然后所有逻辑均在ControlActivity里实现了。
1 public class MainActivity extends ActionBarActivity 2 { 3 private final String TAG = "MainActivity"; 4 private EditText et01; 5 private EditText et02; 6 private Button btOK; 7 private Button btCancel; 8 public static String userIP = "192.168.1.130"; //IP和端口号 9 public static int userPort = 8000; 10 public static int wen_du; //当前温度 11 public static int shui_wei; //当前水位 12 public static int state; //当前状态0关闭;1烧水;2保温 13 @Override 14 protected void onCreate(Bundle savedInstanceState) { 15 super.onCreate(savedInstanceState); 16 setContentView(R.layout.activity_main); 17 et01 = (EditText)findViewById(R.id.et_01); 18 et02 = (EditText)findViewById(R.id.et_02); 19 btOK = (Button)findViewById(R.id.bt_OK); 20 btCancel = (Button)findViewById(R.id.bt_Cancel); 21 22 23 btOK.setOnClickListener(new OnClickListener(){ 24 public void onClick(View v) 25 { 26 //userIP = et01.getText().toString(); 27 //userPort = Integer.parseInt(et02.getText().toString()); 28 //跳到控制界面 29 Intent intent = new Intent(MainActivity.this,ControlActivity.class); 30 Log.i(TAG, "跳转前"); 31 startActivity(intent); 32 } 33 }); 34 btCancel.setOnClickListener(new OnClickListener(){ 35 public void onClick(View v) 36 { 37 et01.setText(""); 38 et02.setText(""); 39 } 40 }); 41 42 } 43 44 45 @Override 46 public boolean onCreateOptionsMenu(Menu menu) { 47 // Inflate the menu; this adds items to the action bar if it is present. 48 getMenuInflater().inflate(R.menu.main, menu); 49 return true; 50 } 51 52 @Override 53 public boolean onOptionsItemSelected(MenuItem item) { 54 // Handle action bar item clicks here. The action bar will 55 // automatically handle clicks on the Home/Up button, so long 56 // as you specify a parent activity in AndroidManifest.xml. 57 int id = item.getItemId(); 58 if (id == R.id.action_settings) { 59 return true; 60 } 61 return super.onOptionsItemSelected(item); 62 } 63 }
另一个activity的框架如下图:主要的有①、②、③三个函数,另外三个是Callback附带要实现的。其主要逻辑为:在onCreate中实例化按钮和surfaceView,然后对按钮进行事件绑定;每当按钮事件触发,则启动线程和TCP服务器进行通信;线程将处理的结果通过msg传给Handler,Handler根据相应消息来更新页面。
全部代码:1 public class ControlActivity extends Activity implements Callback { 2 3 private final String TAG = "ControlActivity"; 4 private final String mAddress = MainActivity.userIP; 5 private final int mPort = MainActivity.userPort; 6 private Socket socket = null; 7 private Button btHeat,btShut,btUpdata; 8 private SurfaceView mSurface; //绘图区 9 private SurfaceHolder mHolder; 10 //消息句柄(线程里无法进行界面更新,所以要把消息从线程里发送出来在消息句柄里进行处理) 11 public Handler myHandler = new Handler() { 12 @Override 13 public void handleMessage(Message msg) 14 { 15 Bundle bundle = msg.getData(); 16 String now = bundle.getString("msg"); 17 //SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");//设置日期格式 18 if (msg.what == 0x01) 19 { 20 toast_show("饮水机开始加热!"); 21 } 22 else if (msg.what == 0x02) 23 { 24 toast_show("饮水机关闭!"); 25 } 26 else if (msg.what == 0x03) 27 { 28 toast_show("饮水机实时状态更新!"+" "+MainActivity.wen_du+" "+MainActivity.shui_wei); 29 draw(MainActivity.wen_du); 30 } 31 else 32 { 33 toast_show("出现错误!"); 34 } 35 } 36 //toast显示用 37 private void toast_show(String msg) { 38 Toast toast = Toast.makeText(getApplicationContext(), 39 msg, Toast.LENGTH_LONG); 40 toast.setGravity(Gravity.CENTER, 0, 0); 41 toast.show(); 42 } 43 //画图像 44 private void draw(int wen_du) { 45 int y = 260 - wen_du * 2; 46 Canvas canvas = mHolder.lockCanvas(); 47 Paint mPaint = new Paint(); 48 mPaint.setColor(Color.WHITE); 49 canvas.drawRect(40, 50, 60, 280, mPaint); 50 Paint paintCircle = new Paint(); 51 paintCircle.setColor(Color.RED); 52 Paint paintLine = new Paint(); 53 paintLine.setColor(Color.BLUE); 54 canvas.drawRect(40, y, 60, 280, paintCircle); 55 canvas.drawCircle(50, 300, 25, paintCircle); 56 int ydegree = 260; 57 int tem = 0;//刻度0~100 58 while (ydegree > 55) { 59 canvas.drawLine(60, ydegree, 67, ydegree, mPaint); 60 if (ydegree % 20 == 0) { 61 canvas.drawLine(60, ydegree, 72, ydegree, paintLine); 62 canvas.drawText(tem + "", 70, ydegree + 4, mPaint); 63 tem+=10; 64 } 65 ydegree = ydegree - 2; 66 } 67 mHolder.unlockCanvasAndPost(canvas);// 更新屏幕显示内容 68 } 69 }; 70 71 protected void onCreate(Bundle savedInstanceState) 72 { 73 super.onCreate(savedInstanceState); 74 setContentView(R.layout.control_activity); 75 btHeat = (Button)findViewById(R.id.bt_heat); 76 btShut = (Button)findViewById(R.id.bt_shut); 77 btUpdata = (Button)findViewById(R.id.bt_updata); 78 mSurface = (SurfaceView) findViewById(R.id.surface); 79 mHolder = mSurface.getHolder(); 80 mHolder.addCallback(this); 81 82 btHeat.setOnClickListener(new OnClickListener() { 83 @Override 84 public void onClick(View v) 85 { 86 String orderMsg="Heat"; 87 //启动线程 向服务器发送和接收信息 88 Log.i(TAG, "Start thread"); 89 new MyThread(orderMsg).start(); 90 } 91 }); 92 93 btHeat.setOnClickListener(new OnClickListener() { 94 @Override 95 public void onClick(View v) 96 { 97 String orderMsg="Heat"; 98 //启动线程 向服务器发送和接收信息 99 Log.i(TAG, "Start thread"); 100 new MyThread(orderMsg).start(); 101 } 102 }); 103 104 btShut.setOnClickListener(new OnClickListener() { 105 @Override 106 public void onClick(View v) 107 { 108 String orderMsg="Shut"; 109 //启动线程 向服务器发送和接收信息 110 Log.i(TAG, "Start thread"); 111 new MyThread(orderMsg).start(); 112 } 113 }); 114 115 btUpdata.setOnClickListener(new OnClickListener() { 116 @Override 117 public void onClick(View v) 118 { 119 String orderMsg="Updata"; 120 //启动线程 向服务器发送和接收信息 121 Log.i(TAG, "Start thread"); 122 new MyThread(orderMsg).start(); 123 } 124 }); 125 } 126 127 class MyThread extends Thread 128 { 129 String orderMsg; 130 MyThread(String str) 131 { 132 orderMsg=str; 133 } 134 @SuppressLint("SimpleDateFormat") 135 public void run() 136 { 137 OutputStream out = null; 138 InputStream in = null; 139 DataInputStream DataIn = null;//数据传输输入输出流 140 DataOutputStream DataOut = null; 141 byte data_of_get_server = 0;//从服务器返回的数据 142 Message msg = new Message();//消息 143 Bundle bundle = new Bundle(); 144 bundle.clear(); 145 try 146 { 147 socket = new Socket(); 148 socket.connect(new InetSocketAddress(mAddress, mPort), 8000); 149 150 //输入输出流实例化 151 out=socket.getOutputStream(); 152 in=socket.getInputStream(); 153 DataIn = new DataInputStream(in); 154 DataOut=new DataOutputStream(out); 155 156 //读取服务器的返回数据 157 //服务器采用单byte数据进行发送 158 /* 159 TCP客户端:输入命令从服务器获得数据 160 PS:服务器只接受1个char,返回也是一个char,上述数据均为16进制 161 */ 162 if(orderMsg.equals("Heat"))//加热命令 163 { 164 msg.what = 0x01;//消息类别 165 DataOut.writeByte('0'); 166 Log.i(TAG, "flush 前"); 167 out.flush(); 168 Log.i(TAG, "flush 后"); 169 data_of_get_server=DataIn.readByte(); 170 Log.i(TAG, "读取数据后"); 171 } 172 else if(orderMsg.equals("Shut")) 173 { 174 msg.what = 0x02;//消息类别 175 DataOut.writeByte('0');//停止加热 176 out.flush(); 177 data_of_get_server=DataIn.readByte(); 178 } 179 else if(orderMsg.equals("Updata")) 180 { 181 msg.what = 0x03;//消息类别 182 DataOut.writeByte('w');//刷新温度信息 183 out.flush(); 184 data_of_get_server=DataIn.readByte(); 185 MainActivity.wen_du=data_of_get_server; 186 187 DataOut.writeByte('s');//刷新深度信息 188 out.flush(); 189 data_of_get_server=DataIn.readByte(); 190 MainActivity.shui_wei=data_of_get_server; 191 } 192 //将消息发送给UI刷新消息句柄处 193 bundle.putByte("msg",data_of_get_server); 194 msg.setData(bundle); 195 myHandler.sendMessage(msg); 196 } 197 catch(Exception e){ 198 e.printStackTrace(); 199 //Intent intent = new Intent(ControlActivity.this,MainActivity.class); 200 //Log.i(TAG, "跳转前"); 201 //startActivity(intent); 202 //将消息发送给UI刷新消息句柄处 203 msg.what = 0x04;//消息类别 204 bundle.putByte("msg",data_of_get_server); 205 msg.setData(bundle); 206 myHandler.sendMessage(msg); 207 }finally{ 208 try{ 209 if(in!=null)in.close();Log.i(TAG, "读取数据后1"); 210 if(out!=null)out.close();Log.i(TAG, "读取数据后2"); 211 if(DataOut!=null)DataOut.close();Log.i(TAG, "读取数据后3"); 212 if(DataIn!=null)DataIn.close();Log.i(TAG, "读取数据后4"); 213 if(socket!=null)socket.close();Log.i(TAG, "读取数据后5"); 214 }catch(Exception e){} 215 } 216 } 217 } 218 219 @Override 220 public void surfaceCreated(SurfaceHolder holder) { 221 // TODO Auto-generated method stub 222 223 } 224 225 @Override 226 public void surfaceChanged(SurfaceHolder holder, int format, int width, 227 int height) { 228 // TODO Auto-generated method stub 229 230 } 231 232 @Override 233 public void surfaceDestroyed(SurfaceHolder holder) { 234 // TODO Auto-generated method stub 235 236 } 237 }
3-2、消息传递详细介绍:
如下,第9-10行要加入回调函数;12-21、23-32、34-43以及45-54分别是几个按钮的点击监听函数,其中对于不同的情况,通过设置orderMsg进行区别,然后启动相应的线程和服务器通信。
1 protected void onCreate(Bundle savedInstanceState) 2 { 3 super.onCreate(savedInstanceState); 4 setContentView(R.layout.control_activity); 5 btHeat = (Button)findViewById(R.id.bt_heat); 6 btShut = (Button)findViewById(R.id.bt_shut); 7 btUpdata = (Button)findViewById(R.id.bt_updata); 8 mSurface = (SurfaceView) findViewById(R.id.surface); 9 mHolder = mSurface.getHolder(); 10 mHolder.addCallback(this); 11 12 btHeat.setOnClickListener(new OnClickListener() { 13 @Override 14 public void onClick(View v) 15 { 16 String orderMsg="Heat"; 17 //启动线程 向服务器发送和接收信息 18 Log.i(TAG, "Start thread"); 19 new MyThread(orderMsg).start(); 20 } 21 }); 22 23 btHeat.setOnClickListener(new OnClickListener() { 24 @Override 25 public void onClick(View v) 26 { 27 String orderMsg="Heat"; 28 //启动线程 向服务器发送和接收信息 29 Log.i(TAG, "Start thread"); 30 new MyThread(orderMsg).start(); 31 } 32 }); 33 34 btShut.setOnClickListener(new OnClickListener() { 35 @Override 36 public void onClick(View v) 37 { 38 String orderMsg="Shut"; 39 //启动线程 向服务器发送和接收信息 40 Log.i(TAG, "Start thread"); 41 new MyThread(orderMsg).start(); 42 } 43 }); 44 45 btUpdata.setOnClickListener(new OnClickListener() { 46 @Override 47 public void onClick(View v) 48 { 49 String orderMsg="Updata"; 50 //启动线程 向服务器发送和接收信息 51 Log.i(TAG, "Start thread"); 52 new MyThread(orderMsg).start(); 53 } 54 }); 55 }
从上面知道,我们必须有一个MyThread的类:构造函数就是把上面说的用于区分命令的orderMsg赋值给MyThread成员变量,然后在run函数中:11-14行来定义用于和TCP服务器通信的输入输出流;第15行的变量是记录从服务器返回的数据(这里服务器每次只返回一个byte类,这个取决于通信协议的约定!);第16-18行实例化的msg、bundle用于传送消息,对应的第77-80行,想要发送消息要:①首先设置消息类别(这里出错消息类别为0x04:msg.what = 0x04;加热为0x01;停止加热为0x02等)②然后用bundle将信息合成bundle.putByte("msg",data_of_get_server);其中第一个string为key,第二个为value ③然后将msg的信息设置为handle,即:msg.setData(bundle); ④最后用myHandler将消息发出:myHandler.sendMessage(msg);
1 class MyThread extends Thread 2 { 3 String orderMsg; 4 MyThread(String str) 5 { 6 orderMsg=str; 7 } 8 @SuppressLint("SimpleDateFormat") 9 public void run() 10 { 11 OutputStream out = null; 12 InputStream in = null; 13 DataInputStream DataIn = null;//数据传输输入输出流 14 DataOutputStream DataOut = null; 15 byte data_of_get_server = 0;//从服务器返回的数据 16 Message msg = new Message();//消息 17 Bundle bundle = new Bundle(); 18 bundle.clear(); 19 try 20 { 21 socket = new Socket(); 22 socket.connect(new InetSocketAddress(mAddress, mPort), 8000); 23 24 //输入输出流实例化 25 out=socket.getOutputStream(); 26 in=socket.getInputStream(); 27 DataIn = new DataInputStream(in); 28 DataOut=new DataOutputStream(out); 29 30 //读取服务器的返回数据 31 //服务器采用单byte数据进行发送 32 /* 33 TCP客户端:输入命令从服务器获得数据 34 PS:服务器只接受1个char,返回也是一个char,上述数据均为16进制 35 */ 36 if(orderMsg.equals("Heat"))//加热命令 37 { 38 msg.what = 0x01;//消息类别 39 DataOut.writeByte('0'); 40 Log.i(TAG, "flush 前"); 41 out.flush(); 42 Log.i(TAG, "flush 后"); 43 data_of_get_server=DataIn.readByte(); 44 Log.i(TAG, "读取数据后"); 45 } 46 else if(orderMsg.equals("Shut")) 47 { 48 msg.what = 0x02;//消息类别 49 DataOut.writeByte('0');//停止加热 50 out.flush(); 51 data_of_get_server=DataIn.readByte(); 52 } 53 else if(orderMsg.equals("Updata")) 54 { 55 msg.what = 0x03;//消息类别 56 DataOut.writeByte('w');//刷新温度信息 57 out.flush(); 58 data_of_get_server=DataIn.readByte(); 59 MainActivity.wen_du=data_of_get_server; 60 61 DataOut.writeByte('s');//刷新深度信息 62 out.flush(); 63 data_of_get_server=DataIn.readByte(); 64 MainActivity.shui_wei=data_of_get_server; 65 } 66 //将消息发送给UI刷新消息句柄处 67 bundle.putByte("msg",data_of_get_server); 68 msg.setData(bundle); 69 myHandler.sendMessage(msg); 70 } 71 catch(Exception e){ 72 e.printStackTrace(); 73 //Intent intent = new Intent(ControlActivity.this,MainActivity.class); 74 //Log.i(TAG, "跳转前"); 75 //startActivity(intent); 76 //将消息发送给UI刷新消息句柄处 77 msg.what = 0x04;//消息类别 78 bundle.putByte("msg",data_of_get_server); 79 msg.setData(bundle); 80 myHandler.sendMessage(msg); 81 }finally{ 82 try{ 83 if(in!=null)in.close();Log.i(TAG, "读取数据后1"); 84 if(out!=null)out.close();Log.i(TAG, "读取数据后2"); 85 if(DataOut!=null)DataOut.close();Log.i(TAG, "读取数据后3"); 86 if(DataIn!=null)DataIn.close();Log.i(TAG, "读取数据后4"); 87 if(socket!=null)socket.close();Log.i(TAG, "读取数据后5"); 88 }catch(Exception e){} 89 } 90 } 91 }
接下来就是myHandler了:第6-7行是取消息的过程,其和放消息有种逆过程的感觉,首先用bundle取出消息:Bundle bundle = msg.getData(); 然后通过String now = bundle.getString("msg");获得键值为“msg”的value,然后分类处理即可。其中toast_show是自己封装的用于显示toast消息的函数,draw是用来绘制那个温度计的函数。
1 //消息句柄(线程里无法进行界面更新,所以要把消息从线程里发送出来在消息句柄里进行处理) 2 public Handler myHandler = new Handler() { 3 @Override 4 public void handleMessage(Message msg) 5 { 6 Bundle bundle = msg.getData(); 7 String now = bundle.getString("msg"); 8 //SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");//设置日期格式 9 if (msg.what == 0x01) 10 { 11 toast_show("饮水机开始加热!"); 12 } 13 else if (msg.what == 0x02) 14 { 15 toast_show("饮水机关闭!"); 16 } 17 else if (msg.what == 0x03) 18 { 19 toast_show("饮水机实时状态更新!"+" "+MainActivity.wen_du+" "+MainActivity.shui_wei); 20 draw(MainActivity.wen_du); 21 } 22 else 23 { 24 toast_show("出现错误!"); 25 } 26 } 27 //toast显示用 28 private void toast_show(String msg) { 29 Toast toast = Toast.makeText(getApplicationContext(), 30 msg, Toast.LENGTH_LONG); 31 toast.setGravity(Gravity.CENTER, 0, 0); 32 toast.show(); 33 } 34 //画图像 35 private void draw(int wen_du) { 36 int y = 260 - wen_du * 2; 37 Canvas canvas = mHolder.lockCanvas(); 38 Paint mPaint = new Paint(); 39 mPaint.setColor(Color.WHITE); 40 canvas.drawRect(40, 50, 60, 280, mPaint); 41 Paint paintCircle = new Paint(); 42 paintCircle.setColor(Color.RED); 43 Paint paintLine = new Paint(); 44 paintLine.setColor(Color.BLUE); 45 canvas.drawRect(40, y, 60, 280, paintCircle); 46 canvas.drawCircle(50, 300, 25, paintCircle); 47 int ydegree = 260; 48 int tem = 0;//刻度0~100 49 while (ydegree > 55) { 50 canvas.drawLine(60, ydegree, 67, ydegree, mPaint); 51 if (ydegree % 20 == 0) { 52 canvas.drawLine(60, ydegree, 72, ydegree, paintLine); 53 canvas.drawText(tem + "", 70, ydegree + 4, mPaint); 54 tem+=10; 55 } 56 ydegree = ydegree - 2; 57 } 58 mHolder.unlockCanvasAndPost(canvas);// 更新屏幕显示内容 59 } 60 };
本文链接:http://www.cnblogs.com/zjutlitao/p/4230360.html
更多精彩:http://www.cnblogs.com/zjutlitao/
工程链接:http://pan.baidu.com/s/1i3zhMVr
GitHub链接:https://github.com/beautifulzzzz/SmartDrink