我正在写一个画廊应用程序。 它适用于列表片段的androidstudio模板,带有AbsList。
我重写getView以使用任务和lrucache来缓存一些位图。
listview中的每个视图都是一个RelativeLayout,在TextView上方有一个ImageView。 如果缓存中没有Bitmap,则AsyncTask会加载它并将其放入缓存中,getView会在ImageView上绘制资源。 加载后,onPostExecute将位图放入ImageView。
如果缓存上有相应的Bitmap,则将其设置为ImageView
我将一个持有TextView和ImageView的对象以及一个id放入getView的convertView参数标记中,以跟踪要绘制的正确位图。
但我有这两个问题:
当我第一次向下滚动时,在任务完成设置正确的位图之前,新的图像视图会显示前一个位图(即使我在适配器的getView上绘制了资源位图)不明白为什么。
当我向后滚动时,大多数时候应用程序崩溃,因为缓存上的位图最终会被回收,但我不知道是谁回收它。
任何人都可以帮我理解这里发生的事情吗?
public View getView(int position, View convertView, ViewGroup parent) {
Log.i(TAG, "getView: Asking for view " + position);
GalleryItemViewHolder lViewHolder;
if (convertView == null) {
convertView = getActivity().getLayoutInflater().inflate(R.layout
.gallery_item, null);
lViewHolder = new GalleryItemViewHolder();
convertView.setTag(lViewHolder);
} else {
lViewHolder = (GalleryItemViewHolder) convertView.getTag();
}
lViewHolder.setId(position);
lViewHolder.setTextView((TextView) convertView.findViewById(R.id.gallery_infoTextView));
lViewHolder.setImageView((ImageView) convertView.findViewById(R.id.gallery_imageView));
lViewHolder.getTextView().setText(getItem(position).getName() + ": (" + getItem
(position).getCount() + ")");
if (!getItem(position).paintCover(lViewHolder.getImageView())) {
Log.i(TAG,"getView: task");
new GalleryItemTask(position, lViewHolder)
.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, null);
}
Log.i(TAG,"getView: return");
return convertView;
}
Cover类有这个paintCover方法,其中mId是图像的uri / stream
public boolean paintCover(ImageView imageView) {
Bitmap lBitmap;
if (mId == null || (lBitmap = BitmapCacheManager.getInstance().get(mId)) == null) {
i(TAG, "paintCover: Sin Cache ");
imageView.getHeight();
imageView.getWidth();
imageView.setImageResource(android.R.drawable.alert_dark_frame);
return false;
} else
{
i(TAG, "paintCover: En Cache "+lBitmap.isRecycled());
imageView.setImageBitmap(lBitmap);
return true;
}
}
更多细节。 在Fragment的onCreate,我运行这个方法:
private void prepareGalleryLoaders() {
LoaderManager lm = getLoaderManager();
Log.i(TAG, "prepareGalleryLoaders: Iniciando loader");
lm.initLoader(IdConstants.GALLERY_LOADER, null, new GalleryLoaderCallbacks());
}
/**
* Callbacks para cargar los datos de las galerías
* Al terminar de cargarlas, se crea el nuevo arreglo
*/
private class GalleryLoaderCallbacks implements LoaderManager.LoaderCallbacks<List<Gallery>> {
@Override
public Loader<List<Gallery>> onCreateLoader(int id, Bundle args) {
return new GalleriesLoader(getActivity());
}private class GalleryItemTask extends AsyncTask<Void, Void, Gallery> {
private static final String TAG = "GalleryItemTask";
private int mId;
private String mCoverId;
private GalleryItemViewHolder mViewHolder;
private Bitmap mBitmap;
GalleryItemTask(int id, GalleryItemViewHolder galleryItemViewHolder) {
mViewHolder = galleryItemViewHolder;
mId = id;
}
@Override
protected void onPostExecute(Gallery galleries) {
if (mId != mViewHolder.getId()) {
Log.i(TAG, "onPostExecute: IDs difieren!!! "+mId+" - "+mViewHolder.getId());
mBitmap.recycle();
mBitmap=null;
return;
}
// Validar y actualizar bitmap
mViewHolder.getImageView().setImageBitmap(mBitmap);
//mGalleries.get(mId).setBitmap(mBitmap);
super.onPostExecute(galleries);
}
@Override
protected Gallery doInBackground(Void... params) {
// generar bitmap (y posiblemente agregarlo a algún cache)
String[] queryProjection = {
MediaStore.Images.ImageColumns.DATA, MediaStore.Images.ImageColumns.TITLE};
String[] selectionArgs = new String[]{String.valueOf(mGalleries.get(mId).getId())};
Cursor lCursor = getView().getContext().getContentResolver().query(
MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
queryProjection, MediaStore.Images.ImageColumns.BUCKET_ID + "= ?",
selectionArgs, MediaStore.Images.ImageColumns.TITLE);
lCursor.moveToFirst();
while (!lCursor.isAfterLast()) {
//Log.i(TAG,"doInBackground: "+mGalleries.get(mId).getName()+" - "+lCursor.getString
// (1)+" - "+ lCursor.getString(0));
lCursor.moveToNext();
}
lCursor.moveToFirst();
Log.i(TAG, "doInBackground: " + mId + " - " + mViewHolder.getId());
BitmapFactory.Options lOptions = new BitmapFactory.Options();
lOptions.inJustDecodeBounds = true;
mBitmap = BitmapFactory.decodeFile(lCursor.getString(0), lOptions);
lOptions.inSampleSize = ImageUtils.calculateInSampleSize(lOptions, 256, 256);
lOptions.inJustDecodeBounds = false;
mBitmap = BitmapFactory.decodeFile(lCursor.getString(0), lOptions);
BitmapCacheManager.getInstance().put(lCursor.getString(0), mBitmap);
//if(mGalleries.get(mId).getBitmap()!=null)
// mGalleries.get(mId).getBitmap().recycle();
//mGalleries.get(mId).setBitmap(mBitmap);
if(!mGalleries.get(mId).hasCover()) {
SimpleCover lSimpleCover=new SimpleCover(getActivity(),lCursor.getString(0));
mGalleries.get(mId).setCover(lSimpleCover);
}
lCursor.close();
return null;
}
}
@Override
public void onLoadFinished(Loader<List<Gallery>> loader, List<Gallery> data) {
if (mGalleries != null) {
mGalleries.clear();
} else
mGalleries = new ArrayList<Gallery>();
mGalleries.addAll(data);
for (Gallery lGallery : data) {
Log.i(TAG, "onLoadFinished: " + lGallery.getName());
}
mAdapter.notifyDataSetChanged();
}
此时,没有定义封面,库列表只是加载了标题和总内容以及id数据。图像(封面)从列表适配器的getView加载。
GalleryItemTask类:
private class GalleryItemTask extends AsyncTask<Void, Void, Gallery> {
private static final String TAG = "GalleryItemTask";
private int mId;
private String mCoverId;
private GalleryItemViewHolder mViewHolder;
private Bitmap mBitmap;
GalleryItemTask(int id, GalleryItemViewHolder galleryItemViewHolder) {
mViewHolder = galleryItemViewHolder;
mId = id;
}
@Override
protected void onPostExecute(Gallery galleries) {
if (mId != mViewHolder.getId()) {
Log.i(TAG, "onPostExecute: IDs difieren!!! "+mId+" - "+mViewHolder.getId());
mBitmap.recycle();
mBitmap=null;
return;
}
// Validar y actualizar bitmap
mViewHolder.getImageView().setImageBitmap(mBitmap);
//mGalleries.get(mId).setBitmap(mBitmap);
super.onPostExecute(galleries);
}
@Override
protected Gallery doInBackground(Void... params) {
// generar bitmap (y posiblemente agregarlo a algún cache)
String[] queryProjection = {
MediaStore.Images.ImageColumns.DATA, MediaStore.Images.ImageColumns.TITLE};
String[] selectionArgs = new String[]{String.valueOf(mGalleries.get(mId).getId())};
Cursor lCursor = getView().getContext().getContentResolver().query(
MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
queryProjection, MediaStore.Images.ImageColumns.BUCKET_ID + "= ?",
selectionArgs, MediaStore.Images.ImageColumns.TITLE);
lCursor.moveToFirst();
while (!lCursor.isAfterLast()) {
//Log.i(TAG,"doInBackground: "+mGalleries.get(mId).getName()+" - "+lCursor.getString
// (1)+" - "+ lCursor.getString(0));
lCursor.moveToNext();
}
lCursor.moveToFirst();
Log.i(TAG, "doInBackground: " + mId + " - " + mViewHolder.getId());
BitmapFactory.Options lOptions = new BitmapFactory.Options();
lOptions.inJustDecodeBounds = true;
mBitmap = BitmapFactory.decodeFile(lCursor.getString(0), lOptions);
lOptions.inSampleSize = ImageUtils.calculateInSampleSize(lOptions, 256, 256);
lOptions.inJustDecodeBounds = false;
mBitmap = BitmapFactory.decodeFile(lCursor.getString(0), lOptions);
BitmapCacheManager.getInstance().put(lCursor.getString(0), mBitmap);
//if(mGalleries.get(mId).getBitmap()!=null)
// mGalleries.get(mId).getBitmap().recycle();
//mGalleries.get(mId).setBitmap(mBitmap);
if(!mGalleries.get(mId).hasCover()) {
SimpleCover lSimpleCover=new SimpleCover(getActivity(),lCursor.getString(0));
mGalleries.get(mId).setCover(lSimpleCover);
}
lCursor.close();
return null;
}
}
答案 0 :(得分:0)
第一次向下滚动时,新的图像视图显示为 在任务完成设置之前的瞬间的前一个位图 正确的位图(即使我在适配器上绘制资源Bitmap) getView)我不明白为什么。
应该是因为你把notifyDataSetChanged()
放在了错误的地方。请将代码发布到您的位置。
当我向后滚动时,大多数时候应用程序崩溃,因为Bitmap打开了 缓存最终被回收,但我不知道谁回收 它
我认为是因为如果paintCover
为true
,您没有说明该怎么做:
if (!getItem(position).paintCover(lViewHolder.getImageView())) {
Log.i(TAG,"getView: task");
new GalleryItemTask(position, lViewHolder)
.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, null);
}
else
{
//what should the adapter do if paintCover is true?
}
如果错误仍然存在,请发布您的GalleryItemTask
代码。