学习android不久,想实现一个聊天功能,发现主要时间还是花在了聊天界面的布局上面。主要功能类似于一个聊天室。客户端启动后,发送的内容都通过socket传给服务端,服务端收到消息后,遍历socket列表中每一个socket,当然除掉消息来源的这个socket,然后发送该消息。
首先附上xml的代码
main.xml聊天的主界面
聊天界面的对话框是一个listview,但是listview的每一个item的布局取决于消息的来源
所以自定义一个adaptar,继承BaseAdapter,最重要的是getview的重写。我这里在获取内容的时候,就加上标志,根据标志判断用哪个布局。感觉总有点怪怪的。如果有好方法,还请大神指教。
package com.hitwhwlp.love;
import java.util.HashMap;
import java.util.List;
import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.ImageView;
import android.widget.TextView;
public class MyAdapter extends BaseAdapter {
private Context context;
private List
> list;
public MyAdapter(Context context, List
> listItems) {
// TODO Auto-generated constructor stubthis.context = context;
this.list = listItems;
this.context = context;
}
@Override
public int getCount() {
return list.size();
}
@Override
public Object getItem(int position) {
return list.get(position);
}
@Override
public long getItemId(int position) {
return position;
}
// ViewHolder静态类
static class ViewHolder {
public TextView content;
}
// 重写getView
//这边写的。。。需要优化
@Override
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder holder;
// if (convertView == null) {
holder = new ViewHolder();
if ("0" == list.get(position).get("flag").toString()) {
convertView = LayoutInflater.from(context).inflate(
R.layout.left_item, null);
holder.content = (TextView) convertView.findViewById(R.id.content1);
} else {
convertView = LayoutInflater.from(context).inflate(
R.layout.right_item, null);
holder.content = (TextView) convertView.findViewById(R.id.content2);
}
// convertView.setTag(holder);
// } else {
// holder = (ViewHolder) convertView.getTag();
// }
holder.content.setText(list.get(position).get("content").toString());
return convertView;
}
}
package com.hitwhwlp.love;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ImageButton;
import android.widget.ListView;
import android.widget.Toast;
public class MainActivity extends Activity {
private ImageButton faceButton;
private EditText inputText;
private Button sendButton;
Handler handler;
ClientThread clientThread;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
inputText = (EditText) findViewById(R.id.input);
sendButton = (Button) findViewById(R.id.send);
inputText = (EditText) findViewById(R.id.input);
sendButton = (Button) findViewById(R.id.send);
final List
> ListItems = new ArrayList
>();
ListView list = (ListView) findViewById(R.id.list);
final MyAdapter adapter = new MyAdapter(MainActivity.this, ListItems);
list.setAdapter(adapter);
handler = new Handler()
{
@Override
public void handleMessage(Message msg) {
// 如果消息来自于子线程
if (msg.what == 0x123) {
// 将读取的内容追加显示在listview
HashMap
tempListItems = new HashMap
(); tempListItems.put("flag", "0"); tempListItems.put("content", msg.obj.toString()); ListItems.add(tempListItems); adapter.notifyDataSetChanged(); } } }; clientThread = new ClientThread(handler); // 客户端启动ClientThread线程创建网络连接、读取来自服务器的数据 new Thread(clientThread).start(); // ① sendButton.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { // TODO Auto-generated method stub try { // 当用户按下发送按钮后,将用户输入的数据封装成Message, // 然后发送给子线程的Handler Message msg = new Message(); msg.what = 0x345; msg.obj = inputText.getText().toString(); clientThread.revHandler.sendMessage(msg); HashMap
tempListItems = new HashMap
(); tempListItems.put("flag", "1"); tempListItems.put("content",inputText.getText().toString() ); ListItems.add(tempListItems); adapter.notifyDataSetChanged(); // 清空input文本框 inputText.setText(""); } catch (Exception e) { e.printStackTrace(); } } }); } }
至于如何接收来自服务端的消息,以及将发送的内容写到网络,上ClientThread(由于本人才结束大一的生活,网络没有学习过,只看了一点点,这一块是参考《Android疯狂讲义》的)
package com.hitwhwlp.love;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.Socket;
import java.net.SocketTimeoutException;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
public class ClientThread implements Runnable
{
private Socket s;
// 定义向UI线程发送消息的Handler对象
private Handler handler;
// 定义接收UI线程的消息的Handler对象
public Handler revHandler;
// 该线程所处理的Socket所对应的输入流
BufferedReader br = null;
OutputStream os = null;
int flag;
public ClientThread(Handler handler)
{
this.handler = handler;
}
public void run()
{
try
{
s = new Socket("192.168.1.230", 30000);//这是我的电脑的本机ip地址
br = new BufferedReader(new InputStreamReader(
s.getInputStream()));
os = s.getOutputStream();
// 启动一条子线程来读取服务器响应的数据
new Thread()
{
@Override
public void run()
{
String content = null;
// 不断读取Socket输入流中的内容。
try
{
while ((content = br.readLine()) != null)
{
// 每当读到来自服务器的数据之后,发送消息通知程序界面显示该数据
Message msg = new Message();
msg.what = 0x123;
msg.obj = content;
handler.sendMessage(msg);
}
}
catch (IOException e)
{
e.printStackTrace();
}
}
}.start();
// 为当前线程初始化Looper
Looper.prepare();
// 创建revHandler对象
revHandler = new Handler()
{
@Override
public void handleMessage(Message msg)
{
// 接收到UI线程中用户输入的数据
if (msg.what == 0x345)
{
// 将用户在文本框内输入的内容写入网络
try
{
os.write((msg.obj.toString() + "\r\n")
.getBytes("utf-8"));
}
catch (Exception e)
{
e.printStackTrace();
}
}
}
};
// 启动Looper
Looper.loop();
}
catch (SocketTimeoutException e1)
{
System.out.println("网络连接超时!!");
}
catch (Exception e)
{
e.printStackTrace();
}
}
}
![闭嘴](http://static.blog.csdn.net/xheditor/xheditor_emot/default/shutup.gif)
public class MyServer
{
// 定义保存所有Socket的ArrayList
public static ArrayList<Socket> socketList
= new ArrayList<Socket>();
public static void main(String[] args)
throws IOException
{
ServerSocket ss = new ServerSocket(30000);
while(true)
{
// 此行代码会阻塞,将一直等待别人的连接
Socket s = ss.accept();
socketList.add(s);
// 每当客户端连接后启动一条ServerThread线程为该客户端服务
new Thread(new ServerThread(s)).start();
}
}
}
// 负责处理每个线程通信的线程类
public class ServerThread implements Runnable {
// 定义当前线程所处理的Socket
Socket s = null;
// 该线程所处理的Socket所对应的输入流
BufferedReader br = null;
public ServerThread(Socket s) throws IOException {
this.s = s;
// 初始化该Socket对应的输入流
br = new BufferedReader(new InputStreamReader(s.getInputStream(),
"utf-8"));
}
public void run() {
try {
String content = null;
// 采用循环不断从Socket中读取客户端发送过来的数据
while ((content = readFromClient()) != null) {
// 遍历socketList中的每个Socket,
// 将读到的内容向每个Socket发送一次,除了自己(这个方法自己想的,如果有更高端方法,还请大神赐教,网络的现在暂时不懂)
for (Socket i : MyServer.socketList) {
if (i != s) {
OutputStream os = i.getOutputStream();
os.write((content + "\n").getBytes("utf-8"));
}
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
// 定义读取客户端数据的方法
private String readFromClient() {
try {
return br.readLine();
}
// 如果捕捉到异常,表明该Socket对应的客户端已经关闭
catch (IOException e) {
// 删除该Socket。
MyServer.socketList.remove(s);
}
return null;
}
}
上一张效果图,这是拿两个机子的对话