【问题标题】:GridView scrolling problem on AndroidAndroid上的GridView滚动问题
【发布时间】:2011-03-20 15:05:23
【问题描述】:

这一定是我忽略的非常简单的事情,但我有以下问题(帖子相当长,但我想提供尽可能多的信息:))。

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

所以最初它可以正常工作,即它显示前 18 个图像和下一行的几个像素。但是当我开始滚动gridvew 时,图像开始随机出现,即在最后一张图像之后,我从一开始就很少看到等等。出于好奇,我尝试了几个这样的示例:http://androidsamples.blogspot.com/2009/06/how-to-display-thumbnails-of-images.html ...并看到相同的结果。

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

BR, 亚历克斯

【问题讨论】:

标签: android gridview


【解决方案1】:

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

因此,您确定视图的内容始终是正确的,使用位置更新,而不是错误的回收视图。

所以一般来说你应该做以下事情:
(基于Android指南here的教程)

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

【讨论】:

  • 我遇到了同样的问题,但无法理解,但你让我明白了这个问题,谢谢,现在我通过每次刷新 gridview 时都设置 convertview==null 来让我的代码工作谢谢
【解决方案2】:

答案是视图回收。

一般而言,您的 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
    }

    ...
}

ViewHolder 类看起来像这样

public class ViewHolder {
    ImageView thumbImage;
    TextView text;
}

那么您应该不会遇到任何问题。另外我不确定你为什么需要在你的 getView 中睡觉?这会减慢 GridView 的滚动速度。

【讨论】:

  • 好的,我相信是视图回收杀死了我的原始代码。非常感谢!
  • 其实,我说得太早了。建议的解决方案不起作用,因为它与我使用的代码之间的主要区别在于它设置了预定义的可绘制对象。但至少我现在看到了问题。
  • 我已经更改了我的代码,希望能更好地适应您的用例。让我知道它是否更好。
  • 您好!我的代码与您的代码相同,但滚动时仍然遇到随机图像。这是 gridview 上的已知错误吗?
  • if (convertView == null) { 这部分很重要。在我的自定义适配器中避免了我的 java.lang.outofmemoryerror 问题。
【解决方案3】:

使用标准的 Lazy Loader 获取图像,但从不更新 onpostexecute,这会导致图像更新过快并获得您不想要的随机性。

我什至使用了代码:

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

int onpostexecute 然后会为第一个屏幕正确更新图像,但如果你扔图像屏幕然后太快回到第一个屏幕再次获得随机性,所以我现在使用:

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

            } 
        }); 

更新视图。

【讨论】:

    【解决方案4】:

    我遇到了同样的问题。 我正朝着在后台渲染视图的方向前进;

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

    http://blog.tomgibara.com/post/7665158012/android-adapter-view-rendering

    https://github.com/desertjim/LazyLoadingGridView

    【讨论】:

      【解决方案5】:

      避免这个问题的人总是在 getView() 中返回新创建的对象

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

      【讨论】:

      • 在每个 getView() 中返回一个新对象是一个非常糟糕的主意。这将导致您的列表在滚动时出现卡顿,并导致过多的垃圾回收。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多