【问题标题】:ListView item background changes depending on scroll positionListView 项目背景根据滚动位置而变化
【发布时间】:2013-03-23 01:48:05
【问题描述】:

我遇到了一个非常奇怪的 ListView 行为。我有一个简单的聊天应用程序,它使用 ListView 和 BaseAdapter 的自定义实现来显示消息。

我的想法是将来自“本地”用户的消息设为灰色,将来自“远程”用户的消息设为白色,以帮助用户区分两者。 下面的两个屏幕截图显示了正在发生的事情。第二个是完全相同的活动,xml等,只是向下滚动了一点。

向上滚动:

向下滚动:

查看“我”@23:05 发送的消息。当它在顶部时,它与它的邻居没有对比,但是当它滚动到底部时,差异是显而易见的。 这发生在 4.2.2 上的节点 4 和 7 以及运行 4.1.2 的 GS3 上。

这是 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>

这里是我设置RelativeLayout颜色的地方:

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

该代码在我的适配器的 getView() 方法中运行。

我在谷歌上搜索了一下,但什么也没找到,有很多关于 android:cacheColorHint 问题的 SO 问题,但我认为这不是这里发生的事情。

以前有人遇到过这种情况吗?我被难住了!

编辑:这是基本适配器代码:

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;
    }
}

【问题讨论】:

  • 您应该发布 BaseAdapter 实现的代码
  • 添加了适配器的代码

标签: android listview background-color baseadapter


【解决方案1】:

由于没有与您的 if 匹配的 else 语句,这可能是由于视图回收。当 ListView 中的项目滚动到屏幕外时,操作系统会将其移除并以与移除时相同的状态将其交还给适配器。这意味着当不是本地用户的消息时,您也需要设置背景颜色。

【讨论】:

  • 解决了它,但似乎我没有正确理解视图回收。即使有问题的视图没有离开屏幕,我在两个屏幕截图中显示的问题也会发生,所以它不应该被回收吗?当我向下拖动它时,我可以看到它在改变颜色,并且它一直停留在可见区域中。对我来说,回收利用似乎是一个非常奇怪的副作用。
  • 第一件事是您实现 getView 方法的方式会使您的应用程序非常慢,因为您每次都调用 findViewById。你不应该这样做。每当您在内部滚动列表时,它都会调用 onDraw 方法来重绘您的列表。因此,如果您不设置 else 条件,该行的背景将为灰色。
  • 我一直在阅读 getView ,看来我应该使用 item id 方法来允许我回收视图。但是这里的答案都没有真正解释为什么列表项的背景颜色逐渐从白色变为灰色。我可以理解它是否突然变化,但这是一个平滑的变化,从 FFFFFF 到 DDDDDD,如果项目位于容器顶部,则为 FFFFFF,但当我将其拉下时,它会慢慢变为 DDDDDD。这里必须有其他事情发生,因为颜色是根据项目在页面下方的距离进行插值的。我只是好奇
  • 这可能只是一种视觉错觉,因为 Holo 主题默认使用微妙的渐变。尝试将窗口背景设置为纯色并检查问题是否仍然存在。 github.com/android/platform_frameworks_base/blob/master/core/…
  • 这真是太有帮助了!!!我有一个后台转换器,它确实有一个 if 但 else 返回 null 而不是透明的。其他任何方法都行不通。非常感谢!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-02-19
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-06-20
相关资源
最近更新 更多