2013-04-01 48 views
0

我遇到了非常奇怪的ListView行爲。我有一個簡單的聊天應用程序,它使用ListView和BaseAdapter的自定義實現來顯示消息。ListView項背景根據滾動位置而變化

我的想法是遮蔽來自「本地」用戶灰色的消息,並讓來自「遠程」用戶的消息變爲白色,以幫助用戶區分這兩者。 下面的兩張截圖顯示了正在發生的事情。第二個是完全相同的活動,XML等等,只是向下滾動了一下。

向上滾動:

scrolled up image

向下滾動:

scrolled down image

查看由 「我」 @ 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問題,但我不認爲這就是這裏發生的事情。

有沒有人遇到過這個?我很難過!

編輯:這裏是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; 
    } 
} 
+0

你應該張貼的代碼爲您實現BaseAdapter – chopchop

+0

添加的代碼適配器 – Tom

回答

1

既然沒有else語句符合您如果,這可能是由於觀看回收。當ListView中的項目從屏幕上滾動出來時,操作系統會將其刪除,並以與刪除相同的狀態將其交給適配器。這意味着,當它不是本地用戶的消息時,您還需要設置背景顏色。

+0

這解決了這個問題,但似乎我沒有正確理解視圖循環。即使有問題的視圖沒有關閉屏幕,我在兩個屏幕截圖中顯示的問題也會發生,所以不應該回收它?我可以看到它隨着我的拖動而變色,並且它始終保持在可見區域。這似乎是回收給我的一個非常奇怪的副作用。 – Tom

+0

首先,您已經實現getView方法的方式會讓您的應用程序非常慢,因爲您每次都調用findViewById。你不應該這樣做。每當你在內部滾動列表時,它都會調用onDraw方法來重繪你的列表。所以如果你不把其他條件的背景排成灰色的話。 – Ankit

+0

我一直在閱讀getView,它看起來好像我應該使用item id方法來允許我回收視圖。但是這裏沒有一個答案真正解釋了爲什麼列表項目的背景顏色從白色逐漸變爲灰色。我可以理解它是否突然改變,但這是一個平穩的變化,從FFFFFF到DDDDDD,如果它位於容器的頂部,則該項目爲FFFFFF,但當我將它拉下時,它會慢慢變爲DDDDDD。此處還有其他一些內容,因爲顏色是根據項目頁面向下多遠插入的。我只是好奇而已 – Tom