23

GridViewいくつかのアイコンを表示するためのがあります。

Android開発者サイトからこの「ビットマップの表示」を効率的に読む前にgetView()、次のように、アダプターのローカルパスから直接ビットマップをデコードしていました。

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)
...

次に、次のように、記事「ビットマップを効率的に表示する」で説明されている[ローダーモード]にコードを変更しようとしています。

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

ローダーのリスナーで:

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

基本的にはトレーニングコードと同じですが、実際にはこの方法でも問題なく機能します。しかし、私は何か違うものを見つけました。このモードではgetView()、アダプターのメソッドが何度も呼び出されましたが、このメソッドへのこれらの繰り返し呼び出しは常に「position」パラメーター== 0であり、何かがgetView(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()。ソースコードを確認したところ、元々imageView.setImageBitmap(result)ローダーの 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();
    }
}

ここでrequestLayout()は、Viewのメソッドであり、View.classの[DirectMode]または[LoaderMode]のいずれかで常に実行されます。

public void requestLayout() {
    mPrivateFlags |= FORCE_LAYOUT;
    mPrivateFlags |= INVALIDATED;

    if (mLayoutParams != null) {
        mLayoutParams.onResolveLayoutDirection(getResolvedLayoutDirection());
    }

    if (mParent != null && !mParent.isLayoutRequested()) {
        mParent.requestLayout();
    }
}

ただし、違いは次のとおりです。[ダイレクトモード]では、 mParent.requestLayout()は1回呼び出されますが、[ローダーモード]では、呼び出すたびにimageView.setImageBitmap(result);、 も呼び出されます。これは、リターンをmParent.requestLayout()意味し、を呼び出すことで子供のレイアウトを測定します。最初の子供にそして次に引き起こす:mParent.isLayoutRequested()falsemParent.requestLayout();GridViewobtainView()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それとも通常の場合ですか?

4

4 に答える 4

7

isLayoutRequestedレイアウトがこの に対して既に保留されているかどうかを通知するためだけに存在しますView。つまり、requestLayout呼び出された後isLayoutRequested、次のレイアウト パスが完了するまで true を返します。このチェックインの唯一の理由は、とにかくレイアウトを実行しようとしている場合に、親requestLayoutを繰り返し呼び出すことを避けるためです。は、ここではニシンです。これは、繰り返し呼び出される原因ではありません。requestLayoutisLayoutRequestedonMeasure

根本的な問題はImageView、ドローアブルを変更するたびに新しいレイアウトを要求することです。これが必要な理由は 2 つあります。

  1. ImageView のサイズは、設定されている場合、ドローアブルのサイズに依存する場合adjustViewBoundsがあります。これは、レイアウトによっては、他のビューのサイズに影響を与える可能性があります。 には、ImageView十分な情報がありません。
  2. ImageView.onMeasureImageViewは、スケール モードに応じて、 の境界に収まるようにドローアブルをどれだけサイズ変更する必要があるかを計算します。新しいドローアブルが古いドローアブルと同じサイズでない場合は、再度測定して、必要なスケーリングを再計算する必要がありますImageView

Bitmapローダーによって返される s のローカル キャッシュを維持することによってのみ、ローダーが多すぎるという問題を解決できます。キャッシュBitmapには、それほど多くないことがわかっている場合、または最近使用されたn 個だけがあることがわかっている場合は、すべての s が含まれている可能性があります。getView、まずBitmapそのアイテムの がキャッシュに存在するかどうかを確認し、存在する場合は、その にImageView既に設定されている を返しBitmapます。ローダーを使用する必要があるのは、キャッシュにない場合のみです。

invalidate注意: 基になるデータが変更される可能性がある場合は、 を呼び出すか、GridViewを介して通知すると同時にキャッシュを無効にする必要がありますContentResolver。アプリでこれを実現するためにいくつかの自作コードを使用しましたが、それは私にとってはうまく機能しますが、Square の善良な人々はPicassoと呼ばれるオープンソース ライブラリを持っており、必要に応じてすべての面倒な作業を行ってくれます。

于 2013-08-09T11:32:26.013 に答える
1

これは通常の動作であり、Android は同じ位置に対して複数回 getView を呼び出すことができます。必要な場合にのみ getView でサムネイルを取得/設定するのは開発者です (つまり、サムネイルが設定されていない場合、またはサムネイルのパスが変更されている場合)。それ以外の場合は、getView のパラメーターとして取得する convertView を返すだけです。

于 2013-01-02T16:14:55.277 に答える