8

Tôi có một bộ điều hợp tùy chỉnh mở rộng ArrayAdapter, nó thực hiện các mẫu khung nhìn để hiển thị dữ liệu (văn bản + hình ảnh) từ một dịch vụ web.Android: hiển thị ListView hình ảnh nhấp nháy khi thêm dữ liệu vào ArrayAdapter

để tải lười biếng trong những hình ảnh tôi sử dụng mô hình nhiệm vụ async từ công tác đào tạo tiên tiến trong trang web của nhà phát triển của Android,

tôi cũng sử dụng đĩa + ram bitmap cache.

khi có dữ liệu bổ sung cần truy xuất, tôi thêm chế độ xem chân trang để nhấp vào nó sẽ truy xuất dữ liệu bổ sung từ dịch vụ web và thêm dữ liệu đó vào bộ điều hợp.

vấn đề là khi dữ liệu mới này được thêm vào, một số hình ảnh hiển thị đang thay đổi và ngay lập tức thay đổi trở lại, dẫn đến nhấp nháy lạ.

ngoài việc mọi thứ hoạt động tốt và việc cuộn trơn tru.

theo như tôi hiểu những thay đổi hình ảnh đó đang xảy ra khi chế độ xem hiển thị đang được làm mới khi dữ liệu mới được thêm vào.

có cách nào bỏ qua hành vi không mong muốn này không?

này là lớp làm tải và quản lý các nhiệm vụ async

public class ImageDownloader { 
private ImageCache mCache; 
private int reqWidth; 
private int reqHeight; 

public void download(String url, ImageView imageView, ImageCache imageCache, int reqHeight, int reqWidth) { 
    mCache = imageCache; 
    this.reqHeight = reqHeight; 
    this.reqWidth = reqWidth; 

    if (cancelPotentialDownload(url, imageView)) { 
     BitmapDownloaderTask task = new BitmapDownloaderTask(imageView); 

     DownloadedDrawable downloadedDrawable = new DownloadedDrawable(task); 
     imageView.setImageDrawable(downloadedDrawable); 
     task.execute(url); 
    } 
} 

private class BitmapDownloaderTask extends AsyncTask<String, Void, Bitmap> { 
    private String url; 
    private WeakReference<ImageView> imageViewReference; 

    public BitmapDownloaderTask(ImageView imageView) { 
     imageViewReference = new WeakReference<ImageView>(imageView); 
    } 

    @Override 
    protected Bitmap doInBackground(String... strings) { 
     Bitmap bitmap = null; 
     try { 
      bitmap = mCache.getBitmapFromURL(strings[0], reqWidth, reqHeight); 
     } catch (IOException e) { 
     } 

     return bitmap; 
    } 

    @Override 
    protected void onPostExecute(Bitmap bitmap) { 
     if (isCancelled()) bitmap = null; 

     if (imageViewReference != null) { 
      ImageView imageView = imageViewReference.get(); 
      BitmapDownloaderTask bitmapDownloaderTask = getBitmapDownloaderTask(imageView); 

      if (imageView != null) { 
       imageView.setImageBitmap(bitmap); 
      } 
     } 
    } 
} 

private static class DownloadedDrawable extends ColorDrawable { 
    private WeakReference<BitmapDownloaderTask> bitmapDownloaderTaskReference; 

    public DownloadedDrawable(BitmapDownloaderTask bitmapDownloaderTask) { 
     bitmapDownloaderTaskReference = new WeakReference<BitmapDownloaderTask>(bitmapDownloaderTask); 
    } 

    public BitmapDownloaderTask getBitmapDownloaderTask() { 
     return bitmapDownloaderTaskReference.get(); 
    } 
} 

private static BitmapDownloaderTask getBitmapDownloaderTask(ImageView imageView) { 
    if (imageView != null) { 
     Drawable drawable = imageView.getDrawable(); 
     if (drawable instanceof DownloadedDrawable) { 
      DownloadedDrawable downloadedDrawable = (DownloadedDrawable)drawable; 
      return downloadedDrawable.getBitmapDownloaderTask(); 
     } 
    } 
    return null; 
} 

private static boolean cancelPotentialDownload(String url, ImageView imageView) { 
    BitmapDownloaderTask bitmapDownloaderTask = getBitmapDownloaderTask(imageView); 

    if (bitmapDownloaderTask != null) { 
     String bitmapUrl = bitmapDownloaderTask.url; 
     if ((bitmapUrl == null) || (!bitmapUrl.equals(url))) { 
      bitmapDownloaderTask.cancel(true); 
     } else { 
      return false; 
     } 
    } 
    return true; 
} 

}

đây là adapter:

lớp tin PlaceAdapter kéo dài ArrayAdapter { thức int viewResourceId;

public PlaceAdapter(Context context, int textViewResourceId, List<PlaceModel> objects) { 
     super(context, textViewResourceId, objects); 
     viewResourceId = textViewResourceId; 
    } 

    @Override 
    public View getView(final int position, View convertView, ViewGroup parent) { 
     ViewHolder holder; 

     if (convertView == null) { 
      LayoutInflater inflater = getLayoutInflater(); 
      convertView = inflater.inflate(viewResourceId, null); 

      holder = new ViewHolder(convertView); 
      convertView.setTag(holder); 
     } else { 
      holder = (ViewHolder) convertView.getTag(); 
     } 

     PlaceModel place = getItem(position); 

     holder.name.setText(place.getName()); 
     holder.address.setText(place.getVicinity()); 
     holder.position = position; 

     if (place.getIcon() != null) { 
      String url = mImageViewUrls.get(holder.image); 
      if (url == null || (url != null && !url.equals(place.getIcon()))) { 
       mDownloader.download(place.getIcon(), holder.image, mCache, 100, 100); 
       mImageViewUrls.put(holder.image, place.getIcon()); 
      } 
     } else { 
      holder.image.setImageBitmap(null); 
      mImageViewUrls.remove(holder.image); 
     } 

     return convertView; 
    } 
} 

private class ViewHolder { 
    public final ImageView image; 
    public final TextView name; 
    public final TextView address; 
    public int position; 

    public ViewHolder(View row) { 
     image = (ImageView) row.findViewById(R.id.placeRow_imageView); 
     name = (TextView) row.findViewById(R.id.placeRow_placeName); 
     address = (TextView) row.findViewById(R.id.placeRow_placeAddress); 
    } 
} 

mImageViewUrls là một WeakHashMap<ImageView, String> mà các bản đồ giữa một ImageView và một url, vì vậy không cần thiết nhiệm vụ async invocations có thể giảm bằng cách kiểm tra nếu ImageView đã cho thấy hình ảnh theo yêu cầu. mà không thực hiện điều này, nhấp nháy đang xảy ra trong tất cả các hình ảnh hiển thị về thay đổi dữ liệu. với điều này, nó chỉ xảy ra với một số hình ảnh. EDIT: Tôi đã cố gắng để loại bỏ hoàn toàn nguyên nhân của vấn đề này, trước tiên tôi đã cố gắng hoàn toàn bỏ qua việc thực hiện bộ nhớ cache và tải xuống từng bitmap từ mạng, sau đó tôi thử gói bộ điều hợp với bộ điều hợp Endless của CommonsWare và cả hai kết quả .. vì vậy điều này chỉ để lại lớp ImageDownloader và bộ điều hợp của tôi là nguyên nhân có thể .. Tôi hoàn toàn bị mất về điều này.

Trả lời

0

Chỉnh sửa: Tôi gặp sự cố tương tự, hình ảnh được sử dụng để thay đổi nhanh chóng. Điều này xảy ra vì phương thức

getView() của bộ điều hợp Danh sách được gọi nhiều lần. mỗi khi getView() được gọi, bạn hãy thử tải xuống và đặt hình ảnh trên hàng. Khi hình ảnh được tải xuống từ mạng, hàng trong danh sách được yêu cầu cho hình ảnh đó có thể đã chuyển ra khỏi phần hiển thị của màn hình nhưng bộ điều hợp cố gắng sử dụng lại hàng đó và điều này dẫn đến đặt hình ảnh được yêu cầu trước đó trên hàng mới tại vị trí đó (vị trí hàng được yêu cầu trước đó).

Hãy thử phương pháp này trong adapter, thiết lập các URL được yêu cầu trên ImageView với setTag:

if(item.get_referenceImage().length()!=0){ 
    //with view, hold the url. This is used to verify that right image is loaded on download completion 
    holder.refImageView.setTag(item.get_referenceImage()); 
    imageManager.loadImage(new MetaItem(item.get_referenceImage(), holder.refImageView)); 
}else{ 
    //no image, set default 
    Log.d(TAG, "No images found for the view setting default image"); 
    holder.refImageView.setTag(null); //this is req, otherwise image cache will retain previous image 
    holder.refImageView.setImageResource(R.drawable.blank_96_1382x); 
} 

trong bộ nhớ cache, trước khi tải bitmap, kiểm tra xem hình ảnh bên phải được nạp:

String url = (String) m_imageViewRef.get().getTag(); 
    if(url != null && url.compareTo(m_metaItem.m_url) == 0){ 
     m_metaItem.m_imageViewRef.get().setImageBitmap(result); 
    } 

PS: MetaItem chỉ là POJO để giữ tham chiếu đến imageView và URL của hình ảnh

4

Lý do cho sự nhấp nháy này là, trong danh sách listview được tái sử dụng. Khi được sử dụng lại, các lần xem ảnh trong mục danh sách sẽ giữ lại tham chiếu hình ảnh cũ được hiển thị trước tiên. Sau đó một khi hình ảnh mới được tải xuống, nó bắt đầu hiển thị. điều này gây ra hành vi nhấp nháy. Để tránh sự cố nhấp nháy này, hãy luôn xóa tham chiếu hình ảnh cũ khỏi các lần xem khi nó được sử dụng lại.

Trong trường hợp của bạn, hãy thêm holder.image.setImageBitmap (null); sau holder = (ViewHolder) convertView.getTag();

Vì vậy, phương pháp getView của bạn() sẽ trông giống như:

@ Override công khai Xem getView (int vị trí cuối cùng, Xem convertView, ViewGroup mẹ) {

... 

if (convertView == null) { 
    LayoutInflater inflater = getLayoutInflater(); 
    convertView = inflater.inflate(viewResourceId, null); 

    holder = new ViewHolder(convertView); 
    convertView.setTag(holder); 
} else { 
    holder = (ViewHolder) convertView.getTag(); 
    holder.image.setImageBitmap(null) 
} 

... 

return convertView; 

}

0

Giải pháp là không tải lại hình ảnh của bạn khi nó không thay đổi.

Trong adapter của bạn getView() làm:

// schedule rendering: 
final String path = ... (set path here); 
if (holder.lastImageUrl == null || !holder.lastImageUrl.equals(path) 
       || holder.headerImageView.getDrawable() == null) { 
    // refresh image 
    imageLoader.displayImage(imageUri, imageAware); 
} else { 
    // do nothing, image did not change and does not need to be updated 
} 

về thành công (thêm một ImageLoadingListener) bạn thiết holder.lastImageUrl = con đường, trên không và hủy bạn thiết holder.lastImageUrl null để nó sẽ được tải lại lần tới.

0

Bạn có thể đang gọi adapter.notifyDatasetChanged() làm cho listview tải lại danh sách bài hát. cố gắng không gọi adapter.notifyDatasetChanged() chế độ xem danh sách của bạn sẽ tự động cập nhật khi bạn cuộn. nhưng điều này có thể gây ra ArrayIndexOutOfBounds ngoại lệ trong khi cuộn.