如何在消息监听器中更新ListView?

时间:2015-12-08 13:44:32

标签: android listview android-listview android-asynctask smack

我正在构建一个聊天应用程序,因此我使用了两个ListView:一个显示在线好友,另一个用于聊天本身,接收消息等等。我正在使用XMPP协议和Smack Library for Android。

Smack Library为我提供了每次朋友状态更改(在线/离线)时激活的监听器,以及用户收到消息时激活的监听器。以下是我在用户按下按钮时声明适配器并调用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内部我创建了一个监听器,将用户添加到listview的数组列表并调用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.

1 个答案:

答案 0 :(得分:0)

我可以给你一个简单的解决方案。在ConnectAndLoad类中创建本地Activity变量

 private Activity activity;

 public ConnectAndLoad(Activity activity)
    {
       ...
       activity.activity=  activity;
    } 

而是直接调用adapter.notifyDataSetChanged();使用

 activity.runOnUiThread(new Runnable() {
            @Override
            public void run() {
                adapter.notifyDataSetChanged();
            }
        });

似乎在另一个线程中调用了presenceChanged()。但要小心并确保在活动被破坏时删除RosterListener,否则可能导致内存泄漏,即活动已被破坏,但您不断收到有关状态更改的通知。