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

Posted

技术标签:

【中文标题】如何在消息侦听器中更新 ListView?【英文标题】:How to update a ListView inside a message listener? 【发布时间】:2016-03-13 11:12:46 【问题描述】:

我正在构建一个聊天应用程序,因此我使用了两个 ListView:一个显示在线朋友,一个用于聊天本身,接收消息等等。我正在使用 XMPP 协议和适用于 android 的 Smack 库。

Smack 库为我提供了侦听器,这些侦听器在每次好友状态更改(在线/离线)时激活,另一个在用户收到消息时激活。下面是我如何声明适配器并在用户按下按钮时调用 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.

【问题讨论】:

你能把整个异步任务贴出来吗? @NigamPatro 嘿,我刚刚发布了整个异步任务,请看一下 我认为,presenceChanged() 在主线程之外的其他线程中运行。 【参考方案1】:

我可以给你一个简单的解决方案。在 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() 在另一个线程中被调用。但要小心,确保在 Activity 被破坏时删除 RosterListener,否则会导致内存泄漏,即 Activity 已经被破坏,但您不断收到有关状态更改的通知。

【讨论】:

以上是关于如何在消息侦听器中更新 ListView?的主要内容,如果未能解决你的问题,请参考以下文章

如何使用更改侦听器 JavaFX 在两个 ListView 之间移动项目

如何在android中去listview适配器项目点击监听器片段?

如何动态更新 C++ ListView?

单击 ListView 上的侦听器

如何使用edittexts在自定义ListView中实现监听器文本更改?

如何使用 Firebase 查询的结果最终在 Android Studio 中填充 ListView