当前位置: 动力学知识库 > 问答 > 编程问答 >

android - ListView item background changes depending on scroll position

问题描述:

I'm running into a very strange ListView behaviour. I have a simple chat application which uses a ListView and a custom implementation of BaseAdapter to show messages.

The idea I had was to shade messages from the "local" user grey and have messages from the "remote" user white, to help the user distinguish between the two.

The two screenshots below show what's happening. The second is the exact same activity, xml etc etc, simply scrolled down a bit.

Scrolled up:

Scrolled down:

Look at the message sent by "Me" @ 23:05. When at the top it has no contrast to its neighbours, but when it is scrolled to the bottom, the difference is clear to see.

This occurs on a nexus 4 and 7 on 4.2.2, and a GS3 running 4.1.2.

Here is the XML for one of the ListView items:

<?xml version="1.0" encoding="utf-8"?>

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"

android:id="@+id/activity_view_conversation_message_list_item_wrapper"

android:layout_width="fill_parent"

android:layout_height="wrap_content"

android:paddingBottom="2dp"

android:paddingLeft="5dp"

android:paddingRight="10dp" >

<ImageView

android:id="@+id/activity_view_conversation_message_list_item_user_image"

android:layout_width="50dp"

android:layout_height="50dp"

android:layout_alignParentLeft="true"

android:layout_marginTop="5dp"

android:src="@drawable/default_user_image" />

<TextView

android:id="@+id/activity_view_conversation_message_list_item_heading"

android:layout_width="wrap_content"

android:layout_height="wrap_content"

android:layout_marginLeft="10dp"

android:layout_marginTop="1dp"

android:layout_toRightOf="@+id/activity_view_conversation_message_list_item_user_image"

android:text="Martyn"

android:textColor="#000000"

android:textSize="18sp"

android:textStyle="bold" />

<TextView

android:id="@+id/activity_view_conversation_message_list_item_contents"

android:layout_width="wrap_content"

android:layout_height="wrap_content"

android:layout_below="@+id/activity_view_conversation_message_list_item_heading"

android:layout_marginLeft="10dp"

android:layout_marginRight="5dp"

android:layout_marginTop="2dp"

android:layout_toLeftOf="@+id/activity_view_conversation_message_list_item_ack"

android:layout_toRightOf="@+id/activity_view_conversation_message_list_item_user_image"

android:text="Hello this is some text"

android:textColor="#333333"

android:textIsSelectable="true"

android:textSize="18sp" />

<TextView

android:id="@+id/activity_view_conversation_message_list_item_time"

android:layout_width="wrap_content"

android:layout_height="wrap_content"

android:layout_alignParentRight="true"

android:layout_alignParentTop="true"

android:layout_marginTop="2dp"

android:paddingLeft="5dp"

android:paddingRight="5dp"

android:paddingTop="2dp"

android:text="12:08"

android:textSize="14sp" />

<ImageView

android:id="@+id/activity_view_conversation_message_list_item_ack"

android:layout_width="wrap_content"

android:layout_height="wrap_content"

android:layout_alignTop="@+id/activity_view_conversation_message_list_item_time"

android:layout_marginRight="2dp"

android:layout_marginTop="8dp"

android:layout_toLeftOf="@+id/activity_view_conversation_message_list_item_time"

android:src="@drawable/red_dot_8dp" />

</RelativeLayout>

And here is where I set the colour of the RelativeLayout:

if(localUserId.equals(remoteUserId)){

itemLayout.setBackgroundColor(Color.parseColor("#F9F9F9"));

}

That code runs inside the getView() method of my Adapter.

I've Googled this a fair bit and turned up nothing, there are a lot of SO questions regarding the android:cacheColorHint issue but I don't think that is what is going on here.

Has anyone run into this before? I'm stumped!

EDIT: Here's the baseadapter code:

public class MessageListAdapter extends BaseAdapter {

private ArrayList<Message> messageList;

Context context;

/**

* Constructor

* @param newConversationsList An ArrayList of Conversation objects that this adapter will use

* @param newContext The context of the activity that instantiated this adapter

*/

MessageListAdapter(ArrayList<Message> newMessageList, Context newContext){

messageList = newMessageList;

//reload();

context = newContext;

}

public int getCount() {

return messageList.size();

}

public Object getItem(int position) {

return messageList.get(position);

}

public long getItemId(int position) {

return position;

}

/**

* Adds a message to the chat list

* @param message A Message object containing all the message's information

*/

public void add(Message message){

//nMessagesToShow++; //A new message has been added, so increase the number to show by one

Log.d(TAG, "COUNT: "+getCount());

//refresh();

}

public void refresh(){

this.notifyDataSetChanged();

}

public View getView(int position, View convertView, ViewGroup parent) {

View view = convertView;

if(view!=null){

//return view;

}

LayoutInflater vi = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);

//Get the Message object from the list

Message message = messageList.get(position);

//Get the data from the message

String senderId = message.getFromUser();

int messageType = message.getType();

String senderFirstName;

ImageView userImage, messageImage;

TextView messageHeading, messageBody;

switch(messageType){

case Message.MESSAGE_TYPE_TEXT: //Standard text message

//The layout we inflate for this list item will vary depending on whether this message has the same sender as the previous

if(position>0 && senderId.equals(messageList.get(position-1).getFromUser())){ //True if this is not the first message AND the sender id matches that of the previous message

view = vi.inflate(R.layout.activity_view_conversation_message_list_item_alternate, null); //Inflate an alternate version of the list item which has no heading or user image

}

else{ //This is the first message OR the sender id is different to the previous

view = vi.inflate(R.layout.activity_view_conversation_message_list_item, null); //Inflate the standard version of the layout

userImage = (ImageView) view.findViewById(R.id.activity_view_conversation_message_list_item_user_image);

messageHeading = (TextView) view.findViewById(R.id.activity_view_conversation_message_list_item_heading);

//Use the sender's ID to get the sender's image and first name

Contact contact = database.getContact(senderId);

if(senderId.equals(localUserId)){ //True if the local user sent this message

senderFirstName = "Me";

}

else{

senderFirstName = contact.getFirstName();

}

userImage.setImageBitmap(contact.getImageBitmap(100, 100, 6));

messageHeading.setText(senderFirstName);

}

messageBody = (TextView) view.findViewById(R.id.activity_view_conversation_message_list_item_contents);

messageBody.setText(message.getContents(null));

break;

case Message.MESSAGE_TYPE_IMAGE: //Image message

view = vi.inflate(R.layout.activity_view_conversation_message_list_item_image, null); //Inflate a list item template for displaying an image

userImage = (ImageView) view.findViewById(R.id.activity_view_conversation_message_list_item_user_image);

//Sender's first name

messageHeading = (TextView) view.findViewById(R.id.activity_view_conversation_message_list_item_heading);

Contact contact = database.getContact(senderId);

if(senderId.equals(localUserId)){ //True if the local user sent this message

senderFirstName = "Me";

}

else{

senderFirstName = contact.getFirstName();

}

messageHeading.setText(senderFirstName);

messageImage = (ImageView) view.findViewById(R.id.activity_view_conversation_message_list_item_image);

String imageResourceId = null;

//The message is a JSON object containing several fields, one of which is the file name which we will use to get the image

try {

JSONObject messageJSON = new JSONObject(message.getContents(null));

String imagePath = Environment.getExternalStorageDirectory()+"/epicChat/resources/"+messageJSON.getString("fileName");

int imageWidth = messageJSON.getInt("width"); //We want the dimensions in order to calculate the aspect ratio of the image

int imageHeight = messageJSON.getInt("height");

if(messageJSON.has("resourceId")){

imageResourceId = messageJSON.getString("resourceId"); //This is used when opening the image gallery

}

int displayWidth = 300;

int displayHeight = (int) ((float) imageHeight / (float) imageWidth * (float) displayWidth);

String imagePathFull = imagePath+displayWidth+displayHeight; //For the caching

Bitmap originalImage = null;

//Check the bitmap cache exists. If not, reinstantiate it

if(MainActivity.bitmapCache==null){ //Cache is null

MainActivity.loadBitmapCache();

}

else{ //Cache is not null, so check it to see if this image is in it

originalImage = MainActivity.bitmapCache.get(imagePathFull);

}

if(originalImage==null){ //True if the bitmap was not in the cache. So we must load from disk instead

new Utils.LoadBitmapAsync(imagePath, messageImage, displayWidth, displayHeight, MainActivity.bitmapCache).execute();

messageImage.getLayoutParams().height = displayHeight;

}

else{

messageImage.setImageBitmap(originalImage);

}

}

catch (JSONException e) {

Log.e(TAG, "Error reading image JSON: "+e.toString());

}

if(imageResourceId!=null){ //Only attach the listener if we got a valid resource ID

final String recourceIdFinal = imageResourceId;

final String conversationIdFinal = message.getUserList();

messageImage.setOnClickListener(new OnClickListener() {

@Override

public void onClick(View v) {

Intent showConversationImageGalleryIntent = new Intent(context, ViewConversationImageGalleryActivity.class);

showConversationImageGalleryIntent.putExtra("conversationId", conversationIdFinal);

showConversationImageGalleryIntent.putExtra("resourceId", recourceIdFinal);

startActivityForResult(showConversationImageGalleryIntent, ACTION_SHOW_CONVERSATION_IMAGE_GALLERY);

}

});

}

userImage.setImageBitmap(contact.getImageBitmap(100, 100, 6));

break;

case Message.MESSAGE_TYPE_INVALID:

default:

break;

}

//Some layout items are present in all layouts. Typically these are the status indicator and the message time

RelativeLayout itemLayout = (RelativeLayout) view.findViewById(R.id.activity_view_conversation_message_list_item_wrapper);

//If the message is from the local user, give it a subtle grey background

if(localUserId.equals(message.getFromUser())){

itemLayout.setBackgroundColor(Color.parseColor("#E9E9E9"));

}

else{

itemLayout.setBackgroundColor(Color.parseColor("#FFFFFF"));

}

TextView messageTimeText = (TextView) view.findViewById(R.id.activity_view_conversation_message_list_item_time);

messageTimeText.setText(message.getFormattedTime());

ImageView messageStatusImage = (ImageView) view.findViewById(R.id.activity_view_conversation_message_list_item_ack);

//Set the status image according to the status of the message

switch(message.getStatus()){

case Message.MESSAGE_STATUS_PENDING: //Pending messages should have a red dot

messageStatusImage.setImageResource(R.drawable.red_dot_8dp);

messageStatusImage.setVisibility(View.VISIBLE);

break;

case Message.MESSAGE_STATUS_ACK_SERVER: //Messages that reached the server should have an orange dot

messageStatusImage.setImageResource(R.drawable.orange_dot_8dp);

messageStatusImage.setVisibility(View.VISIBLE);

break;

case Message.MESSAGE_STATUS_ACK_RECIPIENT: //Messages that reached the recipient should have an green dot

messageStatusImage.setImageResource(R.drawable.green_dot_8dp);

messageStatusImage.setVisibility(View.VISIBLE);

break;

case Message.MESSAGE_STATUS_NOT_SET: //Not set typically means the message came from another user, in which case the status image should be hidden

default: //Also default here

messageStatusImage.setVisibility(View.INVISIBLE);

break;

}

return view;

}

}

网友答案:

Since there's no else statement matching your if, this is probably due to view recycling. When an item from a ListView scrolls off the screen, the operating system removes it and hands it back to the adapter in the same state it was removed. This means you need to set the background color when it's not the local user's message as well.

分享给朋友:
您可能感兴趣的文章:
随机阅读: