简体   繁体   English

Listview奇怪的滚动行为

[英]Listview strange behavior on scroll

I have a listview(chat). 我有一个listview(聊天)。 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. 我唯一能想到的是每次滚动都会调用我的getView几次。 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. 我看到这篇文章: 自定义ListView适配器,奇怪的ImageView行为,但他没有在创建后添加项目到列表。


<?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: 这是getView方法:

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

在此输入图像描述


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. 第124行使用毕加索加载配置文件。

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)
  1. Using Seprate layout file for different type list items- 对不同类型的列表项使用Seprate布局文件 -

using two layout file can end up in losing some convertView reuseability performance stuff. 使用两个布局文件最终会丢失一些convertView可重用性能的东西。 but will help you in having clean things which will solve this mess. 但是会帮助你拥有可以解决这个烂摊子的清洁用品。 determine which file to use for which item. 确定哪个文件用于哪个项目。

2 . 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. 使用相对布局并将一个x1放在另一个x2之上。 and simply make x1 or x2 visible or invisible in getView depending upon the type. 并根据类型简单地在getView中使x1或x2可见或不可见。 Make sure you do no heavy operation in getView. 确保在getView中没有繁重的操作。 where x1 and x2 is layout of your one item type. 其中x1和x2是您的一个项目类型的布局。

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. 更新:转到选项2.使用一个RelativeLayout并在其中添加两个LinearLayout。 one above the other and define one item type in first LinearLayout and other in second one. 一个在另一个之上并在第一个LinearLayout中定义一个项类型,在第二个中定义另一个。 Make these container(LL) go visible or invisible in getView 使这些容器(LL)在getView中可见或不可见

Hope this suggestion helps 希望这个建议有所帮助

This tutorial by Lars Vogel is amazing. Lars Vogel的这个教程非常棒。 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. 在没有看到您的JAva代码的情况下,我可以说您需要为列表条目提供两种不同的布局XML。 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. 我认为发生在你身上的是你在回收的ViewGroup中添加了内容,这些内容传递给了getView而没有删除旧的内容,所以你的新东西被添加到旧的东西之上。 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. 以下是我在上面链接的Vogella文章中的部分。

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. getView()方法中,您将扩展基于XML的布局,然后根据此行的Java对象设置各个视图的内容。 To inflate the XML layout file you can use the system service LayoutInflator . LayoutInflator XML布局文件,您可以使用系统服务LayoutInflator This service can get accessed via the activity or via the context.getSystemService(Context.LAYOUT_INFLATER_SERVICE) method call. 可以通过活动或context.getSystemService(Context.LAYOUT_INFLATER_SERVICE)方法调用访问此服务。

The individual elements in the layout can be found via the findViewById() method call on the top level view. 可以通过顶级视图上的findViewById()方法调用找到布局中的各个元素。

By the way, getView() gets called many many times during a scroll and the order of the calls is not predictable. 顺便说一句,getView()在滚动期间被多次调用,并且调用的顺序是不可预测的。 So do not do anything too intensive in a getView method or else the slowdown will be very noticeable. 因此,不要在getView方法中做任何过于密集的事情,否则减速将非常明显。

It is common question and already asked in StackOverflow. 这是常见问题,已经在StackOverflow中提出过。 However, at first check what android engineer RomainGuy said about getView() method calling 但是,首先检查一下android工程师RomainGuy关于getView()方法调用的内容

This is not an issue, there is absolutely no guarantee on the order in which getView() will be called nor how many times. 这不是问题,绝对不能保证调用getView()的顺序也不保证多少次。

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

I have the exact problem and I solved it by not using the convertView. 我有确切的问题,我通过不使用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 :-) 无论您使用1个布局还是2个列表项,这都很有用,希望这会有所帮助:-)

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

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