Android上的GridView滚动问题

Android上的GridView滚动问题,android,gridview,Android,Gridview,这一定是我忽略的一些非常简单的问题,但我有以下问题(这篇文章相当长,但我想提供尽可能多的信息:) 我的Android应用程序中有一个gridview,其中每个单元格都有自定义视图: <?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical"

这一定是我忽略的一些非常简单的问题,但我有以下问题(这篇文章相当长,但我想提供尽可能多的信息:)

我的Android应用程序中有一个gridview,其中每个单元格都有自定义视图:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
  xmlns:android="http://schemas.android.com/apk/res/android"
  android:orientation="vertical"
  android:layout_width="fill_parent"
  android:layout_height="fill_parent"
  >
    <GridView
    android:id = "@+id/photosGridView"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent" 
    android:clickable="true" 
    android:drawSelectorOnTop="true" 
    android:focusable="true" 
    android:focusableInTouchMode="true" 
    android:numColumns="6" 
    android:columnWidth="90dp"
    android:verticalSpacing="5dp"
    android:horizontalSpacing="5dp"
    android:stretchMode="columnWidth"     
    >
</GridView>

</RelativeLayout>

每个细胞都是

<?xml version="1.0" encoding="utf-8"?>
<com.myapp.widgets.ImageThumbView
  xmlns:android="http://schemas.android.com/apk/res/android"
  android:layout_width="wrap_content"
  android:layout_height="wrap_content"
  android:background = "@android:color/transparent"
  android:paddingLeft = "1dip"
  android:paddingRight = "1dip"
  android:paddingTop = "2dip"
  android:paddingBottom = "2dip"
  >
<ImageView
    android:id="@+id/thumbImage"
    android:layout_width="fill_parent"
    android:layout_height = "fill_parent"
    android:src="@drawable/icon_small"
    /> 

  <RelativeLayout
    android:layout_width="fill_parent"
    android:layout_height = "fill_parent"
    android:background = "@android:color/transparent"
   >

  <ImageView
   android:id="@+id/iconRight"
   android:layout_width="40px"
   android:layout_height = "40px"  
   android:src="@drawable/album_check"
   android:visibility="gone"
   android:layout_alignParentTop = "true"
   android:layout_alignParentRight = "true"     
   />

  <ImageView
   android:id="@+id/iconLeft"
   android:src="@drawable/album_check"
   android:visibility="gone"
   android:layout_width = "40px"
   android:layout_height="40px"
   android:layout_alignParentTop = "true"
   android:layout_alignParentLeft = "true"     
   /> 
</RelativeLayout>

</com.myapp.widgets.ImageThumbView>

我的适配器如下所示:

public class ImageAdapter extends BaseAdapter {
    private List<String> mPictures = null;

    public ImageAdapter(List<String> pictures) {
         mPictures = pictures;
    }

    public int getCount() {
          return mPictures != null ? mPictures.size() : 0;
    }
    public Object getItem(int position) {
          return mPictures != null ?  mPictures.get(position) : null;
    }
    public long getItemId(int position) {
          return mPictures != null ? position : -1;
    }
    @Override
    public View getView(int position,View convertView,ViewGroup parent) 
    {
        ImageThumbView i = null;
        try
            {               
            Thread.sleep(100);

            if (convertView == null) 
            {
                String path = mPictures.get(position);
                Log.d(((Integer)position).toString(), path);
                i = addSingleView(_li, path);
                TextView idx = (TextView) i.findViewById(R.id.caption);
                if (idx != null)
                    idx.setText(((Integer)position).toString());
            }
            else 
            {
                Log.d(((Integer)position).toString(), "ALREADY NOT NULL");
                i = (ImageThumbView) convertView;
                                    // These 2 lines were added only in desperate attempt to get it working, but it makes no difference
                String path = mPictures.get(position);
                i.updateView(path);
            }
        }
        catch(InterruptedException ie)
        {
            ie.printStackTrace();
        }
    return i;
        }
}
公共类ImageAdapter扩展了BaseAdapter{
私有列表mPictures=null;
公共图像适配器(列出图片){
MPactures=图片;
}
public int getCount(){
返回mPictures!=null?mPictures.size():0;
}
公共对象getItem(int位置){
返回mPictures!=null?mPictures.get(位置):null;
}
公共长getItemId(int位置){
返回mPictures!=null?位置:-1;
}
@凌驾
公共视图getView(int位置、视图转换视图、视图组父视图)
{
ImageThumbView i=null;
尝试
{               
睡眠(100);
if(convertView==null)
{
字符串路径=mPictures.get(位置);
Log.d(((整数)位置).toString(),路径);
i=addSingleView(_li,路径);
TextView idx=(TextView)i.findViewById(R.id.caption);
如果(idx!=null)
setText(((整数)位置).toString());
}
其他的
{
Log.d(((整数)位置).toString(),“已不为空”);
i=(ImageThumbView)convertView;
//添加这两行代码只是为了让它正常工作,但没有任何区别
字符串路径=mPictures.get(位置);
i、 更新视图(路径);
}
}
捕获(中断异常ie)
{
即printStackTrace();
}
返回i;
}
}
因此,最初它工作正常,即显示前18幅图像和下一行的几个像素。但当我开始滚动gridvew时,图像开始随机出现,即在最后一张图像之后,我从一开始就很少看到图像,以此类推。出于好奇,我尝试了以下几个样品: …并看到相同的结果

那么,我做错什么了吗?GridView究竟为什么会显示比它应该显示的更多的项目?为什么项目出现在错误的位置

比尔,
Alex

答案是视图回收

一般来说,getView应该始终采用以下形式:

public class ImageAdapter extends BaseAdapter {

    private List<String> mUrls; // put your urls here
    private Map<String, Drawable> mImages; // cache your images here

    public ImageAdapter() {
        ...
        mUrls = new ArrayList<String>();
        mImages = new HashMap<String, Drawable>();
    }

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        ViewHolder holder; // Use the ViewHolder pattern for efficiency

        if (convertView == null) {
            // first time this view has been created so inflate our layout
            convertView = View.inflate(this, R.layout.my_grid_item, null);
            holder = new ViewHolder();
            holder.image = convertView.findViewById(R.id.image);
            holder.text = convertView.findViewById(R.id.text);
            convertView.setTag(holder); // set the View holder
        } else {
           holder = (ViewHolder) convertView.getTag();
        }

        // update the current view - this must be done EVERY
        // time getView is called due to view recycling
        holder.text.setText(Integer.toString(position));

        // check our cache for the downloaded image
        final String url = mUrls.get(position);
        if (mImages.get(url) != null)
            holder.image.setImageDrawable(mImages.get(url));
        else
            loadImage(url, holder.image);

        // return our view
        return convertView;
    }

    public loadImage(final String url, final ImageView image) {
        // load an image (maybe do this using an AsyncTask
        // if you're loading from network
    }

    ...
}

那你就不应该遇到任何问题了。我也不知道你为什么需要在getView中睡觉?这将减慢GridView的滚动速度。

实际上,这里的问题是您在if或else中设置了“convertView”的内容。或者,如果视图为null,则应始终在实例化视图后执行此操作,并仅在返回视图之前设置内容

因此,您可以确保视图的内容始终是正确的,使用位置进行更新,而不是错误的视图

因此,一般来说,您应该执行以下操作:
(基于Android指南中的教程)


我也遇到同样的问题。 我朝着在背景中渲染视图的方向移动

我正在研究这些代码,如果有帮助的话:


要避免此问题,请始终在getView()中返回新创建的对象


使用标准的延迟加载程序获取图像,但从不在PostExecute上更新,这会导致图像更新过快,并获得您不希望的随机性

我甚至使用了代码:

if (result != null && !mFlinging && myPosition < 12)
{   
   imageView.setImageBitmap(result);
}

更新视图。

好的,我相信是视图回收扼杀了我的原始代码。谢谢!事实上,我说得太快了。建议的解决方案不起作用,因为它与我使用的代码之间的主要区别在于它设置了预定义的可绘图项。但至少我现在看到了问题所在。我已经更改了代码,希望能更好地适应您的用例。让我知道是否更好。你好!我有和你一样的代码,但是我在滚动时仍然会遇到随机图像。这是gridview上的已知错误吗?如果(convertView==null){这一部分非常重要。在我的自定义适配器中避免了我的java.lang.outofmemoryerror问题。我也遇到了同样的问题,但无法理解,但您让我理解了这个问题,感谢SSS,现在我通过每次刷新gridview感谢时都使convertview==null来获得ma代码。返回每个getView()中都有一个新对象。这将在滚动时导致列表结巴,并导致垃圾收集过多。签出此链接[[1]:
public View getView(int position, View convertView, ViewGroup parent) {
    ImageView imageView;
    if (convertView == null) { 
        imageView = new ImageView(mContext);
        //just creating the view if not already present
    } else {
        imageView = (ImageView) convertView;
        //re-using if already here
    }

    //here is the tricky part : set the content of the view out of the if, else
    //just before returning the view
    imageView.setImageResource(mThumbIds[position]);
    return imageView;
}
public View getView(int position, View convertView, ViewGroup parent) {
                ImageView       imageView;
                TextView        textView;
                LinearLayout    linearlayout  = new LinearLayout(mContext);
                //if (convertView == null) {
                    imageView       = new ImageView(mContext);
                    textView        = new TextView(mContext);

                    //imageView.setLayoutParams(new GridView.LayoutParams(85, 85));
                    imageView.setAdjustViewBounds(false);
                    imageView.setScaleType(ImageView.ScaleType.CENTER_CROP);
                    imageView.setPadding(4, 8, 8, 8);                   
                    imageView.setBackgroundResource(mThumbIds[position]);

                    textView.setText(mThumbText[position]);                
                    textView.setGravity(0x01);
                    textView.setMaxLines(2);

                    //textView.setPadding(20, 0, 0, 0);
                    linearlayout.addView(imageView,0);
                    linearlayout.addView(textView,1);
                    linearlayout.setPadding(4, 4, 4, 4);
                    linearlayout.setOrientation(LinearLayout.VERTICAL);
                    linearlayout.setGravity(0x01);

               // } else {
                    //linearlayout = (LinearLayout) convertView;
                //}

                return linearlayout;
            }
if (result != null && !mFlinging && myPosition < 12)
{   
   imageView.setImageBitmap(result);
}
mPhotoView.setOnScrollListener(new OnScrollListener() { 



            public void onScroll(AbsListView view, int firstVisibleItem,
                    int visibleItemCount, int totalItemCount) {
                // TODO Auto-generated method stub

            }

            public void onScrollStateChanged(AbsListView view, int scrollState) {
                // TODO Auto-generated method stub
                if(scrollState != SCROLL_STATE_IDLE) {
                  mAdapter.setFlinging(true);
                } else {
                  mAdapter.setFlinging(false);   
                }

                int first = view.getFirstVisiblePosition(); 
                int count = view.getChildCount(); 

                if (scrollState == SCROLL_STATE_IDLE || (first + count > mAdapter.getCount()) ) { 
                    mPhotoView.invalidateViews(); 
                }

            } 
        });