2013-02-23 4 views
1

В моем приложении есть ListView, который использует пользовательские данные CursorAdapter для загрузки данных, которые в основном являются ImageView и TextView. Я загружаю изображения в AsyncTask, теперь проблема в том, что первоначально все изображения назначаются для исправления текста, но когда я быстро прокручиваю вверх и вниз, он связывает случайное изображение для каждого текста. Поскольку я использую CursorAdapter, я не могу использовать здесь ViewHolder, так что я должен сделать, чтобы устранить эту проблему?Проблемы с многопотоковой загрузкой при загрузке изображения

Вот мой пример кода:

public class MyAdapter extends CursorAdapter { 

    private LayoutInflater mLayoutInflater; 
    @SuppressWarnings("unused") 
    private Context mContext; 

    public MyAdapter(Context context, Cursor c, int flags) { 
     super(context, c, flags); 
     mContext = context; 
     mLayoutInflater = LayoutInflater.from(context); 
    } 
    public void bindView(View view, Context context, Cursor cursor) { 
     String title = cursor.getString(cursor.getColumnIndex(MediaStore.MediaColumns.TITLE)); 
     String album_id = cursor.getString(cursor.getColumnIndex(MediaStore.Audio.Media.ALBUM_ID)); 
     TextView text = (TextView)view.findViewById(R.id.txtTitle); 
     text.setText(title); 
     Uri sArtworkUri = Uri.parse("content://media/external/audio/albumart"); 
     Uri uri = ContentUris.withAppendedId(sArtworkUri, Integer.valueOf(album_id)); 
     new MyImageLoader(context,view).execute(uri); 

    } 
    @Override 
    public View newView(Context context, Cursor cursor, ViewGroup parent) { 
     return mLayoutInflater.inflate(R.layout.row, parent, false); 
    } 
    private class MyImageLoader extends AsyncTask<Uri, Void, Bitmap>{ 
     Context context; 
     View view; 


     MyImageLoader(Context context,View view){ 
      this.context = context; 
      this.view = view; 
     } 
     @Override 
     protected Bitmap doInBackground(Uri... uri) { 
      ContentResolver res = context.getContentResolver(); 
      InputStream in = null; 
      try { 
       in = res.openInputStream(uri[0]); 
      } 
      catch (FileNotFoundException e) { 
       // TODO Auto-generated catch block 
       e.printStackTrace(); 
      } 
      Bitmap artwork = BitmapFactory.decodeStream(in); 
      return artwork; 
     } 
     protected void onPostExecute(Bitmap bmp){ 
      ImageView iv = (ImageView)view.findViewById(R.id.imgIcon); 
      if(bmp!=null) 
       //iv.setImageBitmap(bmp); 
       iv.setImageBitmap(Bitmap.createScaledBitmap(bmp, 100, 100, false)); 
     } 
    } 
} 

ОБНОВЛЕНО: я применил метод SetTag, теперь перетасовки меньше, однако, когда я прокручиваю быстро старое изображение сохраняется в течение секунды, пока правильное изображение не будет загружен. Вот новый код:

public void bindView(View view, Context context, Cursor cursor) { 
     String title = cursor.getString(cursor.getColumnIndex(MediaStore.MediaColumns.TITLE)); 
     String album_id = cursor.getString(cursor.getColumnIndex(MediaStore.Audio.Media.ALBUM_ID)); 
     ImageView iv = (ImageView)view.findViewById(R.id.imgIcon); 
     TextView text = (TextView)view.findViewById(R.id.txtTitle); 
     text.setText(title); 
     Uri sArtworkUri = Uri.parse("content://media/external/audio/albumart"); 
     Uri uri = ContentUris.withAppendedId(sArtworkUri, Integer.valueOf(album_id)); 
// *****TAG SET***** 
     iv.setTag(uri); 
//***PASSING BOTH URI AND IMAGEVIEW TO CONSTRUCTOR*** 
     new MyImageLoader(context,view,iv,uri).execute(uri); 

    } 
    @Override 
    public View newView(Context context, Cursor cursor, ViewGroup parent) { 
     View v = mLayoutInflater.inflate(R.layout.row, parent, false); 
     //v.setTag(R.id.imgIcon, v.findViewById(R.id.imgIcon)); 
     return v; 
    } 
    private class MyImageLoader extends AsyncTask<Object, Void, Bitmap>{ 
     Context context; 
     View v; 
     ImageView iv; 
     Uri u; 

     MyImageLoader(Context context,View v,ImageView iv,Uri u){ 
      this.context = context; 
      this.v = v; 
      this.iv = iv; 
      this.u = u; 
     } 
     @Override 

     protected synchronized Bitmap doInBackground(Object... param) { 
      ContentResolver res = context.getContentResolver(); 
      InputStream in = null; 
      try { 
       Uri uri= (Uri)param[0]; 
       in = res.openInputStream(uri); 
      } 
      catch (FileNotFoundException e) { 
       // TODO Auto-generated catch block 
       e.printStackTrace(); 
      } 
      Bitmap artwork = BitmapFactory.decodeStream(in); 

      return artwork; 
     } 
     protected void onPostExecute(Bitmap bmp){ 

      if(bmp!=null) 
      { ImageView iv = (ImageView)v.findViewById(R.id.imgIcon); 
       if(iv.getTag().toString().equals(u.toString())) 
        iv.setImageBitmap(Bitmap.createScaledBitmap(bmp, 100, 100, false)); 
      } 
     } 
    } 

UPDATE: Установки шаблонного изображения перед вызовом фоновой задачи делает это намного лучше.

ответ

2

Одним из решений, которое я использовал, является подключение Uri к ImageView через setTag(). Затем, когда вы переходите к обновлению ImageView, проверьте, соответствует ли значение Uri изображения, которое вы собираетесь применить, от getTag(). Если да, обновите ImageView. Если это не так, то ImageView был переработан, и вы можете пропустить обновление.

Другой подход заключается в use setHasTransientState(true) на ImageView (или, возможно, в строке - не пробовал это), когда вы решили, что у мисс кэш и нужно пнуть в AsyncTask. Это вызовет AdapterView, чтобы избежать повторного использования этой строки до тех пор, пока не будет получен соответствующий вызов setHasTransientState(false). Chet Haase имеет a DevBytes video on this, в контексте применения анимации к строке ListView и пытается избежать проблем с переработкой. Однако setHasTransientState() является новичком в API Level 16, поэтому, если вы пытаетесь поддерживать Android 4.0 или более старые устройства, это не будет доступным вариантом.

+0

Я попробую 1-й метод и вернусь. Не могу попробовать второй, когда я нацеливаю API 15. – h4ck3d

+0

Я не могу понять, где добавить тег, должен ли я создать объект ImageView в 'bindView()' и перед вызовом 'MyImageLoader' установить там свой тег и передать его как аргумент конструктору или выполнить метод? – h4ck3d

+1

@Droider: «Я должен создать объект ImageView в bindView()» - у вас уже есть «ImageView». Вызовите 'findViewById()' для его получения. – CommonsWare