Filip Luchianenco
Filip Luchianenco

Reputation: 7012

Listview strange behavior on scroll

I have a listview(chat). And i get some strange behavior. It happens when i scroll up, then some items start to mess up, and when i go back at the bottom, the items are messed up too. The only thing i could figure out is that my getView is called a couple times at every scroll. Does anyone know how can i solve this issue?

i saw this post: Custom ListView adapter, strange ImageView behavior but there he does not add items to the list after it has been created.


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

<TextView
    android:id="@+id/right_message"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_alignParentBottom="true"
    android:layout_toLeftOf="@+id/right_profilePic"
    android:layout_marginLeft="5dp"
    android:layout_marginRight="5dp"
    android:layout_marginTop="20dp"
    android:autoLink="all"
    />


 <ImageView
     android:id="@+id/left_profilePic"
     android:layout_width="wrap_content"
     android:layout_height="wrap_content"
     android:layout_alignParentBottom="true"
     android:layout_alignParentLeft="true"
     />

 <TextView
     android:id="@+id/left_message"
     android:layout_width="wrap_content"
     android:layout_height="wrap_content"
     android:layout_alignParentBottom="true"
     android:layout_toRightOf="@+id/left_profilePic"
     android:layout_marginLeft="5dp"
     android:layout_marginRight="5dp"
     android:layout_marginTop="20dp"
     android:autoLink="all" 
     />

 <ImageView
     android:id="@+id/right_profilePic"
     android:layout_width="wrap_content"
     android:layout_height="wrap_content"
     android:layout_alignParentBottom="true"
     android:layout_alignParentRight="true" 
     />

 <TextView
     android:id="@+id/left_time"
     android:layout_width="wrap_content"
     android:layout_height="wrap_content"
     android:layout_alignParentTop="true"
     android:layout_marginLeft="70dp"
     android:layout_toRightOf="@+id/left_profilePic"
     android:textColor="@color/Gray" 
     />

 <TextView
     android:id="@+id/right_time"
     android:layout_width="wrap_content"
     android:layout_height="wrap_content"
     android:layout_alignParentTop="true"
     android:layout_marginRight="70dp"
     android:layout_toLeftOf="@+id/right_profilePic"
     android:textColor="@color/Gray" 
     />

 <ImageView
     android:id="@+id/left_image_message"
     android:layout_width="wrap_content"
     android:layout_height="wrap_content"
     android:layout_alignParentBottom="true"
     android:layout_toRightOf="@+id/left_profilePic"
     android:layout_marginLeft="5dp"
     android:layout_marginRight="5dp"
     android:layout_marginTop="20dp"
     />

 <ImageView
     android:id="@+id/right_image_message"
     android:layout_width="wrap_content"
     android:layout_height="wrap_content"
     android:layout_alignParentBottom="true"
     android:layout_toLeftOf="@+id/right_profilePic"
     android:layout_marginLeft="5dp"
     android:layout_marginRight="5dp"
     android:layout_marginTop="20dp"
     />


Update

I have to load images and google static map inside chat, and i suppose this is heavy for a listview.

here is the getView method:

   public View getView(int position, View convertView, ViewGroup parent) {
    // TODO Auto-generated method stub
       Log.d("getView", "getview called");
       ViewHolder holder;
       if(convertView==null)
    {
        holder = new ViewHolder();
        convertView = inflater.inflate(R.layout.chat_item, null);

        holder.right_profilePic = (ImageView)     convertView.findViewById(R.id.right_profilePic);
        holder.right_message = (TextView) convertView.findViewById(R.id.right_message);
        holder.left_profilePic = (ImageView) convertView.findViewById(R.id.left_profilePic);
        holder.left_message = (TextView) convertView.findViewById(R.id.left_message);
        holder.right_time = (TextView) convertView.findViewById(R.id.right_time);
        holder.left_time = (TextView) convertView.findViewById(R.id.left_time);
        holder.right_image_message = (ImageView) convertView.findViewById(R.id.right_image_message);
        holder.left_image_message = (ImageView) convertView.findViewById(R.id.left_image_message);


        convertView.setTag(holder);
    }
    else
        holder=(ViewHolder)convertView.getTag();

    ChatItemBean bean = (ChatItemBean) chatItemList.get(position);
    Log.d("holder", bean.getId() + " | " + GlobalData.myId);
    if (bean.getId().equals(GlobalData.myId)) {
        Picasso.with(context).load(bean.getImage()).resize(130, 130).into(holder.right_profilePic);
        if (bean.getType().equals("1")) {
            Picasso.with(context).load(init_link + bean.getMessage()).into(holder.right_image_message);
            holder.right_image_message.setBackgroundResource(R.drawable.chat_background_right);
            //holder.right_image_message.setPadding(0, 0, 0, 20);
        } else if (bean.getType().equals("2")) {
            StringTokenizer tokens = new StringTokenizer(bean.getMessage(), "&");
            String first = tokens.nextToken();
            String second = tokens.nextToken();
            String url = "http://maps.google.com/maps/api/staticmap?center=" + 
                            first + "," + second + "&zoom=15&size=200x200&sensor=false";
            Picasso.with(context).load(url).into(holder.right_image_message);
            holder.right_image_message.setBackgroundResource(R.drawable.chat_background_right);
        } else {
            holder.right_message.setText(bean.getMessage());
            holder.right_message.setBackgroundResource(R.drawable.chat_background_right);
            }
        holder.right_time.setText(bean.getTime());
    } else {
        Picasso.with(context).load(bean.getImage()).resize(130, 130).into(holder.left_profilePic);

        if (bean.getType().equals("1")) { 
            Picasso.with(context).load(init_link + bean.getMessage()).into(holder.left_image_message);
            holder.left_image_message.setBackgroundResource(R.drawable.chat_background_left);
            holder.left_image_message.setPadding(0, 10, 0, 30);

        } else if (bean.getType().equals("2")) {
            StringTokenizer tokens = new StringTokenizer(bean.getMessage(), "&");
            String first = tokens.nextToken();
            String second = tokens.nextToken();
            String url = "http://maps.google.com/maps/api/staticmap?center=" + 
                            first + "," + second + "&zoom=15&size=200x200&sensor=false";
            Picasso.with(context).load(url).into(holder.left_image_message);
            holder.left_image_message.setBackgroundResource(R.drawable.chat_background_left);
        } else {
            holder.left_message.setText(bean.getMessage());
            holder.left_message.setBackgroundResource(R.drawable.chat_background_left);
        }

        holder.left_time.setText(bean.getTime());
    }
    return convertView;
}public View getView(int position, View convertView, ViewGroup parent) {
    // TODO Auto-generated method stub
    Log.d("getView", "getview called");
    ViewHolder holder;
    if(convertView==null)
    {
        holder = new ViewHolder();
        convertView = inflater.inflate(R.layout.chat_item, null);

        holder.right_profilePic = (ImageView) convertView.findViewById(R.id.right_profilePic);
        holder.right_message = (TextView) convertView.findViewById(R.id.right_message);
        holder.left_profilePic = (ImageView) convertView.findViewById(R.id.left_profilePic);
        holder.left_message = (TextView) convertView.findViewById(R.id.left_message);
        holder.right_time = (TextView) convertView.findViewById(R.id.right_time);
        holder.left_time = (TextView) convertView.findViewById(R.id.left_time);
        holder.right_image_message = (ImageView) convertView.findViewById(R.id.right_image_message);
        holder.left_image_message = (ImageView) convertView.findViewById(R.id.left_image_message);


        convertView.setTag(holder);
    }
    else
        holder=(ViewHolder)convertView.getTag();

    ChatItemBean bean = (ChatItemBean) chatItemList.get(position);
    Log.d("holder", bean.getId() + " | " + GlobalData.myId);
    if (bean.getId().equals(GlobalData.myId)) {
        Picasso.with(context).load(bean.getImage()).resize(130, 130).into(holder.right_profilePic);
        if (bean.getType().equals("1")) {
            Picasso.with(context).load(init_link + bean.getMessage()).into(holder.right_image_message);
            holder.right_image_message.setBackgroundResource(R.drawable.chat_background_right);
            //holder.right_image_message.setPadding(0, 0, 0, 20);
        } else if (bean.getType().equals("2")) {
            StringTokenizer tokens = new StringTokenizer(bean.getMessage(), "&");
            String first = tokens.nextToken();
            String second = tokens.nextToken();
            String url = "http://maps.google.com/maps/api/staticmap?center=" + 
                            first + "," + second + "&zoom=15&size=200x200&sensor=false";
            Picasso.with(context).load(url).into(holder.right_image_message);
            holder.right_image_message.setBackgroundResource(R.drawable.chat_background_right);
        } else {
            holder.right_message.setText(bean.getMessage());
            holder.right_message.setBackgroundResource(R.drawable.chat_background_right);
            }
        holder.right_time.setText(bean.getTime());
    } else {
        Picasso.with(context).load(bean.getImage()).resize(130, 130).into(holder.left_profilePic);

        if (bean.getType().equals("1")) { 
            Picasso.with(context).load(init_link + bean.getMessage()).into(holder.left_image_message);
            holder.left_image_message.setBackgroundResource(R.drawable.chat_background_left);
            holder.left_image_message.setPadding(0, 10, 0, 30);

        } else if (bean.getType().equals("2")) {
            StringTokenizer tokens = new StringTokenizer(bean.getMessage(), "&");
            String first = tokens.nextToken();
            String second = tokens.nextToken();
            String url = "http://maps.google.com/maps/api/staticmap?center=" + 
                            first + "," + second + "&zoom=15&size=200x200&sensor=false";
            Picasso.with(context).load(url).into(holder.left_image_message);
            holder.left_image_message.setBackgroundResource(R.drawable.chat_background_left);
        } else {
            holder.left_message.setText(bean.getMessage());
            holder.left_message.setBackgroundResource(R.drawable.chat_background_left);
        }

        holder.left_time.setText(bean.getTime());
    }
    return convertView;
}

public View getView(int position, View convertView, ViewGroup parent) {
    // TODO Auto-generated method stub
    Log.d("getView", "getview called");
    ViewHolder holder;
    if(convertView==null)
    {
        holder = new ViewHolder();
        convertView = inflater.inflate(R.layout.chat_item, null);

        holder.right_profilePic = (ImageView) convertView.findViewById(R.id.right_profilePic);
        holder.right_message = (TextView) convertView.findViewById(R.id.right_message);
        holder.left_profilePic = (ImageView) convertView.findViewById(R.id.left_profilePic);
        holder.left_message = (TextView) convertView.findViewById(R.id.left_message);
        holder.right_time = (TextView) convertView.findViewById(R.id.right_time);
        holder.left_time = (TextView) convertView.findViewById(R.id.left_time);
        holder.right_image_message = (ImageView) convertView.findViewById(R.id.right_image_message);
        holder.left_image_message = (ImageView) convertView.findViewById(R.id.left_image_message);


        convertView.setTag(holder);
    }
    else
        holder=(ViewHolder)convertView.getTag();

    ChatItemBean bean = (ChatItemBean) chatItemList.get(position);
    Log.d("holder", bean.getId() + " | " + GlobalData.myId);
    if (bean.getId().equals(GlobalData.myId)) {
        Picasso.with(context).load(bean.getImage()).resize(130, 130).into(holder.right_profilePic);
        if (bean.getType().equals("1")) {
            Picasso.with(context).load(init_link + bean.getMessage()).into(holder.right_image_message);
            holder.right_image_message.setBackgroundResource(R.drawable.chat_background_right);
            //holder.right_image_message.setPadding(0, 0, 0, 20);
        } else if (bean.getType().equals("2")) {
            StringTokenizer tokens = new StringTokenizer(bean.getMessage(), "&");
            String first = tokens.nextToken();
            String second = tokens.nextToken();
            String url = "http://maps.google.com/maps/api/staticmap?center=" + 
                            first + "," + second + "&zoom=15&size=200x200&sensor=false";
            Picasso.with(context).load(url).into(holder.right_image_message);
            holder.right_image_message.setBackgroundResource(R.drawable.chat_background_right);
        } else {
            holder.right_message.setText(bean.getMessage());
            holder.right_message.setBackgroundResource(R.drawable.chat_background_right);
            }
        holder.right_time.setText(bean.getTime());
    } else {
        Picasso.with(context).load(bean.getImage()).resize(130, 130).into(holder.left_profilePic);

        if (bean.getType().equals("1")) { 
            Picasso.with(context).load(init_link + bean.getMessage()).into(holder.left_image_message);
            holder.left_image_message.setBackgroundResource(R.drawable.chat_background_left);
            holder.left_image_message.setPadding(0, 10, 0, 30);

        } else if (bean.getType().equals("2")) {
            StringTokenizer tokens = new StringTokenizer(bean.getMessage(), "&");
            String first = tokens.nextToken();
            String second = tokens.nextToken();
            String url = "http://maps.google.com/maps/api/staticmap?center=" + 
                            first + "," + second + "&zoom=15&size=200x200&sensor=false";
            Picasso.with(context).load(url).into(holder.left_image_message);
            holder.left_image_message.setBackgroundResource(R.drawable.chat_background_left);
        } else {
            holder.left_message.setText(bean.getMessage());
            holder.left_message.setBackgroundResource(R.drawable.chat_background_left);
        }

        holder.left_time.setText(bean.getTime());
    }
    return convertView;
}

enter image description here


Update

Here i tried using two different layouts:

@Override
public View getView(int position, View convertView, ViewGroup parent) {
    // TODO Auto-generated method stub
    Log.d("getView", "getview called");
    ViewHolder holder;
    ChatItemBean checkBean = (ChatItemBean) chatItemList.get(position);

    if (checkBean.getId().equals(GlobalData.myId)) {
         if(convertView==null)
         {
             holder = new ViewHolder();
             convertView = inflater.inflate(R.layout.chat_item_right, null);
             holder.right_profilePic = (ImageView) convertView.findViewById(R.id.right_profilePic);
             holder.right_message = (TextView) convertView.findViewById(R.id.right_message);
             holder.right_time = (TextView) convertView.findViewById(R.id.right_time);
             holder.right_image_message = (ImageView) convertView.findViewById(R.id.right_image_message);
             convertView.setTag(holder);
         } else 
             holder=(ViewHolder)convertView.getTag();

         ChatItemBean bean = (ChatItemBean) chatItemList.get(position);
         Log.d("holder", bean.getId() + " | " + GlobalData.myId);

         Picasso.with(context).load(bean.getImage()).resize(130, 130).into(holder.right_profilePic);
        if (bean.getType().equals("1")) {
            Picasso.with(context).load(init_link + bean.getMessage()).into(holder.right_image_message);
            holder.right_image_message.setBackgroundResource(R.drawable.chat_background_right);
            //holder.right_image_message.setPadding(0, 0, 0, 20);
        } else if (bean.getType().equals("2")) {
            StringTokenizer tokens = new StringTokenizer(bean.getMessage(), "&");
            String first = tokens.nextToken();
            String second = tokens.nextToken();
            String url = "http://maps.google.com/maps/api/staticmap?center=" + 
                            first + "," + second + "&zoom=15&size=200x200&sensor=false";
            Picasso.with(context).load(url).into(holder.right_image_message);
            holder.right_image_message.setBackgroundResource(R.drawable.chat_background_right);
        } else {
            holder.right_message.setText(bean.getMessage());
            holder.right_message.setBackgroundResource(R.drawable.chat_background_right);
            }
        holder.right_time.setText(bean.getTime());

    } else {
        if(convertView==null)
        {
            holder = new ViewHolder();
            convertView = inflater.inflate(R.layout.chat_item, null);

            holder.left_profilePic = (ImageView) convertView.findViewById(R.id.left_profilePic);
            holder.left_message = (TextView) convertView.findViewById(R.id.left_message);
            holder.left_time = (TextView) convertView.findViewById(R.id.left_time);
            holder.left_image_message = (ImageView) convertView.findViewById(R.id.left_image_message);

            convertView.setTag(holder);
        }
        else
            holder=(ViewHolder)convertView.getTag();

        ChatItemBean bean = (ChatItemBean) chatItemList.get(position);
        Log.d("holder", bean.getId() + " | " + GlobalData.myId);

            Picasso.with(context).load(bean.getImage()).resize(130, 130).into(holder.left_profilePic);

            if (bean.getType().equals("1")) { 
                Picasso.with(context).load(init_link + bean.getMessage()).into(holder.left_image_message);
                holder.left_image_message.setBackgroundResource(R.drawable.chat_background_left);
                holder.left_image_message.setPadding(0, 10, 0, 30);

            } else if (bean.getType().equals("2")) {
                StringTokenizer tokens = new StringTokenizer(bean.getMessage(), "&");
                String first = tokens.nextToken();
                String second = tokens.nextToken();
                String url = "http://maps.google.com/maps/api/staticmap?center=" + 
                                first + "," + second + "&zoom=15&size=200x200&sensor=false";
                Picasso.with(context).load(url).into(holder.left_image_message);
                holder.left_image_message.setBackgroundResource(R.drawable.chat_background_left);
            } else {
                holder.left_message.setText(bean.getMessage());
                holder.left_message.setBackgroundResource(R.drawable.chat_background_left);
            }

            holder.left_time.setText(bean.getTime());
    }

    return convertView;
}

but my application crashes when i scroll a bit more, like the moment when it tries to mess up, i get the Target must not be null. line 124 loading profile pic using picasso.

12-01 21:14:40.442: E/AndroidRuntime(21764): java.lang.IllegalArgumentException: Target must not be null.
12-01 21:14:40.442: E/AndroidRuntime(21764):    at com.squareup.picasso.RequestCreator.into(RequestCreator.java:333)
12-01 21:14:40.442: E/AndroidRuntime(21764):    at com.squareup.picasso.RequestCreator.into(RequestCreator.java:319)
12-01 21:14:40.442: E/AndroidRuntime(21764):    at com.petcial.petopen.custom.ChatCustomAdapter.getView(ChatCustomAdapter.java:124)

Upvotes: 0

Views: 1767

Answers (4)

user3685578
user3685578

Reputation: 148

I have the exact problem and I solved it by not using the convertView.

Basically, what I did was:

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

    View rowView = inflater.inflate(R.layout.your_layout, parent, false);

    //Do what you intend to do, populate the TextView, or set the ImageViews, but do them with respect to rowView, not convertView.

    ImageView imageView = (ImageView) rowView.findViewById(R.id.my_picture);

    imageView.setImageResources(R.drawable.sample_picture);

    return rowView;

}

This works great whether you are using 1 layout or 2 for the list items, hope this helps :-)

Upvotes: 0

androidcodehunter
androidcodehunter

Reputation: 21947

It is common question and already asked in StackOverflow. However, at first check what android engineer RomainGuy said about getView() method calling

This is not an issue, there is absolutely no guarantee on the order in which getView() will be called nor how many times.

So the best you can handle re-using the existing views (row layouts) properly.

Or Just add this line of code in your adapter class.

@Override
public int getViewTypeCount() {

    if (getCount() != 0)
        return getCount();

    return 1;
}

Upvotes: 0

Vidia
Vidia

Reputation: 481

This tutorial by Lars Vogel is amazing. I highly recommend using it. Without seeing your JAva code, I can say that you need to have two different layout XMLs for the list entries. One for the image on the left, and one for the image on the right. Then, in your adapter, inflate the one that you need for that part of the conversation.

That is the right way to do it. What I think happened with you is that you added content to the recycled ViewGroup that is passed to getView without removing the old content so your new stuff got added on top of the old stuff. With the new process, you need to make sure that the recycled view is the correct "orientation" then remove the old content and add the new.

Here is the section from the Vogella article I linked above.

Within the getView() method you would inflate an XML based layout and then set the content of the individual views based on the Java object for this row. To inflate the XML layout file you can use the system service LayoutInflator. This service can get accessed via the activity or via the context.getSystemService(Context.LAYOUT_INFLATER_SERVICE) method call.

The individual elements in the layout can be found via the findViewById() method call on the top level view.

By the way, getView() gets called many many times during a scroll and the order of the calls is not predictable. So do not do anything too intensive in a getView method or else the slowdown will be very noticeable.

Upvotes: 1

Rohit Sharma
Rohit Sharma

Reputation: 13825

  1. Using Seprate layout file for different type list items-

using two layout file can end up in losing some convertView reuseability performance stuff. but will help you in having clean things which will solve this mess. determine which file to use for which item.

2 . Using one layout file.

It is possible/(requires effort) to cleanly define the layout of one file to use them properly for all type of listitems you have. use a relative layout and put one x1 above the other x2. and simply make x1 or x2 visible or invisible in getView depending upon the type. Make sure you do no heavy operation in getView. where x1 and x2 is layout of your one item type.

Try this. But you can surely post your code to get more help. But suggested will be to try the cleanup first at your end.

Updated: go for option 2. Use one RelativeLayout and add two LinearLayout in it. one above the other and define one item type in first LinearLayout and other in second one. Make these container(LL) go visible or invisible in getView

Hope this suggestion helps

Upvotes: 1

Related Questions