Я работаю с отложенной загрузкой Gallery
изображений (один класс для Gallery
и Adapter
, а другой - для отложенной загрузки).Второй класс использует runOnUiThread
для обновления первого класса, используя его контекст, но кажется, что он снова вызывает метод getView()
адаптера первого класса.Это означает, что getView()
вызывается дважды для каждого изображения в Галерее.Посмотрите код ниже.
Странно то, что второй вызов getView()
вызывается только для выбранного изображения в виджете Gallery
.Если четыре изображения показываются одновременно, getView()
будет вызываться на этих четырех изображениях один раз и еще три раза на выбранном изображении.
Есть идеи, почему это происходит?
Вот адаптер
public class ImageAdapter extends BaseAdapter {
public HorizontalImageLoader horImageLoader;
public ImageAdapter() {
horImageLoader = new HorizontalImageLoader(Main.this);
}
public int getCount() {
return coverFileNames.size();
}
public Object getItem(int position) {
return position;
}
public long getItemId(int position) {
return position;
}
public View getView(int position, View convertView, ViewGroup parent) {
ImageView imageView;
if (convertView == null) {
imageView = new ImageView(Main.this);
} else {
imageView = (ImageView) convertView;
}
// If I just use this, getView() is only called once per image (the correct way):
// imageView.setImageResource(R.drawable.noposterxl);
// If I just use this, getView() is only called once per
// image, and additional times on the selected image (not correct):
horImageLoader.DisplayImage(coverFileNames.get(position), imageView, position);
return imageView;
}
}
Вот класс HorizontalImageLoader ( на основе этого примера )
public class HorizontalImageLoader {
private Activity activity;
private Map<ImageView, String> imageViews=Collections.synchronizedMap(new WeakHashMap<ImageView, String>());
public HorizontalImageLoader(Activity activity) {
this.activity = activity;
photoLoaderThread.setPriority(Thread.NORM_PRIORITY-1);
}
public void DisplayImage(String fileUrl, ImageView imageView, final int pos) {
imageViews.put(imageView, fileUrl);
queuePhoto(fileUrl, activity, imageView, pos);
imageView.setImageResource(R.drawable.noposterxl);
}
private void queuePhoto(String url, Activity activity, ImageView imageView, int position) {
//This ImageView may be used for other images before. So there may be some old tasks in the queue. We need to discard them.
photosQueue.Clean(imageView);
PhotoToLoad p=new PhotoToLoad(url, imageView, position);
synchronized(photosQueue.photosToLoad){
photosQueue.photosToLoad.push(p);
photosQueue.photosToLoad.notifyAll();
}
//start thread if it's not started yet
if(photoLoaderThread.getState()==Thread.State.NEW)
photoLoaderThread.start();
}
private Bitmap getBitmap(String fileUrl, int position) {
BitmapFactory.Options options = new BitmapFactory.Options();
options.inPurgeable = true;
Bitmap bm = BitmapFactory.decodeFile(fileUrl, options);
return bm;
}
//Task for the queue
private class PhotoToLoad
{
public String url;
public ImageView imageView;
public int pos;
public PhotoToLoad(String u, ImageView i, int p){
url=u;
imageView=i;
pos = p;
}
}
PhotosQueue photosQueue=new PhotosQueue();
public void stopThread()
{
photoLoaderThread.interrupt();
}
//stores list of photos to download
class PhotosQueue
{
private Stack<PhotoToLoad> photosToLoad=new Stack<PhotoToLoad>();
//removes all instances of this ImageView
public void Clean(ImageView image)
{
try {
for(int j=0 ;j<photosToLoad.size();){
if(photosToLoad.get(j).imageView==image)
photosToLoad.remove(j);
else
++j;
}
} catch (Exception e) {
// Do nothing
}
}
}
class PhotosLoader extends Thread {
public void run() {
try {
while(true)
{
//thread waits until there are any images to load in the queue
if(photosQueue.photosToLoad.size()==0)
synchronized(photosQueue.photosToLoad){
photosQueue.photosToLoad.wait();
}
if(photosQueue.photosToLoad.size()!=0)
{
PhotoToLoad photoToLoad;
synchronized(photosQueue.photosToLoad){
photoToLoad=photosQueue.photosToLoad.pop();
}
Bitmap bmp = getBitmap(photoToLoad.url, photoToLoad.pos);
String tag=imageViews.get(photoToLoad.imageView);
if(tag!=null && tag.equals(photoToLoad.url)){
BitmapDisplayer bd=new BitmapDisplayer(bmp, photoToLoad.imageView);
Activity a=(Activity)photoToLoad.imageView.getContext();
a.runOnUiThread(bd); // This seems to be causing the additional calls to getView()
}
}
if(Thread.interrupted())
break;
}
} catch (InterruptedException e) {
//allow thread to exit
}
}
}
PhotosLoader photoLoaderThread=new PhotosLoader();
//Used to display bitmap in the UI thread
class BitmapDisplayer implements Runnable
{
Bitmap bitmap;
ImageView imageView;
public BitmapDisplayer(Bitmap b, ImageView i){
bitmap=b;
imageView=i;
}
public void run()
{
if(bitmap!=null)
imageView.setImageBitmap(bitmap);
else
imageView.setImageResource(R.drawable.noposterxl);
}
}
}
ОБНОВЛЕНО СНОВА
Если я делаю следующее в моем getView()
методе, LogCat говорит следующее:
Код:
Log.d("POSITION", "Current position: " + position);
horImageLoader.DisplayImage(coverFileNames.get(position), imageView, position);
LogCat:
09-03 13:59:11.920: DEBUG/POSITION(15278): Current position: 1
09-03 13:59:11.960: DEBUG/POSITION(15278): Current position: 2
09-03 13:59:11.960: DEBUG/POSITION(15278): Current position: 3
09-03 13:59:11.960: DEBUG/POSITION(15278): Current position: 0
09-03 13:59:12.110: DEBUG/POSITION(15278): Current position: 1
09-03 13:59:12.240: DEBUG/POSITION(15278): Current position: 1
09-03 13:59:12.300: DEBUG/POSITION(15278): Current position: 1
(обратите внимание на дополнительные три записи журнала с «Текущей позицией: 1» внизу)
Если я сделаю это, то вот что LogCat говорит:
Код:
Log.d("POSITION", "Current position: " + position);
imageView.setImageResource(R.drawable.noposterxl);
LogCat:
09-03 14:02:47.340: DEBUG/POSITION(15412): Current position: 1
09-03 14:02:47.360: DEBUG/POSITION(15412): Current position: 2
09-03 14:02:47.370: DEBUG/POSITION(15412): Current position: 3
09-03 14:02:47.370: DEBUG/POSITION(15412): Current position: 0
(обратите внимание, что это возвращает правильный результат - только один вызов на изображение)
PS.Мой Gallery
настроен на выбор индекса 1 первым, поэтому позиция 1 вызывается первой.