如何更新消息侦听器中的 ListView?
How to update a ListView inside a message listener?
我正在构建一个聊天应用程序,因此我使用了两个 ListView:一个显示在线好友,一个用于聊天本身,用于接收消息等。我正在为 Android 使用 XMPP 协议和 Smack 库。
Smack 库为我提供了每次朋友状态更改时激活的监听器 (online/offline),另一个在用户收到消息时激活。以下是我在用户按下按钮时声明适配器并调用 AsyncTask 的方式:
peopleList = (ListView) findViewById(R.id.peopleList);
adapter = new MyAdapter(this, people);
peopleList.setAdapter(adapter);
btn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
new ConnectAndLoad(MainActivity.this).execute();
}
});
在 AsyncTask 中,我在 doInBackground 方法中连接到服务器,在 onPostExecute 中我创建了将用户添加到列表视图的数组列表的侦听器并调用 adapter.notifyDataSetChanged();
public class ConnectAndLoad extends AsyncTask<String, Integer, Boolean> {
private ProgressDialog dialog;
public ConnectAndLoad(Activity activity)
{
this.dialog = new ProgressDialog(activity);
this.dialog.setTitle("Loading..");
this.dialog.setMessage("Connecting to the server..");
dialog.show();
}
@Override
protected Boolean doInBackground(String... arg0) {
MyConnectionManager.getInstance().setConnectionConfiguration(getApplicationContext());
MyConnectionManager.getInstance().connect();
MyConnectionManager.getInstance().login();
return true;
}
protected void onPostExecute(Boolean boo)
{
MyConnectionManager.getInstance().bored();
Roster roster = Roster.getInstanceFor(MyConnectionManager.getInstance().getConnection());
try
{
if (!roster.isLoaded()) roster.reloadAndWait();
}
catch (Exception e)
{
Log.e(TAG, "reload");
}
roster.addRosterListener(new RosterListener() {
public void entriesDeleted(Collection<String> addresses) {
}
public void entriesUpdated(Collection<String> addresses) {
}
public void entriesAdded(Collection<String> addresses) {
}
@Override
public void presenceChanged(Presence presence) {
people.add(new People(presence.getFrom(), presence.getStatus()));
adapter.notifyDataSetChanged();
}
});
dialog.dismiss();
}
}
下面是我的自定义适配器:
public class PeopleAdapter extends ArrayAdapter<People> {
private ArrayList<People> events_list = new ArrayList<>();
Context context;
public PeopleAdapter(Context context, ArrayList<People> users) {
super(context, 0, users);
this.context = context;
this.events_list = users;
}
@Override
public View getView(final int position, View convertView, ViewGroup parent) {
People user = getItem(position);
if (convertView == null) {
convertView = LayoutInflater.from(getContext()).inflate(R.layout.people_list, parent, false);
}
TextView tvName = (TextView) convertView.findViewById(R.id.name);
TextView tvStatus = (TextView) convertView.findViewById(R.id.status);
tvName.setText(user.name);
tvStatus.setText(user.status);
convertView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Toast.makeText(context, "You Clicked " + events_list.get(position).name, Toast.LENGTH_SHORT).show();
Intent i = new Intent(context, ConversationActivity.class);
i.putExtra("user", events_list.get(position).name);
context.startActivity(i);
}
});
return convertView;
}
}
我的意思是我想做的事我认为这是一件简单的事情,每个聊天应用程序都这样做,基本上是自动更新列表视图,但我有两个问题:
- 列表视图仅在我单击后更新。所以它基本上有效
但我必须点击列表视图..
每次列表视图更新时我都会收到此错误(但该应用程序仍在运行):
Exception in packet listener: android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.
我可以给你一个简单的解决办法。在 ConnectAndLoad class
中创建局部 Activity 变量
private Activity activity;
public ConnectAndLoad(Activity activity)
{
...
activity.activity= activity;
}
而不是直接调用 adapter.notifyDataSetChanged();
使用
activity.runOnUiThread(new Runnable() {
@Override
public void run() {
adapter.notifyDataSetChanged();
}
});
似乎 presenceChanged()
在另一个线程中调用了。但是要小心,确保在 activity 被销毁时删除 RosterListener,否则它会导致内存泄漏,即 activity 已经被销毁,但您会不断收到有关状态更改的通知。
我正在构建一个聊天应用程序,因此我使用了两个 ListView:一个显示在线好友,一个用于聊天本身,用于接收消息等。我正在为 Android 使用 XMPP 协议和 Smack 库。
Smack 库为我提供了每次朋友状态更改时激活的监听器 (online/offline),另一个在用户收到消息时激活。以下是我在用户按下按钮时声明适配器并调用 AsyncTask 的方式:
peopleList = (ListView) findViewById(R.id.peopleList);
adapter = new MyAdapter(this, people);
peopleList.setAdapter(adapter);
btn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
new ConnectAndLoad(MainActivity.this).execute();
}
});
在 AsyncTask 中,我在 doInBackground 方法中连接到服务器,在 onPostExecute 中我创建了将用户添加到列表视图的数组列表的侦听器并调用 adapter.notifyDataSetChanged();
public class ConnectAndLoad extends AsyncTask<String, Integer, Boolean> {
private ProgressDialog dialog;
public ConnectAndLoad(Activity activity)
{
this.dialog = new ProgressDialog(activity);
this.dialog.setTitle("Loading..");
this.dialog.setMessage("Connecting to the server..");
dialog.show();
}
@Override
protected Boolean doInBackground(String... arg0) {
MyConnectionManager.getInstance().setConnectionConfiguration(getApplicationContext());
MyConnectionManager.getInstance().connect();
MyConnectionManager.getInstance().login();
return true;
}
protected void onPostExecute(Boolean boo)
{
MyConnectionManager.getInstance().bored();
Roster roster = Roster.getInstanceFor(MyConnectionManager.getInstance().getConnection());
try
{
if (!roster.isLoaded()) roster.reloadAndWait();
}
catch (Exception e)
{
Log.e(TAG, "reload");
}
roster.addRosterListener(new RosterListener() {
public void entriesDeleted(Collection<String> addresses) {
}
public void entriesUpdated(Collection<String> addresses) {
}
public void entriesAdded(Collection<String> addresses) {
}
@Override
public void presenceChanged(Presence presence) {
people.add(new People(presence.getFrom(), presence.getStatus()));
adapter.notifyDataSetChanged();
}
});
dialog.dismiss();
}
}
下面是我的自定义适配器:
public class PeopleAdapter extends ArrayAdapter<People> {
private ArrayList<People> events_list = new ArrayList<>();
Context context;
public PeopleAdapter(Context context, ArrayList<People> users) {
super(context, 0, users);
this.context = context;
this.events_list = users;
}
@Override
public View getView(final int position, View convertView, ViewGroup parent) {
People user = getItem(position);
if (convertView == null) {
convertView = LayoutInflater.from(getContext()).inflate(R.layout.people_list, parent, false);
}
TextView tvName = (TextView) convertView.findViewById(R.id.name);
TextView tvStatus = (TextView) convertView.findViewById(R.id.status);
tvName.setText(user.name);
tvStatus.setText(user.status);
convertView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Toast.makeText(context, "You Clicked " + events_list.get(position).name, Toast.LENGTH_SHORT).show();
Intent i = new Intent(context, ConversationActivity.class);
i.putExtra("user", events_list.get(position).name);
context.startActivity(i);
}
});
return convertView;
}
}
我的意思是我想做的事我认为这是一件简单的事情,每个聊天应用程序都这样做,基本上是自动更新列表视图,但我有两个问题:
- 列表视图仅在我单击后更新。所以它基本上有效 但我必须点击列表视图..
每次列表视图更新时我都会收到此错误(但该应用程序仍在运行):
Exception in packet listener: android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.
我可以给你一个简单的解决办法。在 ConnectAndLoad class
中创建局部 Activity 变量 private Activity activity;
public ConnectAndLoad(Activity activity)
{
...
activity.activity= activity;
}
而不是直接调用 adapter.notifyDataSetChanged();
使用
activity.runOnUiThread(new Runnable() {
@Override
public void run() {
adapter.notifyDataSetChanged();
}
});
似乎 presenceChanged()
在另一个线程中调用了。但是要小心,确保在 activity 被销毁时删除 RosterListener,否则它会导致内存泄漏,即 activity 已经被销毁,但您会不断收到有关状态更改的通知。