Android - 基于 pubnub 的聊天的自定义列表视图

Android - Custom Listview for a pubnub based chat

我正在构建一个 Android 应用程序,它使用 pubnub 允许用户互相聊天。用户名和聊天频道是通过 Parse 设置的,我已经设法让聊天的发布和订阅部分正常工作。我要尝试做的下一部分是为要显示的聊天设置自定义列表视图。

我希望聊天显示在像 whatsapp 等的气泡中,并带有消息走到屏幕的两边。
我创建了一个自定义 chat_item.xml 来保存列表视图中的每一行,一个 ChatArrayAdapter.java 和一个 ChatMessage.java 对象来保存每条消息。
我知道我非常接近它的工作,但它的结构在某处是错误的,每次收到消息并将其添加到列表中时,我都会在 [=] 的第 28 行收到 Only the original thread that created a view hierarchy can touch its views. 异常43=] 即 super.add(object); 内:

@Override
    public void add(ChatMessage object) {

        chatMessageList.add(object);
        super.add(object);
    }. 

谁能告诉我这里做错了什么?我觉得我快要让它工作了,构建块就在那里,只是没有以正确的方式构建。

ChatActivity.java:

    public class ChatActivity extends Activity {

        final Pubnub pubnub = new Pubnub("example", "example");

        TextView tvBtnBackToSelection;
        TextView tvBtnNewChat;
        EditText etMessageText;
        Button btnSendMessage;

        private ChatArrayAdapter chatArrayAdapter;
        private ListView listView;

        String USER_ID;
        String CHAT_ID;
        String CHAT_STATUS;

        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_chat);

            //get data passed from last activity by intent
            Intent intent = getIntent();
            USER_ID = intent.getStringExtra("USER_ID");
            CHAT_ID = intent.getStringExtra("CHAT_ID");
            CHAT_STATUS = intent.getStringExtra("CHAT_STATUS");

            //initialise ui and pubnub
            uiInit();

            //subscribe to chat channel
            subscribeToChat(CHAT_ID);

            //initialise chat presence
            chatPresenceInit(CHAT_ID);

        }

        //-----inistialise ui
        public void uiInit() {

            listView = (ListView) findViewById(R.id.lvChatList);

            chatArrayAdapter = new ChatArrayAdapter(getApplicationContext(), R.layout.chat_item, USER_ID);
            listView.setAdapter(chatArrayAdapter);

            tvBtnBackToSelection = (TextView) findViewById(R.id.tvBtnBackToSelection);
            tvBtnNewChat = (TextView) findViewById(R.id.tvBtnNewChat);
            etMessageText = (EditText) findViewById(R.id.etMessageText);
            btnSendMessage = (Button) findViewById(R.id.btnSendMessage);

            tvBtnBackToSelection.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {

                    backToSelection();
                }
            });

            tvBtnNewChat.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {

                }
            });

            btnSendMessage.setOnClickListener(new View.OnClickListener() {

                @Override
                public void onClick(View v) {

                    //publish message to chat
                    publishToChat(etMessageText.getText().toString());

                    //set edittext back to null
                    etMessageText.setText("");
                }
            });

        }

//-----subscribe to chat channel
        public void subscribeToChat(String chatChannel) {

            try {

                pubnub.subscribe(chatChannel, new Callback() {

                            @Override
                            public void connectCallback(String channel, Object message) {
                                System.out.println("SUBSCRIBE : CONNECT on channel:" + channel
                                        + " : " + message.getClass() + " : "
                                        + message.toString());

                            }

                            @Override
                            public void disconnectCallback(String channel, Object message) {
                                System.out.println("SUBSCRIBE : DISCONNECT on channel:" + channel
                                        + " : " + message.getClass() + " : "
                                        + message.toString());
                            }

                            public void reconnectCallback(String channel, Object message) {
                                System.out.println("SUBSCRIBE : RECONNECT on channel:" + channel
                                        + " : " + message.getClass() + " : "
                                        + message.toString());
                            }

                            @Override
                            public void successCallback(String channel, Object message) {

                                String messageSplit[] = message.toString().split("_");;
                                String receivedUserID = messageSplit[0];
                                String receivedMessage = messageSplit[1];

                                System.out.println("CHANNEL: " + channel);
                                System.out.println("USERID: " + receivedUserID);
                                System.out.println("MESSAGE: " + receivedMessage);

                                chatArrayAdapter.add(new ChatMessage(receivedUserID, receivedMessage));
                            }

                            @Override
                            public void errorCallback(String channel, PubnubError error) {

                                System.out.println("SUBSCRIBE : ERROR on channel " + channel
                                        + " : " + error.toString());
                            }
                        }
                );

            }
            catch(Exception e) {

            }


        }

        //-----publish to chat channel
        public void publishToChat(String message) {


            message = message.replace("_", "").trim();
            message = USER_ID + "_" + message;

            //set up publish callback
            Callback callback = new Callback() {
                public void successCallback(String channel, Object response) {
                    System.out.println(response.toString());
                }
                public void errorCallback(String channel, PubnubError error) {
                    System.out.println(error.toString());
                }
            };

            //publish message to channel
            pubnub.publish(CHAT_ID, message, callback);
        }
    }

ChatMessage.java:

public class ChatMessage {

    public String user;
    public String message;

    public ChatMessage(String user, String message) {
        super();

        this.user = user;
        this.message = message;
    }
}

ChatArrayAdapter.java

public class ChatArrayAdapter extends ArrayAdapter<ChatMessage> {

    public TextView tvChatLeft;
    public TextView tvChatRight;
    private List<ChatMessage> chatMessageList = new ArrayList<ChatMessage>();
    String thisUserId;

    @Override
    public void add(ChatMessage object) {

        chatMessageList.add(object);
        super.add(object);
    }

    public ChatArrayAdapter(Context context, int textViewResourceId, String userId) {
        super(context, textViewResourceId);


        thisUserId = userId;
    }

    public int getCount() {

        return this.chatMessageList.size();
    }

    public ChatMessage getItem(int index) {

        return this.chatMessageList.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.chat_item, parent, false);
        }

        ChatMessage chatMessageObj = getItem(position);

        tvChatRight = (TextView)row.findViewById(R.id.tvChatBubbleRight);
        tvChatLeft = (TextView)row.findViewById(R.id.tvChatBubbleLeft);

        //create flag to define if message sender is current user
        final boolean isMe = chatMessageObj.user.equals(thisUserId);

        if (isMe) {

            //get message text from parse
            String messageText = chatMessageObj.message;

            //if message text is not null
            if (!messageText.equals(null)||!messageText.equals("")||!messageText.equals(" ")) {

                //set message to right chat bubble
                tvChatRight.setText(messageText);

                //set right textview bubble gravity to right and set as visible
                tvChatRight.setGravity(Gravity.RIGHT);
                tvChatRight.setVisibility(View.VISIBLE);

                //hide left textview bubble
                tvChatLeft.setVisibility(View.GONE);
            }

            //if a valid message has not been found
            else {


            }
        }

        //other user
        else {

            //get message text from parse
            String messageText = chatMessageObj.message;

            //if message text is null, only update the users location
            if (!messageText.equals(null)||!messageText.equals("")||!messageText.equals(" ")) {

                //set left chat bubble at message text
                tvChatLeft.setText(messageText);

                //set left chat bubble to left and visible
                tvChatLeft.setGravity(Gravity.LEFT);
                tvChatLeft.setVisibility(View.VISIBLE);

                //hide right chat bubble
                tvChatRight.setVisibility(View.GONE);
            }

            //else if a valid message is found then update chat list as well as users location
            else {


            }
        }

        return row;
    }

    public Bitmap decodeToBitmap(byte[] decodedByte) {
        return BitmapFactory.decodeByteArray(decodedByte, 0, decodedByte.length);
    }

chat_item.xml:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:orientation="vertical" >

    <TextView
        android:textSize="12sp"
        android:id="@+id/tvChatBubbleLeft"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:background="@drawable/chat_bubble_left"
        android:layout_gravity="left"
        android:layout_weight="1"
        android:gravity="left">
    </TextView>

    <TextView
        android:textSize="12sp"
        android:id="@+id/tvChatBubbleRight"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:background="@drawable/chat_bubble_right"
        android:layout_gravity="right"
        android:gravity="right"
        android:layout_weight="1">
    </TextView>

</LinearLayout>

设法让这个工作。我只是添加了

ChatActivity.this.runOnUiThread(new Runnable() {

                                @Override
                                public void run() {

                                    chatArrayAdapter.add(new ChatMessage(receivedUserID, receivedMessage));
                                }
                            });

在我的 pubnub 订阅方法的成功回调中,现在一切正常!