0

Я создаю приложение чата, поэтому я использую два ListViews: один, который показывает онлайн-друзей и один для самого чата, который получает сообщения и так далее. Я использую протокол XMPP и Smack Library для Android.Как обновить ListView внутри прослушивателя сообщений?

Библиотека 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 я создаю слушатель, который добавляет пользователь список массив из 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; 
    } 
} 

Я имею в виду, что я тусклый т делать я думаю, что это очень простая вещь, каждый приложение чата делает это, в основном обновить вид списка автоматически, но у меня две проблемы:

  • The ListView обновляет только после того, как я нажимаю на него. Так что в основном работает , но я должен нажать на ListView ..
  • Я получаю эту ошибку каждый раз, когда обновляется просматривать список (приложение продолжает работать, хотя):

    Exception in packet listener: android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.

+0

Можете ли вы просто опубликовать всю асинтеску? –

+0

@NigamPatro Эй, я только что опубликовал всю асинтеску, пожалуйста, посмотрите – gooeyn

+0

Я думаю, 'presenceChanged()' работает в другом потоке, кроме основного потока. –

ответ

0

I может дать вам простое решение. Сделать локальную переменную активность в ConnectAndLoad классе

private Activity activity; 

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

Вместо прямого вызова на adapter.notifyDataSetChanged(); использование

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

Похоже, что presenceChanged() называется в другом потоке. Но будьте осторожны и убедитесь, что вы удалите RosterListener, когда активность будет уничтожена или это может привести к утечке памяти. I.e активность уже уничтожена, но вы продолжаете получать уведомления об изменении присутствия.

+0

Он работал как шарм! Огромное спасибо!!!!!!! – gooeyn