Androidは、表示されなくなったときに非同期読み込みリスト要素を停止します

パオローネ

多くのチュートリアルやAndroid開発者のページでも説明されているように、私は非同期タスクを使用して、画像をサムネイルとしてListViewにロードしています。このタスクは、SDカードからフルサイズの画像を読み込み、サイズを変更して、リストアイテムのレイアウトのImageViewに配置します。

リストを上下に高速にスクロールした後、単一の表示要素の画像が、正しい画像を取得する前に異なる画像で2〜3回更新されるという事実を除いて、すべてがうまく機能します。

私の意見では、この動作はListViewのビューリサイクルに関連しています。非同期タスクが参照されたビューにリストのelement-Xイメージを挿入する準備ができている場合、ビュー自体はすでにリサイクルされ、リストのelement-Yに割り当てられている可能性があります。

コードの醜さ意識しています。たとえば、サムネイル(次のリリースを対象)に揮発性キャッシュも永続キャッシュも実装していないという事実ですが、問題はそれによって部分的にしか隠されていません。

画像をロードするためにライブラリを使用し可能な解決策を見つけましたが、問題はより一般的にリストと組み合わせて非同期コードを使用することに関連しているため、コードを修正する方法を調査しています。今日は画像を扱いますが、明日は直面する可能性がありますテキストやその他の種類のデータの読み込みで同じ問題が発生します。

私が調査している可能な解決策は次のとおりです。

  1. 作業中のリストのアイテムについて非同期タスクに通知します。ロードされた画像は、アイテムが表示されている場合にのみ更新されます。
  2. リストがビューを要素から切り離すとき(これを検出するにはどうすればよいですか?)、asynctaskを停止します
  3. リストのOnScrollListenerをオーバーライドして、アイテムが表示されているアイテムのリストから出た場合にOnScrollイベントが発生するタイミングを確認し、存在する場合はその非同期タスクを停止します。

これらの解決策の1つは実行可能ですか、それとも別の解決策を提案しますか?

これは私のリストのアダプターです(フラグメントで展開可能なリストを使用しています):

@Override
public View getChildView(int groupPosition, final int childPosition, boolean isLastChild, View convertView, ViewGroup parent) {

    Log.i(TAG, "ExpandableListAdapter.getChildView entered, getting view n. " + groupPosition + "-" + childPosition + ", convertview = " + convertView);

    ViewHolder holder;

    if (convertView == null) {

        convertView = inf.inflate(R.layout.selfie_list_item_layout, parent, false);
        holder = new ViewHolder();

        holder.date = (TextView) convertView.findViewById(R.id.selfieListItemDateView);
        holder.place = (TextView) convertView.findViewById(R.id.selfieListItemPlaceView);
        holder.thumb = (ImageView) convertView.findViewById(R.id.selfieListItemThumbView);
        convertView.setTag(holder);
    } else {
        holder = (ViewHolder) convertView.getTag();
    }

    Integer mChildIndex = (Integer) getChild(groupPosition, childPosition);
    SelfieItem mChildObj = selfies.get(mChildIndex);
    String mText = mChildObj.getDate().toString();
    holder.date.setText(mText);
    holder.thumb.setImageBitmap(BitmapFactory.decodeResource(convertView.getResources(), R.drawable.selfie_place_holder));


    File selfieFile = mChildObj.getFile();

    new LoadSelfieTask(mFragmentContext).executeOnExecutor(AsyncTask.SERIAL_EXECUTOR, selfieFile, holder.thumb);

    return convertView;
}

そして、以下は非同期コードです:

   @Override
    protected Bitmap doInBackground(Object... params) {

        File selfieFile = (File)params[0];
        Bitmap mySrcBitmap = null;
        Bitmap myDestBitmap = null;

        if (selfieFile.exists()) {

            mySrcBitmap = BitmapFactory.decodeFile(selfieFile.getAbsolutePath());

        }

        if (mySrcBitmap != null) {

            // Get info about view to be updated
            mImageViewToBeUpdated = (ImageView) params[1];
            mImageHeight = mImageViewToBeUpdated.getHeight();
            mImageWidth = mImageViewToBeUpdated.getWidth();

            if (mySrcBitmap.getWidth() >= mySrcBitmap.getHeight()){

                myDestBitmap = Bitmap.createBitmap(
                        mySrcBitmap,
                        mySrcBitmap.getWidth()/2 - mySrcBitmap.getHeight()/2,
                        0,
                        mySrcBitmap.getHeight(),
                        mySrcBitmap.getHeight()
                );

            }else{

                myDestBitmap = Bitmap.createBitmap(
                        mySrcBitmap,
                        0,
                        mySrcBitmap.getHeight()/2 - mySrcBitmap.getWidth()/2,
                        mySrcBitmap.getWidth(),
                        mySrcBitmap.getWidth()
                );
            }

            mySrcBitmap = Bitmap.createScaledBitmap(myDestBitmap, mImageWidth, mImageHeight, true);

        }

        return mySrcBitmap;

    }

よろしくお願いします。

パオローネ

このAndroid開発者トレーニングレッスンのサンプルコードで、私の質問に対する答えを見つけました

ImageWorker.javaで、バックグラウンドタスクを起動するメソッドを見つけることができます。thadは画像をロードします。

/**
 * Load an image specified by the data parameter into an ImageView (override
 * {@link ImageWorker#processBitmap(Object)} to define the processing logic). A memory and
 * disk cache will be used if an {@link ImageCache} has been added using
 * {@link ImageWorker#addImageCache(android.support.v4.app.FragmentManager, ImageCache.ImageCacheParams)}. If the
 * image is found in the memory cache, it is set immediately, otherwise an {@link AsyncTask}
 * will be created to asynchronously load the bitmap.
 *
 * @param data The URL of the image to download.
 * @param imageView The ImageView to bind the downloaded image to.
 */
public void loadImage(Object data, ImageView imageView) {
    if (data == null) {
        return;
    }

    BitmapDrawable value = null;

    if (mImageCache != null) {
        value = mImageCache.getBitmapFromMemCache(String.valueOf(data));
    }

    if (value != null) {
        // Bitmap found in memory cache
        imageView.setImageDrawable(value);
    } else if (cancelPotentialWork(data, imageView)) {
        //BEGIN_INCLUDE(execute_background_task)
        final BitmapWorkerTask task = new BitmapWorkerTask(data, imageView);
        final AsyncDrawable asyncDrawable =
                new AsyncDrawable(mResources, mLoadingBitmap, task);
        imageView.setImageDrawable(asyncDrawable);

        // NOTE: This uses a custom version of AsyncTask that has been pulled from the
        // framework and slightly modified. Refer to the docs at the top of the class
        // for more info on what was changed.
        task.executeOnExecutor(AsyncTask.DUAL_THREAD_EXECUTOR);
        //END_INCLUDE(execute_background_task)
    }
}

AsyncTaskインスタンスへの参照は、AsyncDrawableクラスに保存されます。

/**
 * A custom Drawable that will be attached to the imageView while the work is in progress.
 * Contains a reference to the actual worker task, so that it can be stopped if a new binding is
 * required, and makes sure that only the last started worker process can bind its result,
 * independently of the finish order.
 */
private static class AsyncDrawable extends BitmapDrawable {
    private final WeakReference<BitmapWorkerTask> bitmapWorkerTaskReference;

    public AsyncDrawable(Resources res, Bitmap bitmap, BitmapWorkerTask bitmapWorkerTask) {
        super(res, bitmap);
        bitmapWorkerTaskReference =
            new WeakReference<BitmapWorkerTask>(bitmapWorkerTask);
    }

    public BitmapWorkerTask getBitmapWorkerTask() {
        return bitmapWorkerTaskReference.get();
    }
}

バックグラウンドアクティビティの最後に、AsyncTaskは、更新する必要のあるビューに最後に「アタッチ」されているかどうかを確認し、他のタスクがビューに「アタッチ」されていない場合にのみ更新を実行します。

    /**
     * Returns the ImageView associated with this task as long as the ImageView's task still
     * points to this task as well. Returns null otherwise.
     */
    private ImageView getAttachedImageView() {
        final ImageView imageView = imageViewReference.get();
        final BitmapWorkerTask bitmapWorkerTask = getBitmapWorkerTask(imageView);

        if (this == bitmapWorkerTask) {
            return imageView;
        }

        return null;
    }


   /**
     * Once the image is processed, associates it to the imageView
     */
    @Override
    protected void onPostExecute(BitmapDrawable value) {
        //BEGIN_INCLUDE(complete_background_work)
        // if cancel was called on this task or the "exit early" flag is set then we're done
        if (isCancelled() || mExitTasksEarly) {
            value = null;
        }

        final ImageView imageView = getAttachedImageView();
        if (value != null && imageView != null) {
            if (BuildConfig.DEBUG) {
                Log.d(TAG, "onPostExecute - setting bitmap");
            }
            setImageDrawable(imageView, value);
        }
        //END_INCLUDE(complete_background_work)
    }

この記事はインターネットから収集されたものであり、転載の際にはソースを示してください。

侵害の場合は、連絡してください[email protected]

編集
0

コメントを追加

0

関連記事

分類Dev

Androidは、表示されなくなったときに非同期読み込みリスト要素を停止します

分類Dev

ページの読み込みが速くなったときに読み込み画面をテストするにはどうすればよいですか?

分類Dev

ProgressDialogによって作成された読み込みが停止しない、ページが読み込まれたときにプログレスバーを非表示にする方法

分類Dev

ドキュメントの書き込みを実行:非同期で読み込まれた外部スクリプトから明示的に開かない限り、ドキュメントに書き込むことはできません。

分類Dev

Angular 4は読み込みと非同期で、空のときに表示されます

分類Dev

Javascript:ページの読み込みが完了し、スクリプトが非同期を読み込んでいるときに関数を実行します

分類Dev

x:バインドデータが非同期に表示されない/ UWPアプリで読み込まれたデータセットを待つ

分類Dev

p:fileDownloadは、例外がスローされたときにクエリ文字列なしでページを再読み込みします

分類Dev

LocalStorage変数が存在しますか:要素の再読み込み後にステートメントが機能しない場合(再読み込みされたフォームを送信した後にページが再読み込みされます)

分類Dev

スクリプトDOM要素が非同期読み込みになる理由

分類Dev

変更時だけでなく、ページの読み込み時にもng-repeatにネストされた入力要素を検証します

分類Dev

グリースモンキースクリプトで要素が表示された直後(ページが完全に読み込まれた後ではない)に要素を変更しますか?

分類Dev

jQueryは、特定の要素が読み込まれたときにイベントをトリガーします

分類Dev

リダイレクトされたstdoutへの非同期書き込みは同期または非同期ですか?

分類Dev

新しいページが読み込まれたときにChrome.storage.syncストレージがリセットされるのはなぜですか?

分類Dev

非同期グリッドビューで画像をクリックして、コンテキストアクションメニューにアクセスするときにアクティビティが再読み込みされないようにします

分類Dev

非同期グリッドビューで画像をクリックして、コンテキストアクションメニューにアクセスするときにアクティビティが再読み込みされないようにします

分類Dev

スナップアプリを開くと、「共有ライブラリの読み込み中にエラーが発生しました」および「共有オブジェクトファイルを開くことができません:そのようなファイルまたはディレクトリはありません」と表示されます

分類Dev

エラー:非同期で読み込まれた外部スクリプトからドキュメントに書き込むことはできません

分類Dev

フォームの送信時にページが再読み込みされないようにするにはどうすればよいですか。また、「正常に登録されました」という小さなテキストを表示することもできます。

分類Dev

リストビューにカーソル値が読み込まれない、または表示されない

分類Dev

Xpathによって検出されたテーブル要素のリストには、getText()メソッドを介して受信できる正しいテキスト値を持つ表示可能な10個の要素のみが含まれています。

分類Dev

ページが初めて読み込まれ、その後は読み込まれないときにイベントをトリガーします

分類Dev

非同期のJavaスクリプトの読み込みでエラーが表示されます

分類Dev

Android ListViewは、Firebaseからインポートされた画像要素を常に再読み込みします

分類Dev

'r +'で、1行を読み取った*後に*テキストファイルを書き込むと、 `f.tell()`の位置ではなく、最後に書き込まれるのはなぜですか?

分類Dev

ネストされたELを使用して番号付きヘッダー値を動的に読み取ることはできなくなりました

分類Dev

node.js:接続された書き込み可能と読み取り可能なストリームペアを作成します

分類Dev

非同期ストレージを使用していないときに「非同期ストレージが抽出されました」という警告が表示される

Related 関連記事

  1. 1

    Androidは、表示されなくなったときに非同期読み込みリスト要素を停止します

  2. 2

    ページの読み込みが速くなったときに読み込み画面をテストするにはどうすればよいですか?

  3. 3

    ProgressDialogによって作成された読み込みが停止しない、ページが読み込まれたときにプログレスバーを非表示にする方法

  4. 4

    ドキュメントの書き込みを実行:非同期で読み込まれた外部スクリプトから明示的に開かない限り、ドキュメントに書き込むことはできません。

  5. 5

    Angular 4は読み込みと非同期で、空のときに表示されます

  6. 6

    Javascript:ページの読み込みが完了し、スクリプトが非同期を読み込んでいるときに関数を実行します

  7. 7

    x:バインドデータが非同期に表示されない/ UWPアプリで読み込まれたデータセットを待つ

  8. 8

    p:fileDownloadは、例外がスローされたときにクエリ文字列なしでページを再読み込みします

  9. 9

    LocalStorage変数が存在しますか:要素の再読み込み後にステートメントが機能しない場合(再読み込みされたフォームを送信した後にページが再読み込みされます)

  10. 10

    スクリプトDOM要素が非同期読み込みになる理由

  11. 11

    変更時だけでなく、ページの読み込み時にもng-repeatにネストされた入力要素を検証します

  12. 12

    グリースモンキースクリプトで要素が表示された直後(ページが完全に読み込まれた後ではない)に要素を変更しますか?

  13. 13

    jQueryは、特定の要素が読み込まれたときにイベントをトリガーします

  14. 14

    リダイレクトされたstdoutへの非同期書き込みは同期または非同期ですか?

  15. 15

    新しいページが読み込まれたときにChrome.storage.syncストレージがリセットされるのはなぜですか?

  16. 16

    非同期グリッドビューで画像をクリックして、コンテキストアクションメニューにアクセスするときにアクティビティが再読み込みされないようにします

  17. 17

    非同期グリッドビューで画像をクリックして、コンテキストアクションメニューにアクセスするときにアクティビティが再読み込みされないようにします

  18. 18

    スナップアプリを開くと、「共有ライブラリの読み込み中にエラーが発生しました」および「共有オブジェクトファイルを開くことができません:そのようなファイルまたはディレクトリはありません」と表示されます

  19. 19

    エラー:非同期で読み込まれた外部スクリプトからドキュメントに書き込むことはできません

  20. 20

    フォームの送信時にページが再読み込みされないようにするにはどうすればよいですか。また、「正常に登録されました」という小さなテキストを表示することもできます。

  21. 21

    リストビューにカーソル値が読み込まれない、または表示されない

  22. 22

    Xpathによって検出されたテーブル要素のリストには、getText()メソッドを介して受信できる正しいテキスト値を持つ表示可能な10個の要素のみが含まれています。

  23. 23

    ページが初めて読み込まれ、その後は読み込まれないときにイベントをトリガーします

  24. 24

    非同期のJavaスクリプトの読み込みでエラーが表示されます

  25. 25

    Android ListViewは、Firebaseからインポートされた画像要素を常に再読み込みします

  26. 26

    'r +'で、1行を読み取った*後に*テキストファイルを書き込むと、 `f.tell()`の位置ではなく、最後に書き込まれるのはなぜですか?

  27. 27

    ネストされたELを使用して番号付きヘッダー値を動的に読み取ることはできなくなりました

  28. 28

    node.js:接続された書き込み可能と読み取り可能なストリームペアを作成します

  29. 29

    非同期ストレージを使用していないときに「非同期ストレージが抽出されました」という警告が表示される

ホットタグ

アーカイブ