Android XMPP - 更新适配器更新视图

时间:2015-06-27 17:55:46

标签: android xmpp

我想做的事情:

使用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服务器发送。

问题:

视图未自动更新,这意味着如果foobar发送消息,bar必须向应用程序发送事件(触摸,返回,刷新),以便他的视图已更新。 我在adapter.notifyDataSetChanged()方法中添加了processMessage,但它不起作用。

任何建议都将不胜感激!

1 个答案:

答案 0 :(得分:2)

首先,您应该阅读一下ArrayAdapter,BaseAdapter和Adapters中的差异。在你的ArrayAdapter中,你手动充气视图,因此不应该是ArrayAdapter,但最有可能的是BaseAdapter。这将使您免受notifyDataSetChanged confussion和重复条目的限制(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输出,以了解有关该问题的更多信息。