Я пишу приложение галереи. Он работает с шаблоном androidstudio для фрагмента списка с помощью AbsList.ListView перерабатывает растровое изображение без уведомления в lrucache
Я переопределяю getView для использования задачи и lrucache для кэширования некоторых растровых изображений.
Каждый вид из списка представляет собой RelativeLayout с ImageView над TextView. Если в кеше нет растрового изображения, тогда AsyncTask загружает его и помещает в кеш, а getView рисует ресурс в ImageView. После того, как он загружен, onPostExecute помещает растровое изображение в ImageView.
Если есть соответствующее изображение на кэше, он устанавливается в ImageView
Я установил объект, содержащий TextView и ImageView вместе с идентификатором в параметре тега convertView в GetView, чтобы следить из правильный битмап для рисования.
У меня есть эти две проблемы, хотя:
Когда я прокручиваю вниз в первый раз, новые виды изображения появляются с предыдущим растрового изображения на мгновение до того, как задача завершает создание правильного растрового изображения (даже хотя я рисую битмап ресурсов на 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;
}
Класс крышки имеет этот метод paintCover, где срединно является URI/потока к изображению
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;
}
}
более подробно. В 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();
}
На данный момент, нет обложки определена, галереи список просто загружаются с названиями и общим содержанием и данными идентификаторов. Изображения (обложки) загружаются в 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;
}
}
Если paintCover возвращает false, это означает, что он нарисовал шаблон в ImageView, а поток запускает создание Bitmap и помещает его в кеш и нарисовывает его на ImageView позднее. Если он возвращает true, это означает, что изображение было найдено в кеше и помещено в ImageView, поэтому ничего не делается. Однако, пытаясь выяснить, я увидел, что изображение в кеше было переработано, но я не знаю, кто это сделал или как это произошло. – dabicho
Что вы хотите сделать, если paintCover верно? –
Спасибо, я только что узнал, что я сделал неправильно ... Если идентификатор отличается, я ошибочно перерабатываю растровое изображение, потому что я уже помещал его в кеш. Виноват. Но каким-то образом просмотр/обсуждение кода здесь дал мне понять. Если значение paintCover истинно, это означает, что обложка была нарисована финальным битовым рисунком (как было обнаружено в кеше), поэтому на данный момент ничего не происходит. – dabicho