在gridview适配器中,getView(position == 0)被调用太多时间来测量布局时,setImageBitmap()

那么…这是我的第一个问题,所以也许我不清楚如何描述它。

基本上,我有一个GridView显示一些图标。

在阅读完 Android开发人员站点显示位图之后 ,我直接在适配器的getView()中从本地path解码位图,如下所示:

 public View getView(int position, View convertView, ViewGroup parent) { ... ImageView icon = ...... (from getTag() of convertView) icon.setImageBitmap(BitmapUtil.decode(iconPath)); ... } 

这种方式很好,我把它称为[直接模式], getView()方法的输出日志应该是:

 getView(0) // measure kid's layout. getView(0) getView(1) getView(2) ... getView(n) // when scrolling gridview. getView(n+1) ... getView(n+3) // scrolling again. getView(n+4) ... 

那么我正在尝试将代码更改为有效地显示位图中提到的[Loader模式],如下所示:

 public View getView(int position, View convertView, ViewGroup parent) { ... ImageView icon = ...... (from getTag() of convertView) loadIcon(icon, iconPath); ... } 

loadIcon()

 ... final CacheImageLoader loader = new CacheImageLoader(getActivity(), imageView, imageUrl, savePath); final AsyncDrawable asyncDrawable = new AsyncDrawable(getResources(), placeHolderBitmap, loader); imageView.setImageDrawable(asyncDrawable); 

在Loader的监听器中:

  @Override public void onLoadComplete(Loader<Bitmap> arg0, Bitmap arg1) { ... ImageView imageView = imageViewReference.get(); if (result != null && imageView != null) { imageView.setImageBitmap(result); } } 

基本上和训练代码一样,实际上这个方法也很好。 然而,我发现了一些不同的东西,在这种模式下,适配器中的getView()方法被调用了太多次,但是这些重复调用这个方法总是以“position”参数== 0,这意味着调用g etView(0, X, X)

 getView(0) // measure kid's layout. getView(0) getView(1) getView(2) ... getView(0) // loader completed then imageView.setImageBitmap(result); getView(0) // same as above getView(0) getView(0) ... getView(n) // when scrolling gridview. getView(n+1) getView(n+2) getView(0) // loader completed then imageView.setImageBitmap(result); getView(0) // same as above getView(0) ... getView(n+3) // scrolling again. getView(n+4) getView(0) // loader completed then imageView.setImageBitmap(result); getView(0) // same as above getView(0) 

这是不好的,因为我在getView()使用加载器。 我已经检查了源代码,发现它们最初是由loader的onLoadComplete方法中的onLoadComplete imageView.setImageBitmap(result)调用的,在ImageView

  /** * Sets a drawable as the content of this ImageView. * * @param drawable The drawable to set */ public void setImageDrawable(Drawable drawable) { ... int oldWidth = mDrawableWidth; int oldHeight = mDrawableHeight; updateDrawable(drawable); if (oldWidth != mDrawableWidth || oldHeight != mDrawableHeight) { requestLayout(); } invalidate(); } } 

在这里, requestLayout()是View的方法,并且总是在View.class的[Direct Mode]或[Loader Mode]中执行:

 public void requestLayout() { mPrivateFlags |= FORCE_LAYOUT; mPrivateFlags |= INVALIDATED; if (mLayoutParams != null) { mLayoutParams.onResolveLayoutDirection(getResolvedLayoutDirection()); } if (mParent != null && !mParent.isLayoutRequested()) { mParent.requestLayout(); } } 

不同的是:在[Direct Mode]中, mParent.requestLayout()被调用一次,但在[Loader Mode]中,每次调用imageView.setImageBitmap(result);mParent.requestLayout()也会被调用,这意味着mParent.isLayoutRequested()返回false ,而mParent.requestLayout(); 将导致GridView测量其孩子的布局,通过调用obtainView()第一个孩子,然后导致getView(0, X, X)

 @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { ... mItemCount = mAdapter == null ? 0 : mAdapter.getCount(); final int count = mItemCount; if (count > 0) { final View child = obtainView(0, mIsScrap); ... 

所以,我的问题是:为什么mParent.isLayoutRequested()返回false如果我正在使用[加载模式]? 还是只是一个正常的情况?

Solutions Collecting From Web of "在gridview适配器中,getView(position == 0)被调用太多时间来测量布局时,setImageBitmap()"

isLayoutRequested就在那里告诉你一个布局是否已经挂起这个View 。 也就是说,在调用requestLayout之后, isLayoutRequested将返回true,直到下一个布局过程完成。 在requestLayout进行这种检查的唯一原因是为了避免在父节点上重复调用requestLayout ,无论如何要进行布局。 isLayoutRequested在这里是一个红鲱鱼:它不是重复调用onMeasure的原因。

根本问题是ImageView每当您更改绘图时都会请求新的布局。 这是必要的,原因有两个:

  1. 如果设置了adjustViewBounds ,则ImageView的大小可能取决于drawable的大小。 这可能会反过来影响其他视图的大小,具体取决于布局: ImageView本身没有足够的信息来知道。
  2. ImageView.onMeasure负责根据缩放模式计算出drawable必须resize以适应ImageView的边界。 如果新绘图的大小与旧绘图不一样,则必须再次测量ImageView以重新计算所需的缩放比例。

您只能通过保持加载器返回的Bitmap的本地caching来解决拥有太多加载器的问题。 如果你知道这个数字不是那么多,或者只是最近使用的n个 ,caching可能有所有的Bitmap 。 在您的getView ,首先检查该项目的Bitmap是否存在于caching中,如果是,则返回已经设置为该BitmapImageView 。 只有当它不在caching中时,你才需要使用一个加载器。

要小心:如果基础数据可以改变,现在需要确保在调用GridView invalidate或通过ContentResolver通知的同时,使cachinginvalidate 。 我在自己的应用程序中使用了一些自制软件代码来实现这一点,它对我来说很好,但Square的好人有一个叫做毕加索的开源库,如果你愿意,可以为你做所有的辛苦工作。

这是正常的行为,android可以多次调用同一位置的getView。 它是在开发者获取/设置缩略图只有在需要时(即如果缩略图没有设置或缩略图path有变化)。 在其他情况下,只需返回我们在getView中获取参数的convertView。

我有同样的问题。 网格总是测量它的第一个孩子,即使我在30的位置。
我只是通过在getView top中添加此检查来绕过整个getView代码:

 @Override public View getView(final int position, View convertView, ViewGroup parent) { // Patch for multiple getView for position 0 if(convertView!=null && position==0 && viewGrid.getFirstVisiblePosition()>1) return convertView; 

这并不会阻止getView被调用,但至less文本,图像和布局的变化不会运行。

尝试在任何引用高度的地方调整你的xml布局,因为android会做一个度量来绘制每一次,并将重新绘制单元格。 尝试使用listview match_parent和行上的单元格一个完全的高度。 对不起我英语不好。