简体   繁体   English

ListView项目背景根据滚动位置而变化

[英]ListView item background changes depending on scroll position

I'm running into a very strange ListView behaviour. 我遇到了一个非常奇怪的ListView行为。 I have a simple chat application which uses a ListView and a custom implementation of BaseAdapter to show messages. 我有一个简单的聊天应用程序,它使用ListView和BaseAdapter的自定义实现来显示消息。

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. 第二个是完全相同的活动,xml等,只需向下滚动一下即可。

Scrolled up: 向上滚动:

向上滚动图像

Scrolled down: 向下滚动:

向下滚动图像

Look at the message sent by "Me" @ 23:05. 查看“我” @ 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. 这发生在4.2.2的联系4和7,以及运行4.1.2的GS3上。

Here is the XML for one of the ListView items: 这是ListView项之一的XML:

<?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: 这是我设置RelativeLayout的颜色的地方:

if(localUserId.equals(remoteUserId)){
    itemLayout.setBackgroundColor(Color.parseColor("#F9F9F9"));
}

That code runs inside the getView() method of my Adapter. 该代码在我的适配器的getView()方法中运行。

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. 我已经用Google搜索了一下,什么也没发现,关于android:cacheColorHint问题有很多SO问题,但是我不认为这是怎么回事。

Has anyone run into this before? 有人遇到过吗? I'm stumped! 我很沮丧!

EDIT: Here's the baseadapter code: 编辑:这是baseadapter代码:

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. 由于没有其他与您的if匹配的语句,这可能是由于视图回收所致。 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. 当ListView中的项目从屏幕上滚动时,操作系统将其删除,并以与删除状态相同的状态将其交还给适配器。 This means you need to set the background color when it's not the local user's message as well. 这意味着您还需要设置背景颜色,而不是本地用户的消息。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM