3

ギャラリー活動:

==================
    TextView (tv)
------------------
 | tv | tv | tv |
------------------
    GalleryFragment: dynamically created; replaces FrameLayout
        FrameLayout: W&H: match_parent


    imageView: W&H are match_parent; scaleType: fitCenter; 
        layout_below: the tv's above;
        layout_above: the TextView below;
            result: imageView fits snuggly between.
        Parent: RelativeLayout W&H: match_parent        


------------------
    TextView
==================

ギャラリー アクティビティの説明: このアクティビティでは、特定のコレクションに属する画像を 1 つずつ表示します。ユーザーが画像をクリック: 新しいフラグメントが次の画像を表示します。

次の方法を理解するのに数日分の問題がありました。

  • A. imageView のサイズを取得して、ビットマップを適切にスケーリングできるようにします。
  • B. 無限ループが発生しています。そして、私はその理由を知っていると思います。論理的には、ビットマップを imageView に詰め込み、おそらくonPreDraw()再び起動し、コードがレースに出た瞬間を意味します。…が、直し方がわかりません。
  • C. 答えかもしれないと思っsynchronizationたが、これまで使用したことがなく、正しく使用できていないと思う。
  • D. チェックを入れるとうまくいくかもしれないと思ったのif(workerThread == null)ですが、ほんの一瞬だけ遅くなりました。

この時点で私は迷っています。動的に追加されたフラグメント内に埋め込まれた ImageView のサイズを取得する方法がわかりませんが、それらのサイズ使用してビットマップのサイズを縮小し、サイズ変更後にそのビットマップをフラグメントの ImageView にロードします。

説明が必要な場合は、お尋ねください。

以下は、GalleryFragment の onCreateView と BitmapWorkerTask のコードです。その下には、フィルタリングした logcat のペーストがあります。

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
        Bundle savedInstanceState) {
    // inflate view
    View view = inflater.inflate(R.layout.frag_gallery_image, container, false);

    // get view handles
    imageView = (ImageView)view.findViewById(R.id.gallery_image);   

    // TESTING
    ViewTreeObserver vto = imageView.getViewTreeObserver();
    vto.addOnPreDrawListener(new OnPreDrawListener() {

        @Override
        public boolean onPreDraw() {
            // TESTING
            viewWidth = imageView.getMeasuredWidth();
            viewHeight = imageView.getMeasuredHeight();

            Log.d(TAG, SCOPE +"onPreDraw viewWidth: " +viewWidth +", viewHeight: " +viewHeight);

            // TESTING: Added because "synchronization" attempt didn't work 
            if(bmwt == null){
                loadImage(viewWidth, viewHeight);                   
            }

            return true;
        }
    });

    // set view actions     
    imageView.setOnClickListener(GalleryFragment.this);     

    return view;
}

/**
 * A synchronized method for loading bitmaps in background thread.
 * Synchronization: http://docs.oracle.com/javase/tutorial/essential/concurrency/syncmeth.html
 * ...something isn't working as intended...
 */
private synchronized void loadImage(int viewWidth, int viewHeight){
    // TESTING
    Log.d(TAG, SCOPE +"loadImage viewWidth: " +viewWidth +", viewHeight: " +viewHeight);

    bmwt = new BitmapWorkerTask(imageView, viewWidth, viewHeight);
    bmwt.execute(imageUri);
}


/**
 * BitmapWorkerTask is a subclass of Asynctask for the purpose of loading
 * images off of the UI thread. 
 */
private class BitmapWorkerTask extends AsyncTask<String, Void, Bitmap>{
    private String uri;
    private int imageViewWidth;
    private int imageViewHeight;

    // Constructor.
    public BitmapWorkerTask(ImageView imageView, int width, int height){
        imageViewWidth = width;
        imageViewHeight = height;
    }

    // Decode image in background.
    @Override
    protected Bitmap doInBackground(String... params) {
        uri = params[0];
        final Bitmap bitmap =  ImageUtils.decodeSampledBitmapFromUri(uri, imageViewWidth, imageViewHeight);

        return bitmap;
    }

    @Override
    protected void onPostExecute(Bitmap bitmap) {
        if (isCancelled()) {
            bitmap = null; // use some default bitmap
        }
        if (bitmap != null) {
            Log.d(TAG, SCOPE +"bitmap kilobyte count: "+ bitmap.getByteCount() / 1024);

            imageView.setImageBitmap(bitmap);

            // make BitmapWorkerTask reference null again.
            bmwt = null;
        }// else do nothing.
    }       
}

ログキャット。「ビットマップ キロバイト カウント」は、asyncTask の onPostExecute で呼び出されることに注意してください。...そして、ロギングが停止することを望んでいた場所のようなものです。

06-13 01:50:41.442: D/ROSS(12982): GalleryFragment: imageUri: /mnt/sdcard/So so so beautiful.jpg
06-13 01:50:41.522: D/ROSS(12982): GalleryFragment: onPreDraw viewWidth: 480, viewHeight: 692
06-13 01:50:41.522: D/ROSS(12982): GalleryFragment: loadImage viewWidth: 480, viewHeight: 692
06-13 01:50:41.632: D/ROSS(12982): GalleryFragment: onPreDraw viewWidth: 480, viewHeight: 692
06-13 01:50:41.827: D/ROSS(12982): GalleryFragment: onPreDraw viewWidth: 480, viewHeight: 692
06-13 01:50:43.053: D/ROSS(12982): GalleryFragment: bitmap kilobyte count: 1183
06-13 01:50:43.142: D/ROSS(12982): GalleryFragment: onPreDraw viewWidth: 480, viewHeight: 692
06-13 01:50:43.142: D/ROSS(12982): GalleryFragment: loadImage viewWidth: 480, viewHeight: 692
06-13 01:50:43.632: D/ROSS(12982): GalleryFragment: bitmap kilobyte count: 1183
06-13 01:50:43.713: D/ROSS(12982): GalleryFragment: onPreDraw viewWidth: 480, viewHeight: 692
06-13 01:50:43.713: D/ROSS(12982): GalleryFragment: loadImage viewWidth: 480, viewHeight: 692
06-13 01:50:44.023: D/ROSS(12982): GalleryFragment: bitmap kilobyte count: 1183
06-13 01:50:44.101: D/ROSS(12982): GalleryFragment: onPreDraw viewWidth: 480, viewHeight: 692
06-13 01:50:44.101: D/ROSS(12982): GalleryFragment: loadImage viewWidth: 480, viewHeight: 692
06-13 01:50:44.332: D/ROSS(12982): GalleryFragment: bitmap kilobyte count: 1183
06-13 01:50:44.414: D/ROSS(12982): GalleryFragment: onPreDraw viewWidth: 480, viewHeight: 692
06-13 01:50:44.414: D/ROSS(12982): GalleryFragment: loadImage viewWidth: 480, viewHeight: 692
06-13 01:50:44.681: D/ROSS(12982): GalleryFragment: bitmap kilobyte count: 1183
06-13 01:50:44.761: D/ROSS(12982): GalleryFragment: onPreDraw viewWidth: 480, viewHeight: 692
06-13 01:50:44.761: D/ROSS(12982): GalleryFragment: loadImage viewWidth: 480, viewHeight: 692
06-13 01:50:45.151: D/ROSS(12982): GalleryFragment: bitmap kilobyte count: 1183
06-13 01:50:45.227: D/ROSS(12982): GalleryFragment: onPreDraw viewWidth: 480, viewHeight: 692
06-13 01:50:45.231: D/ROSS(12982): GalleryFragment: loadImage viewWidth: 480, viewHeight: 692
06-13 01:50:45.521: D/ROSS(12982): GalleryFragment: bitmap kilobyte count: 1183
06-13 01:50:45.593: D/ROSS(12982): GalleryFragment: onPreDraw viewWidth: 480, viewHeight: 692
06-13 01:50:45.593: D/ROSS(12982): GalleryFragment: loadImage viewWidth: 480, viewHeight: 692
06-13 01:50:45.962: D/ROSS(12982): GalleryFragment: bitmap kilobyte count: 1183

編集:

ニコラスの提案を実装した後、合計で logcat は次のようになります。

06-13 14:51:44.976: D/ROSS(15465): GalleryFragment: imageUri: /mnt/sdcard/RossAndClay - Copy (12).JPG
06-13 14:51:45.146: D/ROSS(15465): GalleryFragment: onPreDraw viewWidth: 480, viewHeight: 692
06-13 14:51:45.146: D/ROSS(15465): GalleryFragment: loadImage viewWidth: 480, viewHeight: 692
06-13 14:51:45.376: D/ROSS(15465): GalleryFragment: onPreDraw viewWidth: 480, viewHeight: 692
06-13 14:51:45.422: D/ROSS(15465): GalleryFragment: loadImage viewWidth: 480, viewHeight: 692
06-13 14:51:45.626: D/ROSS(15465): GalleryFragment: onPreDraw viewWidth: 480, viewHeight: 692
06-13 14:51:45.626: D/ROSS(15465): GalleryFragment: loadImage viewWidth: 480, viewHeight: 692
06-13 14:51:49.336: D/ROSS(15465): GalleryFragment: bitmap kilobyte count: 1183
06-13 14:51:49.356: D/ROSS(15465): GalleryFragment: onPreDraw viewWidth: 480, viewHeight: 692
06-13 14:51:51.327: D/ROSS(15465): GalleryFragment: bitmap kilobyte count: 1183
06-13 14:51:51.336: D/ROSS(15465): GalleryFragment: onPreDraw viewWidth: 480, viewHeight: 692
06-13 14:51:53.395: D/ROSS(15465): GalleryFragment: bitmap kilobyte count: 1183
06-13 14:51:53.469: D/ROSS(15465): GalleryFragment: onPreDraw viewWidth: 480, viewHeight: 692

そして、画像をクリックした後、フラグメントで、これはそのイベントの合計 logcat です。

06-13 14:54:41.315: D/ROSS(15465): GalleryFragment: imageUri: /mnt/sdcard/RossAndClay - Copy (11).JPG
06-13 14:54:41.402: D/ROSS(15465): GalleryFragment: onPreDraw viewWidth: 480, viewHeight: 692
06-13 14:54:41.406: D/ROSS(15465): GalleryFragment: onPreDraw viewWidth: 480, viewHeight: 692
06-13 14:54:41.406: D/ROSS(15465): GalleryFragment: loadImage viewWidth: 480, viewHeight: 692
06-13 14:54:41.655: D/ROSS(15465): GalleryFragment: onPreDraw viewWidth: 480, viewHeight: 692
06-13 14:54:41.665: D/ROSS(15465): GalleryFragment: onPreDraw viewWidth: 480, viewHeight: 692
06-13 14:54:41.665: D/ROSS(15465): GalleryFragment: loadImage viewWidth: 480, viewHeight: 692
06-13 14:54:44.285: D/ROSS(15465): GalleryFragment: bitmap kilobyte count: 1183
06-13 14:54:44.305: D/ROSS(15465): GalleryFragment: onPreDraw viewWidth: 480, viewHeight: 692
06-13 14:54:44.305: D/ROSS(15465): GalleryFragment: onPreDraw viewWidth: 480, viewHeight: 692
06-13 14:54:46.965: D/ROSS(15465): GalleryFragment: bitmap kilobyte count: 1183
06-13 14:54:47.036: D/ROSS(15465): GalleryFragment: onPreDraw viewWidth: 480, viewHeight: 692
06-13 14:54:47.036: D/ROSS(15465): GalleryFragment: onPreDraw viewWidth: 480, viewHeight: 692

画像をクリックするたびに、logcat のエントリ数がどんどん長くなります。私のフラグメントトランザクションは次のようになります:

/*
 * Create the fragment that holds the collection image.
 * @param imageUri
 */
private void createImageFragment(String imageUri) {
    // With each click wipe previous entry, ie: there's no going back.
    getFragmentManager().popBackStack();

    // create new fragment
    GalleryFragment galleryFrag = GalleryFragment.newInstance(imageUri);

    // programmatically add new fragment
    FragmentTransaction ft = getFragmentManager().beginTransaction();
    ft.replace(R.id.gallery_imageFrame, galleryFrag, GALLERY_FRAG);
    ft.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN);
    ft.commit();        
}

ここに変更しようとした瞬間popBackStack()popBackStackImmediate()増え続ける logcat エントリのリストが停止しました。さて、さまざまな画像サイズを読み込んでいくつかのテストを行った後、画像がどれだけ大きいかによって loadImage() が呼び出される回数が決まるように見えます。 imageView のイメージが実際に設定される前に、3 つの非同期タスクが起動されています。loadImage()したがって、ここでのタスクは、が 1 回だけ呼び出されるようにする方法を理解することです。

編集2:

特定の問題が私たちの脳を凍らせてしまうことがあります。クラス フィールドprivate boolean thisMethodCalled = false;と onPreDraw で単純にメソッドへの複数の呼び出しを解決しました。

            if(!thisMethodCalled){
                loadImage(viewWidth, viewHeight);
                thisMethodCalled = true;
            }

...これは、フラグメントの置換ごとに onPreDraw が呼び出される回数が増えるのを止めませんが、それについて何かできるかどうかはわかりません。

最終編集 - 最良の解決策:

この回答のコメントの1つから収集された概念を使用しimageView.getViewTreeObserver().removeOnPreDrawListener(this);て、リスナーなしで、メソッドが複数回呼び出されることなく、最後の近くでリスナーを削除するだけです。明らかに追加の onPreDraw() 呼び出しはありません。最後に、意図したとおりに動作します。

    ViewTreeObserver vto = imageView.getViewTreeObserver();
    vto.addOnPreDrawListener(new OnPreDrawListener() {

        @Override
        public boolean onPreDraw() {
            // TESTING
            viewWidth = imageView.getMeasuredWidth();
            viewHeight = imageView.getMeasuredHeight();

            loadImage(viewWidth, viewHeight);

            imageView.getViewTreeObserver().removeOnPreDrawListener(this);
            return true;
        }
    });
4

1 に答える 1

1

イメージ ビュー タグを false または true に設定してみてください。すでに描画されていることを意味します。うまくいくかもしれませんが、アプリケーションでどのように機能するかはわかりません。

次に、次のようなものを持つことができます

imageView = (ImageView)view.findViewById(R.id.gallery_image);
// we have not loaded the image yet
imageView.setTag(false);

その後、プレドローで

boolean hasLoaded = ((Boolean) imageView.getTag());
// if we have not loaded the image yet
// we want to load it
if(!hasLoaded){
            loadImage(viewWidth, viewHeight);                   
}

その後、ポスト実行で

    if (bitmap != null) {
        Log.d(TAG, SCOPE +"bitmap kilobyte count: "+ bitmap.getByteCount() / 1024);
        // we succesfully loaded bitmap
        imageView.setTag(true);
        imageView.setImageBitmap(bitmap);

        // make BitmapWorkerTask reference null again.
        bmwt = null;
    }

編集:読みやすくするためにいくつかのコードを修正しました

于 2013-06-13T05:52:00.787 に答える