在gridview适配器中,getView(position == 0)被调用太多次以在装载器中的setImageBitmap()时测量布局

ray*_*ang 23 android android-gridview android-view

我有一个GridView显示一些图标.

在我从Android开发者网站上有效地阅读这个显示位图之前,我正在从getView()适配器直接解码本地路径中的位图,如下所示:

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

这种方式无论如何工作正常,我称之为[直接模式],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)
...
Run Code Online (Sandbox Code Playgroud)

然后我试图将代码更改为有效显示位图的文章中提到的[Loader Mode] ,如下所示:

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

loadIcon():

...
final CacheImageLoader loader = new CacheImageLoader(getActivity(), imageView, imageUrl, savePath);
final AsyncDrawable asyncDrawable = new AsyncDrawable(getResources(), placeHolderBitmap, loader);
imageView.setImageDrawable(asyncDrawable);
Run Code Online (Sandbox Code Playgroud)

在Loader的听众中:

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

基本上,它与训练代码相同,实际上,这种方式也可以正常工作.但是,我发现了一些不同的东西,在这种模式下getView(),适配器中的方法被调用了太多次,但是,这些重复调用此方法始终使用"position"参数== 0,这意味着etView(0, X, X)重复调用g .

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)
Run Code Online (Sandbox Code Playgroud)

这不好,因为我正在使用装载机getView().我检查了源代码,发现它们最初是imageView.setImageBitmap(result)在loader的onLoadComplete方法中调用的 ,并在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();
    }
}
Run Code Online (Sandbox Code Playgroud)

这里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();
    }
}
Run Code Online (Sandbox Code Playgroud)

然而不同之处在于:在[直接模式]中, mParent.requestLayout()调用一次,但在[Loader模式]中,每次调用时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);
    ...
Run Code Online (Sandbox Code Playgroud)

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

Dan*_*lme 7

isLayoutRequested是在那里告诉你一个布局是否已经等待这个View.也就是说,在requestLayout调用之后,isLayoutRequested将返回true直到下一个布局传递完成.这种检查的唯一原因requestLayout是为了避免反复呼叫requestLayout父母,无论如何要进行布局.isLayoutRequested这里是一只红鲱鱼:它不是onMeasure被反复召唤的原因.

根本问题是ImageView每当您更改其drawable时请求新布局.这有两个原因: -

  1. ImageView的大小可能取决于drawable的大小,如果adjustViewBounds已设置.这可能反过来影响其他视图的大小,具体取决于布局:ImageView本身没有足够的信息可以知道.
  2. ImageView.onMeasureImageView根据比例模式,负责计算必须调整大小以适应范围的多少.如果新的drawable与旧drawable的尺寸不同,则ImageView 必须再次测量以重新计算所需的比例.

你只能通过保存加载器Bitmap返回的s 的本地缓存来解决拥有太多加载器的问题.缓存可能拥有所有的Bitmap,如果你知道有没有那么多,或者只是在S ñ最近使用的.在您的getView首先检查Bitmap缓存中是否存在该项目,如果存在,则返回ImageView已设置为该项目Bitmap.只有当它不在缓存中时才需要使用加载器.

小心:如果对基础数据的改变,你现在需要确保在同一时间调用缓存中无效invalidateGridView或通知通过ContentResolver.我已经在我的应用程序中使用了一些自制代码来实现这一点,它对我来说很有效,但Square的优秀人员有一个名为Picasso的开源库,如果您愿意,可以为您完成所有艰苦的工作.