1、复杂ListView的展示
聊天模块其实就是一个复杂的ListView,在数据适配器中,从application中获取到当前登录的用户,与聊天消息中的from参数进行比较,如果两者相同则说明是自己发送的消息,那么ListView中就显示发送消息的布局,否则显示接收消息的布局。适配器的代码如下:
/**
* 聊天消息的适配器
*
* @author ZHY
*
*/
public class ChartMessageAdapter extends ArrayAdapter<QQMessage> {
ImApp app;
public ChartMessageAdapter(Context context, List<QQMessage> objects) {
super(context, 0, objects);
Activity activity = (Activity) context;
app = (ImApp) activity.getApplication();
}
/**
* 根据集合中的position位置,返回不同的值,代表不同的布局 0代表自己发送的消息 1代表接收到的消息
*
*/
@Override
public int getItemViewType(int position) {// 这个方法是给getView用得
QQMessage msg = getItem(position);
// 消息来自谁,如果消息来自我自己,说明是我发送的
if (msg.from == app.getMyAccount()) {
// 我自己的消息,发送
return 0;
} else {
return 1;
}
}
/**
* 两种布局
*/
@Override
public int getViewTypeCount() {
return 2;
}
class ViewHolder {
TextView time;
TextView content;
ImageView head;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
int type = getItemViewType(position);
if (0 == type) {
// 发送的布局
ViewHolder holder;
if (convertView == null) {
convertView = View.inflate(getContext(),
R.layout.item_chat_send, null);
holder = new ViewHolder();
holder.time = (TextView) convertView.findViewById(R.id.time);
holder.content = (TextView) convertView
.findViewById(R.id.content);
holder.head = (ImageView) convertView.findViewById(R.id.head);
convertView.setTag(holder);
} else {
holder = (ViewHolder) convertView.getTag();
}
// 设置值
QQMessage msg = getItem(position);
holder.time.setText(msg.sendTime);
holder.head.setImageResource(msg.fromAvatar);
holder.content.setText(msg.content);
return convertView;
} else {
// 接收的布局
ViewHolder holder;
if (convertView == null) {
convertView = View.inflate(getContext(),
R.layout.item_chat_receive, null);
holder = new ViewHolder();
holder.time = (TextView) convertView.findViewById(R.id.time);
holder.content = (TextView) convertView
.findViewById(R.id.content);
holder.head = (ImageView) convertView.findViewById(R.id.head);
convertView.setTag(holder);
} else {
holder = (ViewHolder) convertView.getTag();
}
// 设置值
QQMessage msg = getItem(position);
holder.head.setImageResource(msg.fromAvatar);
holder.time.setText(msg.sendTime);
holder.content.setText(msg.content);
return convertView;
}
}
}
2、消息处理
在聊天页面中添加接收消息的监听,如果接收到的消息是聊天消息,那么就将接收到的消息添加到messages消息集合中,然后通知适配器数据发生了变化,进而刷新界面。当用户点击发送按钮的时候,封装消息,将消息发送个给服务器。聊天界面的代码如下:
/**
* 聊天界面
*
* @author ZHY
*
*/
public class ChartActivity extends Activity {
@ViewInject(R.id.title)
private TextView title;
@ViewInject(R.id.listview)
private ListView listView;
@ViewInject(R.id.input)
private EditText input;
private ImApp app;
private ChartMessageAdapter adapter;
// 这是点击的用户,也就是你要发消息给谁
private String toNick;// 要发送给谁
private long toAccount;// y要发送的账号
private long fromAccount;// 我的账号,我要跟谁睡聊天
private String inputStr;// 聊天内容
// 聊天消息的集合
private List<QQMessage> messages = new ArrayList<QQMessage>();
// 点击发送按钮的时候,获得输入框中的内容,将内容封装到messages集合中,聊调消息是一个QQMessageJava对象,一定包含四个字段
@OnClick(R.id.send)
public void send(View view) {
// Toast.makeText(getBaseContext(), "ddadasf", 0).show();
inputStr = input.getText().toString().trim();
// 清空输入框
input.setText("");
final QQMessage msg = new QQMessage();
if ("".equals(inputStr)) {
Toast.makeText(getApplicationContext(), "不能为空", 0).show();
return;
}
msg.type = QQMessageType.MSG_TYPE_CHAT_P2P;
msg.from = fromAccount;
msg.to = toAccount;
msg.content = inputStr;
msg.fromAvatar = R.drawable.ic_launcher;
messages.add(msg);
// 数据集合有了,创建适配器
// 刷新消息
if (adapter != null) {
adapter.notifyDataSetChanged();
}
// 展示到最新发送的消息处
if (messages.size() > 0) {
listView.setSelection(messages.size() - 1);
}
// 发送消息到服务器--子线程
ThreadUtils.runInSubThread(new Runnable() {
public void run() {
try {
app.getMyConn().sendMessage(msg);
} catch (IOException e) {
e.printStackTrace();
}
}
});
}
/**
* 接收消息,使用监听器
*/
private OnMessageListener listener = new OnMessageListener() {
public void onReveive(final QQMessage msg) {
// 注意onReveive是子线程,更新界面一定要在主线程中
ThreadUtils.runInUiThread(new Runnable() {
public void run() {
// 服务器返回回来的消息
System.out.println(msg.content);
if (QQMessageType.MSG_TYPE_CHAT_P2P.equals(msg.type)) {
messages.add(msg);// 把消息加到消息集合中,这是最新的消息
// 刷新消息
if (adapter != null) {
adapter.notifyDataSetChanged();
}
// 展示到最新发送的消息出
if (messages.size() > 0) {
listView.setSelection(messages.size() - 1);
}
}
}
});
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_chart);
ViewUtils.inject(this);// 相当于findViewByID
app = (ImApp) getApplication();
// 开启监听
app.getMyConn().addOnMessageListener(listener);
// 聊天的界面是复杂的listView。发送消息的条目是item_chat_send.xml布局,接收到的消息现实的条目是item_chat_receive.xml布局
/**
* 聊天的消息 content "约不?" type chatp2p from 老王 to 大头
*/
// 获得从上一个界面获取的账号与昵称
Intent intent = getIntent();
toNick = intent.getStringExtra("nick");
toAccount = intent.getLongExtra("account", 0);
title.setText("与" + toNick + "聊天中");
fromAccount = app.getMyAccount();// 我的账户
adapter = new ChartMessageAdapter(this, messages);
listView.setAdapter(adapter);
}
@Override
protected void onDestroy() {
// TODO Auto-generated method stub
super.onDestroy();
app.getMyConn().removeOnMessageListener(listener);
}
}
至此,基于Socket的Android即时通讯软件就介绍完了,功能很丑陋,原理基本能说得通,自己留一份备忘,同时也希望能帮到别人。源码的话,稍后整理发出来,整个项目运行起来的效果如下。