我正在使用自定义ArrayAdapter的Android应用中的消息传递屏幕上工作,因此成员之间发送的消息将显示为聊天气泡。在onCreate()期间下载数据,并在onPostExecute()中解析JSON并将其一次加载到适配器中,一次一条消息。日志证实了这一部分。
适配器被加载到位于EditText字段上方的listView和新消息的Send按钮。这是片段java,其中包括修改后的ArrayAdapter类,以及UI和消息行布局的两个XML文件。
package com.davepeyton.android.seekbromance;
import android.annotation.TargetApi;
import android.os.AsyncTask;
import android.os.Build;
import android.os.Bundle;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.Fragment;
import android.text.Editable;
import android.text.TextWatcher;
import android.util.Log;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.LinearLayout;
import android.widget.TextView;
import android.widget.EditText;
import android.widget.ArrayAdapter;
import android.widget.ListView;
import org.apache.http.NameValuePair;
import org.apache.http.message.BasicNameValuePair;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.ArrayList;
import java.util.List;
/*
* This fragment displays a simple list view using a custom array adapter, with a TextEdit
* field for sending new messages.
*
* The layout 'dresses up' the messages (of class BroChatMsg) to look like chat balloons.
*/
public class BrofileBroChatFragment extends Fragment {
public static final String EXTRA_BROFILE_ID = "com.davepeyton.android.seekbromance.brofile_id";
// no need to fetch the whole brofile here - it doesn't get displayed or altered
// private static final String url_fetch_brofile = "http://www.seekbromance.com/android_connx/fetch_brofile.php";
private static final String url_fetch_messages = "http://www.seekbromance.com/android_connx/fetch_messages.php";
private int mCurrUserMemberId; // the member ID for the logged-in user
// private String brofile_id; // parameter for fetch_brofile.php
private int mConnectFailed = 0; // for HTTP exceptions and the like
int brofileId;
// parameters for fetch_message.php
private String sender_id = "19";
private String receiver_id = "2905";
private int sid;
private int rid;
// JSON parser class
JSONParser jsonParser = new JSONParser();
// JSON node names to be used in this fragment
private static final String TAG_SUCCESS = "success";
private static final String TAG_MESSAGES = "messages";
private static final String TAG_SID = "sid";
private static final String TAG_RID = "rid";
private static final String TAG_BODY = "body";
private static final String TAG_TIMESTAMP = "timestamp";
private static BroChatMsg sBroChatMsg;
private ArrayList<BroChatMsg> mRetrievedMessages; // originally took the message data, now used only to initialize the adapter.
private MessageAdapter mAdapter;
private String mQuickString = "";
private EditText mNewMsgField;
private Button mSendMessageBtn;
private ListView mLv;
private String newMessageBody = "";
public static BrofileBroChatFragment newInstance(int brofileId) {
Bundle args = new Bundle();
args.putInt(EXTRA_BROFILE_ID, brofileId);
BrofileBroChatFragment fragment = new BrofileBroChatFragment();
fragment.setArguments(args);
return fragment;
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setRetainInstance(true);
//Will probably need these later to get back to calling intent
// get the ID of the Brofile to display from the calling intent
brofileId = getArguments().getInt(EXTRA_BROFILE_ID);
mCurrUserMemberId = 19; // quick set for testing
//mCurrUserBrofileId = 25; // this is a "full" dummy profile in the SB database; used for testing*/
sBroChatMsg = new BroChatMsg();
mRetrievedMessages = new ArrayList<BroChatMsg>();
mAdapter = new MessageAdapter(mRetrievedMessages);
//sid 19 and rid 2905 are for testing
sid = 19;
rid = 2905;
sender_id = String.valueOf(sid);
receiver_id = String.valueOf(rid);
String logMsg = "Requesting sid = " + sender_id + " receiver_id = " + receiver_id;
Log.d("Actions OnCreate", logMsg);
new FetchMessages().execute();
}
class FetchMessages extends AsyncTask<String, Void, JSONObject> {
@Override
protected JSONObject doInBackground(String... strings) {
JSONObject json = null;
// Set up parameters for the PHP
List<NameValuePair> parameters = new ArrayList<NameValuePair>();
parameters.add(new BasicNameValuePair("id1", sender_id));
parameters.add(new BasicNameValuePair("id2", receiver_id));
// Make the HTTP request
json = jsonParser.makeHttpRequest(url_fetch_messages, "GET", parameters);
// Log the JSON response
Log.d("Single Message Details", json.toString());
return json;
}
@Override
protected void onPostExecute(JSONObject messagesStuff) {
int success = 0; // JSON Success tag
JSONArray messagesArray = null;
JSONObject message = null;
BroChatMsg broChatMsgObj = new BroChatMsg();
// ArrayList<BroChatMsg> messagesList = new ArrayList<BroChatMsg>(); (use the adapter instead)
long timestamp;
String msgDate;
try {
success = messagesStuff.getInt(TAG_SUCCESS);
} catch (JSONException e) {
e.printStackTrace();
}
if (success == 1) {
try {
messagesArray = messagesStuff.getJSONArray(TAG_MESSAGES);
} catch (JSONException e) {
e.printStackTrace();
}
mAdapter.clear();
for (int i = 0; i < messagesArray.length(); i++) {
//Populate the broChat object
try {
message = messagesArray.getJSONObject(i);
mQuickString = message.getString(TAG_SID);
broChatMsgObj.setSenderId(mQuickString);
mQuickString = message.getString(TAG_RID);
broChatMsgObj.setReceiverId(mQuickString);
mQuickString = message.getString(TAG_BODY);
mQuickString = mQuickString.replaceAll("\\r\\n", "");
broChatMsgObj.setMsgBody(mQuickString);
mQuickString = message.getString(TAG_TIMESTAMP);
broChatMsgObj.setTimestampStr(mQuickString);
timestamp = Long.valueOf(mQuickString) * 1000; //Used to format date
broChatMsgObj.setTimestamp(timestamp);
Date msgTime = new Date(timestamp);
broChatMsgObj.setMsgTime(msgTime);
msgDate = new SimpleDateFormat("MM/dd/yy h:mm a").format(msgTime);
broChatMsgObj.setMsgDate(msgDate);
} catch (JSONException e) {
e.printStackTrace();
}
sBroChatMsg = broChatMsgObj;
Log.d("sBroChat Message: ", String.valueOf(i));
Log.d("sBroChat SID ", sBroChatMsg.getSenderId());
Log.d("sBroChat RID ", sBroChatMsg.getReceiverId());
Log.d("sBroChat BODY ", sBroChatMsg.getMsgBody());
Log.d("sBroChat TIMESTAMP ", sBroChatMsg.getMsgDate());
mAdapter.add(sBroChatMsg);
// mAdapter.notifyDataSetChanged();
// mRetrievedMessages.add(broChatMsgObj);
/* This is pretty much redundant.
for (i = 0; i < mRetrievedMessages.size(); i++) {
Log.d("message ", String.valueOf(i));
Log.d("message ", mRetrievedMessages.get(i).getId().toString());
Log.d("message TIMESTAMP ", mRetrievedMessages.get(i).getMsgDate());
Log.d("message SID ", mRetrievedMessages.get(i).getSenderId());
Log.d("message RID ", mRetrievedMessages.get(i).getReceiverId());
Log.d("message BODY ", mRetrievedMessages.get(i).getMsgBody());
}
*/
} // end message parsing
// sBroChat.setMessages(mRetrievedMessages); (no longer used)
// refresh the list view
mAdapter.notifyDataSetChanged();
} else {
// no matching messages turned up
try {
mQuickString = messagesStuff.getString("message");
} catch (JSONException e) {
e.printStackTrace();
}
if (mQuickString == "Connection error") {
mConnectFailed = 1;
}
}
// return control to the main fragment
// onFetchBrofileTaskComplete.setFetchBrofileTaskComplete();
}
}
@TargetApi(Build.VERSION_CODES.HONEYCOMB)
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle saveInstanceState) {
View v = inflater.inflate(R.layout.brochat_fragment, container, false);
/* This View holds the list and an edit window for new messages.
NOTE!: Nothing from the list is being inflated here! The adapter does all that.
*/
mLv = (ListView) v.findViewById(R.id.listView1);
mLv.setAdapter(mAdapter);
mLv.setEmptyView(v.findViewById(R.id.emptyElement));
mNewMsgField = (EditText) v.findViewById(R.id.editText1);
mNewMsgField.setText(newMessageBody);
mNewMsgField.addTextChangedListener(new TextWatcher() {
@Override
public void beforeTextChanged(CharSequence c, int start, int before, int count) {
newMessageBody = c.toString();
}
@Override
public void onTextChanged(CharSequence c, int start, int count, int after) {
// This method intentionally left blank
}
@Override
public void afterTextChanged(Editable c) {
// This one too
}
});
mSendMessageBtn = (Button)v.findViewById(R.id.new_chat_msg_button);
mSendMessageBtn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// TODO: Prepare new message, check sending privilege, send if allowed.
}
});
return v;
}
private class MessageAdapter extends ArrayAdapter<BroChatMsg> {
public MessageAdapter (ArrayList<BroChatMsg> chatter) {
super(getActivity(), 0, chatter);
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
// Displays one item at "position"
boolean isLoggedUser = false; // determines whether to show chat bubble on left or right
boolean hasPrevious = false; // false if this balloon is the oldest message
String dateStr = "";
BroChatMsg last_bcm = new BroChatMsg();
long prevTimeStamp = 0;
// If we weren't given a view, inflate one
if (convertView == null) {
convertView = getActivity().getLayoutInflater().inflate(R.layout.list_item_brochat, null);
}
// Configure the view for this message
BroChatMsg bcm = getItem(position);
if (position > 0) { hasPrevious = true; }
if (hasPrevious) { last_bcm = getItem(position - 1); }
if (bcm.getSid() == mCurrUserMemberId) {isLoggedUser = true; }
Date itemDate = bcm.getMsgTime();
long itemTimestamp = bcm.getTimestamp();
dateStr = new SimpleDateFormat("E, MMM dd, h:mm a").format(itemDate);
// don't print date if under 5 minutes since last message
if (hasPrevious) {
prevTimeStamp = last_bcm.getTimestamp();
if ((itemTimestamp - prevTimeStamp) < 600000) { dateStr = "";}
}
TextView MsgTextView = (TextView)convertView.findViewById(R.id.comment);
TextView DateTextView = (TextView)convertView.findViewById(R.id.timelabel);
LinearLayout wrapper = (LinearLayout)convertView.findViewById(R.id.wrapper);
MsgTextView.setText(bcm.getMsgBody());
DateTextView.setText(dateStr);
MsgTextView.setBackgroundResource(isLoggedUser ? R.drawable.bubble_yellow : R.drawable.bubble_green);
wrapper.setGravity(isLoggedUser ? Gravity.LEFT : Gravity.RIGHT);
return convertView;
}
}
@Override
public void onStart() {
super.onStart();
if (mConnectFailed == 1) {
FragmentManager fm = getActivity().getSupportFragmentManager();
CantConnectDialogFragment connectDialog = new CantConnectDialogFragment();
connectDialog.show(fm, "Not connected");
}
}
}
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical">
<ListView
android:id="@+id/listView1"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_centerVertical="true"
android:layout_above="@+id/form"
android:layout_alignParentLeft="true">
</ListView>
<TextView
android:id="@+id/emptyElement"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:text="No messages"
android:textSize="24dp"
android:gravity="center_vertical|center_horizontal" />
<RelativeLayout
android:id="@+id/form"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_alignParentLeft="true"
android:orientation="vertical">
<EditText
android:id="@+id/editText1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:ems="10"
android:inputType="text"
android:layout_toLeftOf="@+id/new_chat_msg_button"
android:layout_toStartOf="@+id/new_chat_msg_button" />
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Send"
android:id="@+id/new_chat_msg_button"
android:layout_alignParentRight="true" />
</RelativeLayout>
</RelativeLayout>
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical">
<ListView
android:id="@+id/listView1"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_centerVertical="true"
android:layout_above="@+id/form"
android:layout_alignParentLeft="true">
</ListView>
<TextView
android:id="@+id/emptyElement"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:text="No messages"
android:textSize="24dp"
android:gravity="center_vertical|center_horizontal" />
<RelativeLayout
android:id="@+id/form"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_alignParentLeft="true"
android:orientation="vertical">
<EditText
android:id="@+id/editText1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:ems="10"
android:inputType="text"
android:layout_toLeftOf="@+id/new_chat_msg_button"
android:layout_toStartOf="@+id/new_chat_msg_button" />
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Send"
android:id="@+id/new_chat_msg_button"
android:layout_alignParentRight="true" />
</RelativeLayout>
</RelativeLayout>
目前,显示正确的数量下载的消息,但它们都是从JSON结构中检索到的最后一项的重复项。据我所知,XML布局不是罪魁祸首。发现的其他邮件发生了什么?
答案 0 :(得分:0)
在您的周期中使用相同的实例broChatMsgObj
会发生这种情况
所以所有项都指的是同一个对象的最后一个值。
for (int i = 0; i < messagesArray.length(); i++) {
//You have to create a new object
broChatMsgObj = new BroChatMsg();
//Populate the broChat object
try {
message = messagesArray.getJSONObject(i);
mQuickString = message.getString(TAG_SID);
broChatMsgObj.setSenderId(mQuickString);
//....
}
//....
}