多くのチュートリアルやAndroid開発者のページでも説明されているように、私は非同期タスクを使用して、画像をサムネイルとしてListViewにロードしています。このタスクは、SDカードからフルサイズの画像を読み込み、サイズを変更して、リストアイテムのレイアウトのImageViewに配置します。
リストを上下に高速にスクロールした後、単一の表示要素の画像が、正しい画像を取得する前に異なる画像で2〜3回更新されるという事実を除いて、すべてがうまく機能します。
私の意見では、この動作はListViewのビューのリサイクルに関連しています。非同期タスクが参照されたビューにリストのelement-Xイメージを挿入する準備ができている場合、ビュー自体はすでにリサイクルされ、リストのelement-Yに割り当てられている可能性があります。
コードの醜さを意識しています。たとえば、サムネイル(次のリリースを対象)に揮発性キャッシュも永続キャッシュも実装していないという事実ですが、問題はそれによって部分的にしか隠されていません。
画像をロードするためにライブラリを使用して可能な解決策を見つけましたが、問題はより一般的にリストと組み合わせて非同期コードを使用することに関連しているため、コードを修正する方法を調査しています。今日は画像を扱いますが、明日は直面する可能性がありますテキストやその他の種類のデータの読み込みで同じ問題が発生します。
私が調査している可能な解決策は次のとおりです。
これらの解決策の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]
コメントを追加