使用Android 22.0.1和Smack 4.1.1 / Openfire 3.10.2实现一个简单的XMPP聊天应用程序。
实施了ChatActivity,ChatArrayAdapter和消息:
ChatActivity
public class ChatActivity extends Activity {
private static final String XMPP_HOST = "myhost";
private static final Integer XMPP_PORT = 5222;
private ChatArrayAdapter adapter;
private ListView listView;
private EditText editText;
private AbstractXMPPConnection abstractXMPPConnection;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_discuss);
/* Get username, either "foo" or "bar" */
final String username = getIntent().getStringExtra("username");
try {
abstractXMPPConnection = new XMPConnectionInitTask().execute(username).get();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
listView = (ListView) findViewById(R.id.listView1);
adapter = new ChatArrayAdapter(getApplicationContext(), R.layout.listitem_discuss);
listView.setAdapter(adapter);
editText = (EditText) findViewById(R.id.editText1);
final ChatManager chatManager = ChatManager.getInstanceFor(abstractXMPPConnection);
final Chat chat;
final String other;
if (username.equals("foo")) {
other = "bar@bathroom-pc";
} else {
other = "foo@bathroom-pc";
}
chatManager.addChatListener(new MyChatManagerListener());
chat = chatManager.createChat(other);
editText.setOnKeyListener(new View.OnKeyListener() {
public boolean onKey(View v, int keyCode, KeyEvent event) {
/* If the event is a key-down event on the "enter" button */
if ((event.getAction() == KeyEvent.ACTION_DOWN) && (keyCode == KeyEvent.KEYCODE_ENTER)) {
/* Perform action on key press */
String guiMessage = editText.getText().toString();
adapter.add(new Message(false, editText.getText().toString()));
editText.setText("");
/* Actually send the message */
try {
org.jivesoftware.smack.packet.Message message = new org.jivesoftware.smack.packet.Message();
message.setBody(guiMessage);
message.setType(org.jivesoftware.smack.packet.Message.Type.chat);
chat.sendMessage(message);
} catch (SmackException.NotConnectedException e) {
e.printStackTrace();
}
return true;
}
return false;
}
});
}
private class MyChatManagerListener implements ChatManagerListener {
@Override
public void chatCreated(Chat chat, boolean createdLocally) {
chat.addMessageListener(new ChatMessageListener() {
public void processMessage(Chat chat, org.jivesoftware.smack.packet.Message message) {
adapter.add(new Message(true, message.getBody()));
adapter.notifyDataSetChanged();
}
});
}
}
private class XMPConnectionInitTask extends AsyncTask<String, Void, AbstractXMPPConnection> {
@Override
protected AbstractXMPPConnection doInBackground(String... urls) {
String username = urls[0];
XMPPTCPConnectionConfiguration conf = XMPPTCPConnectionConfiguration.builder()
.setHost(XMPP_HOST)
.setPort(XMPP_PORT)
.setUsernameAndPassword(username, username)
.setServiceName("local")
.setSecurityMode(ConnectionConfiguration.SecurityMode.disabled)
.setDebuggerEnabled(true)
.build();
SASLAuthentication.registerSASLMechanism(new SASLPlainMechanism());
AbstractXMPPConnection xmppConnection = new XMPPTCPConnection(conf);
try {
xmppConnection.connect();
xmppConnection.login(username, username);
} catch (SmackException e) {
Log.d("D:", e.getLocalizedMessage());
} catch (IOException e) {
Log.d("D:", e.getLocalizedMessage());
} catch (XMPPException e) {
Log.d("D:", e.getLocalizedMessage());
}
return xmppConnection;
}
}
}
ChatActivity
public class ChatArrayAdapter extends ArrayAdapter<Message> {
private TextView textView;
private List<Message> messages = new ArrayList<Message>();
private LinearLayout linearLayout;
public ChatArrayAdapter(Context context, int textViewResourceId) {
super(context, textViewResourceId);
}
@Override
public void add(Message object) {
messages.add(object);
super.add(object);
}
public int getCount() {
return this.messages.size();
}
public Message getItem(int index) {
return this.messages.get(index);
}
public View getView(int position, View convertView, ViewGroup parent) {
View row = convertView;
if (row == null) {
LayoutInflater inflater = (LayoutInflater) this.getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
row = inflater.inflate(R.layout.listitem_discuss, parent, false);
}
linearLayout = (LinearLayout) row.findViewById(R.id.wrapper);
Message message = getItem(position);
textView = (TextView) row.findViewById(R.id.comment);
textView.setText(message.content);
textView.setBackgroundResource(message.left ? R.drawable.bubble_yellow : R.drawable.bubble_green);
linearLayout.setGravity(message.left ? Gravity.LEFT : Gravity.RIGHT);
return row;
}
}
消息
public class Message {
public boolean left;
public String content;
public Message(boolean left, String content) {
super();
this.left = left;
this.content = content;
}
}
您可以与用户foo
或用户bar
建立联系,并相互发送消息。消息通过Openfire服务器发送。
视图未自动更新,这意味着如果foo
向bar
发送消息,bar
必须向应用程序发送事件(触摸,返回,刷新),以便他的视图已更新。
我在adapter.notifyDataSetChanged()
方法中添加了processMessage
,但它不起作用。
任何建议都将不胜感激!
答案 0 :(得分:2)
首先,您应该阅读一下ArrayAdapter,BaseAdapter和Adapters中的差异。在你的ArrayAdapter中,你手动充气视图,因此不应该是ArrayAdapter,但最有可能的是BaseAdapter。这将使您免受notifyDataSetChanged confussion和重复条目的限制(ArrayAdapter还包含一组数据,但您永远不会使用它! - 您始终在您的消息对象上操作)。
第二个问题是你应该在你的适配器中调用notifyDataSetChanged(按设计)。
现在您的代码存在问题
public void processMessage(Chat chat, org.jivesoftware.smack.packet.Message message) {
adapter.add(new Message(true, message.getBody()));
adapter.notifyDataSetChanged();
}
这段代码最像是从非ui线程调用的。为了使它工作,你应该在runOnUiThread(...)(doc)中包含内部两行。查看您当前实现的logcat输出,以了解有关该问题的更多信息。