Android XMPP - 适配器更改时更新视图
Android XMPP - Update View on Adapter change
我想做什么:
使用 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
必须向应用程序发送事件(触摸、返回、刷新)以便他的观点得到了更新。
我在 processMessage
方法中添加了 adapter.notifyDataSetChanged()
,但它不起作用。
如有任何建议,我们将不胜感激!
首先,您应该大致了解一下 ArrayAdapter、BaseAdapter 和适配器的区别。在你的 ArrayAdapter 中,你正在手动膨胀视图,所以它不应该是一个 ArrayAdapter,但 - 最有可能 - 一个 BaseAdapter。这将使您免于 notifyDataSetChanged 混淆和重复条目(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 输出,以了解有关该问题的更多信息。
我想做什么:
使用 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
必须向应用程序发送事件(触摸、返回、刷新)以便他的观点得到了更新。
我在 processMessage
方法中添加了 adapter.notifyDataSetChanged()
,但它不起作用。
如有任何建议,我们将不胜感激!
首先,您应该大致了解一下 ArrayAdapter、BaseAdapter 和适配器的区别。在你的 ArrayAdapter 中,你正在手动膨胀视图,所以它不应该是一个 ArrayAdapter,但 - 最有可能 - 一个 BaseAdapter。这将使您免于 notifyDataSetChanged 混淆和重复条目(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 输出,以了解有关该问题的更多信息。