5

Galleryウィジェットで使用する遅延ロード アダプターを作成したいと考えています。

つまりgetView()、すぐに を返し、ImageView後で他のメカニズムがそのsetImageBitmap()メソッドを非同期的に呼び出します。ImageView拡張する「怠惰な」を作成することでこれを行いましたImageView

public class GalleryImageView extends ImageView {

    // ... other stuff here ...

    public void setImage(final Looper looper, final int position) {

    final Uri uri = looper.get(position);
    final String path = looper.sharePath(position);

    new Thread(new Runnable() {

        @Override
        public void run() {
            GalleryBitmap gbmp = new GalleryBitmap(context, uri, path);
            final Bitmap bmp = gbmp.getBitmap(); // all the work is here
            handler.post(new Runnable() {

                @Override
                public void run() {
                    if (GalleryImageView.this.getTag().equals(uri)) {
                        setImageBitmap(bmp);
                    }
                }
            });
        }
    }).start();
}

}

でゆっくりスクロールするとGallery、中央の画像が中央に飛び出し続けます。正確に説明するのは難しいですが、本当に面倒です。スピナーアダプターにも同じアプローチを試みましたが、そこでは完全に機能します。

何か案は?

4

3 に答える 3

12

答えがあります!

setImage...メソッドのいずれかがImageView内部で呼び出されると、レイアウト パスが要求されます。たとえば、setImageBitmap()上記のように定義されています。

public void setImageBitmap(Bitmap bm) {
    setImageDrawable(new BitmapDrawable(mContext.getResources(), bm));
}

呼び出す

public void setImageDrawable(Drawable drawable) {
    if (mDrawable != drawable) {
        mResource = 0;
        mUri = null;
        updateDrawable(drawable);
        requestLayout(); //layout requested here!
        invalidate();
    }
}

これは、現在ギャラリーの中心に最も近い画像の中心にギャラリーが「スナップ」する効果があります。

これを防ぐために私が行ったことは、ギャラリーに読み込まれるビューに明示的な高さと幅 ( dips 単位) を持たせ、ImageViewレイアウト要求を無視するサブクラスを使用することです。ギャラリーには最初はまだレイアウト パスがあるため、これは機能しますが、ギャラリー内の画像が変更されるたびにこれを行う必要はありませんWRAP_CONTENTinvalidate()as が画像内で呼び出されsetImageDrawable()ても、設定時に描画されることに注意してください。

以下の私の非常に単純なImageViewサブクラス!

/**
 * This class is useful when loading images (say via a url or file cache) into
 * ImageView that are contained in dynamic views (Gallerys and ListViews for
 * example) The width and height should be set explicitly instead of using
 * wrap_content as any wrapping of content will not be triggered by the image
 * drawable or bitmap being set (which is normal behaviour for an ImageView)
 * 
 */
public class ImageViewNoLayoutRefresh extends ImageView
{
    public ImageViewNoLayoutRefresh(Context context, AttributeSet attrs, int defStyle)
    {
        super(context, attrs, defStyle);
    }

    public ImageViewNoLayoutRefresh(Context context, AttributeSet attrs)
    {
        super(context, attrs);
    }

    public ImageViewNoLayoutRefresh(Context context)
    {
        super(context);
    }

    @Override
    public void requestLayout()
    {
        // do nothing - for this to work well this image view should have its dims
        // set explicitly
    }
}

編集: onItemSelected アプローチも機能することを言及する必要がありますが、フリングが行われている間にそれにフックする必要があったため、上記を思いつきました。これはより柔軟なアプローチだと思います

于 2011-07-21T08:29:06.037 に答える
12

解決策は、いつサムネイルを取得するかについて、よりインテリジェントな方法を実装することです。ユーザーがリストを飛び回っている間にサムネイルを取得しても意味がありません。基本的に、Romain Guy のShelvesアプリケーションに実装されたようなものが必要です。

最も応答性の高いギャラリーを取得するには、何らかの形式のメモリ内キャッシュを実装し、次のことを行う必要があります。

  • のメモリ内キャッシュに存在する場合にのみ、イメージを設定しますgetView。イメージが設定されたかどうか、またはダウンロードが必要かどうかを示すフラグを設定します。また、SD カードと内部メモリのキャッシュにメモリを維持することもできます。フリングが現在進行中でない場合は、inSampleSizeスクロールするだけで表示される低解像度 (16 または 8 に設定) バージョン (高解像度) を表示できます。ユーザーが手を離して画像に落ち着くと、バージョンがロードされます。
  • ユーザーの指が上にある場合にのみ、ダウンロードが必要なすべての表示項目の新しいサムネイルをダウンロードするを追加しますOnItemSelectedListener(および初期化時に必ず呼び出します) (およびを使用して、表示されるビューの範囲を見つけることができます)。setCallbackDuringFling(false)getFirstVisiblePositiongetLastVisiblePosition
  • また、ユーザーが指を離したときに、1. ユーザーが指を置いてから選択した位置が変更されたかどうかを確認し、2. ユーザーが原因でダウンロードが開始されたかどうかを確認します。そうOnItemSelectedListenerでない場合はダウンロードを開始します。これは、フリンジが発生しないケースをキャッチするOnItemSelectedためであり、この状況では常に指を下ろした状態で呼び出されるため、何も実行されません。onItemSelectedハンドラーを使用して、ギャラリーのアニメーション時間までにダウンロードの開始を遅らせます (が呼び出されるたびに、またはACTION_DOWNイベントを取得するたびに、このハンドラーに投稿された遅延メッセージを必ずクリアしてください。
  • 画像がダウンロードされた後、表示されているビューがこの画像を要求しているかどうかを確認し、それらのビューを更新します

また、デフォルトのギャラリー コンポーネントはビューのリサイクルを適切に実装していないことに注意してください (アダプター内の各位置に固有のビューがあると想定し、これらのアイテムが画面外に移動すると、これらのアイテムのリサイクラーをクリアして、かなり無意味になります)。編集:もっと見ても無意味ではありませんが、次/前のビューに関してリサイクラーではなく、getViewレイアウトの変更中に現在のビューを呼び出す必要がないようにするのに役立ちます。

これは、メソッドにconvertView渡されるパラメーターgetViewが null ではないことが多いことを意味します。つまり、多くのビューを膨らませることになります (これは高価です) それに関するいくつかのヒントについて。(PS: その後、そのコードを変更しました。レイアウト フェーズとスクロール フェーズには別のごみ箱を使用し、レイアウト フェーズでは、レイアウトのごみ箱にビューを配置して、その位置に応じてビューを取得します。ビンから取得したビューは、まったく同じビューになるため、null ではありません。また、レイアウト フェーズの後にレイアウトのごみ箱をクリアします。これにより、物事が少しすっきりします)。

PS: また、何をするかにも十分注意してください。OnItemSelectedつまり、上記の場所でない限り、できるだけ何もしないようにしてください。たとえばTextView、ギャラリーの上の にテキストを設定していましたOnItemSelected。この呼び出しを、サムネイルを更新した場所と同じポイントに移動するだけで、顕著な違いが生じました。

于 2011-05-04T12:32:36.960 に答える
2

これは、Gallery の onLayout メソッドのバグである可能性があります。考えられる回避策については、 http://code.google.com/p/android/issues/detail?id=16171を確認してください。

于 2011-05-04T04:49:51.380 に答える