2009-09-11 29 views
6

Đây là một trường hợp rất phổ biến: hiển thị hình ảnh trong một ListView mà phải được tải xuống từ internet.Android - Vấn đề với hình ảnh tải chậm vào một ListView

Ngay bây giờ tôi có một lớp con tùy chỉnh của ArrayAdapter mà tôi sử dụng cho ListView. Trong getView() của tôi thực hiện của ArrayAdapter, tôi sinh ra một luồng riêng biệt để tải một hình ảnh. Sau khi tải xong, nó tìm kiếm ImageView thích hợp và đặt hình ảnh bằng ImageView.setImageDrawable(). Vì vậy, các giải pháp tôi sử dụng là loại tương tự như thế này: Lazy load of images in ListView

Vấn đề tôi gặp phải là ngay sau khi tôi thực hiện cuộc gọi đến setImageDrawable() trên ImageView, ListView bằng cách nào đó làm mới tất cả các hàng hiện có thể nhìn thấy trong danh sách! Điều này dẫn đến loại một vòng lặp vô hạn:

  1. getView() được gọi
  2. thread được sinh ra để tải hình ảnh
  3. hình ảnh được tải; setImageDrawable() được gọi trên ImageView
  4. ListView chọn nó vì lý do nào đó và tự làm mới chính nó
  5. Để ListView làm mới, getView() được gọi cho mỗi hàng nhìn thấy được, vì vậy chúng ta quay lại bước 1 và toàn bộ lặp lại chính nó

Vì vậy, theo như tôi có thể thấy, giải pháp được đề xuất trong "Android - Làm cách nào để tải ảnh chậm trong ListView" (xem liên kết ở trên) không hoạt động. Nó có thể trông giống như nó, nhưng nó sẽ chạy rất chậm vì trong nền, nó tiếp tục tải lại các hàng hiện đang nhìn thấy.

Có ai gặp phải điều này trước và/hoặc có giải pháp cho việc này không?

Trả lời

2

Trong giải pháp được liên kết, chỉ nên gọi fetchDrawableOnThread() nếu chế độ xem chưa có độ chính xác.

Chế độ xem không thể vẽ nếu getDrawable() trả về giá trị rỗng.

Nếu bạn đang sử dụng lại các vị trí, bạn xem bạn cần phải tiếp tục và quản lý trạng thái. Nếu quan điểm của bạn có một biến thành viên lưu trữ URL, và một boolean để nói cho dù nó được nạp, nó sẽ được dễ dàng để biết liệu để gọi fetchDrawableOnThread() hay không, ví dụ.

Tôi dự đoán rằng có thể vẽ được toString() chi tiết đường dẫn mà hình ảnh đã được tải. (Nếu nó không, bạn có thể phân lớp drawable trở lại để làm cho nó như vậy). Trong trường hợp này, bạn có thể tránh boolean được nêu ở trên và chỉ thực hiện so sánh để xác định liệu nó có thể vẽ đúng hay không để lấy một thay thế.

Ngoài ra, getView() của bạn trên hàng hiển thị phải đảm bảo rằng những người không còn nhìn thấy được tải xuống, để ngăn chặn tình trạng kiệt sức của bộ nhớ. Một sự khéo léo sẽ là di chuyển các hình ảnh không còn nhìn thấy được thành các tham chiếu mềm (vì vậy chúng được dỡ xuống khi cần bộ nhớ) như một áp phích khác trên luồng ban đầu được ghi nhận.

+1

Có Tôi đang sử dụng bản đồ để lưu hình ảnh. Nhưng điều đó không quan trọng, vì tôi cuối cùng vẫn gọi setImageDrawable() mà một lần nữa kích hoạt làm mới. Nếu tôi bằng cách nào đó có thể vô hiệu hóa refesh nó sẽ giải quyết vấn đề của tôi. Tôi không sử dụng SoftReferences (nhưng tôi sẽ), nhưng đó chỉ là tối ưu hóa bộ nhớ, điều này không giải quyết được vòng lặp vô hạn –

+0

điểm tốt Tôi hiểu lầm những gì bạn có nghĩa là 'thread được sinh ra để * tải * hình ảnh' . Tôi sẽ viết lại câu trả lời của tôi – Will

+0

Cảm ơn bạn đã trả lời nhanh chóng :) Những gì bạn đang mô tả hiện không giải quyết được vấn đề khi bạn không sử dụng lại chế độ xem để hiển thị các hàng như tôi. Với việc sử dụng lại các hàng, tôi có nghĩa là sử dụng đối số "convertView" được trao cho getView(). Bạn có biết giải pháp cho điều này khi sử dụng lại chế độ xem hàng không? (bởi vì trong trường hợp đó, bạn phải gọi setImageDrawable() mỗi lần) –

3

tôi đã sử dụng mã trong đường dẫn sau: another stackoverflow question

tôi đã thực hiện những thay đổi nhỏ để giải quyết tái chế xem problem.i thiết lập các url của hình ảnh để tìm kiếm từ nguồn của ImageView trong adapter.Mã sau đây chứa giải pháp của tôi giải quyết vấn đề tái chế:

public void fetchDrawableOnThread(final String urlString, final ImageView imageView,Drawable drw) { 

    imageView.setImageDrawable(drw);//drw is default image 
    if (drawableMap.containsKey(urlString)) { 
     if(imageView.getTag().toString().equals(urlString)) 
     { 
      imageView.setImageBitmap(drawableMap.get(urlString)); 
      imageView.invalidate(); 
      return; 
     } 

    } 

    final Handler handler = new Handler() { 
     @Override 
     public void handleMessage(Message message) { 
      BitmapWrapper wrapper = (BitmapWrapper)message.obj; 
      if(wrapper.imageurl.equals(imageView.getTag().toString())) 
      { 
       imageView.setImageBitmap((Bitmap)wrapper.bitmap); 
       imageView.invalidate(); 
      } 

     } 
    }; 

    Thread thread = new Thread() { 
     @Override 
     public void run() { 
      //TODO : set imageView to a "pending" image 

      Bitmap drawable = fetchDrawable(urlString); 
      BitmapWrapper wrapper = new BitmapWrapper(); 
      wrapper.bitmap = drawable; 
      wrapper.imageurl = urlString; 
      Message message = handler.obtainMessage(1, wrapper); 
      handler.sendMessage(message); 
     } 
    }; 
    thread.start(); 
} 


    public class BitmapWrapper 
{ 
    public Bitmap bitmap; 
    public String imageurl; 
} 
3

Tôi gặp vấn đề tương tự.

Sau gần 2 ngày kể từ khi nặng gỡ lỗi/tối ưu hóa và cố gắng tìm ra, tại sao tôi getView() được gọi cho tất cả các quan điểm hơn và hơn nữa khi sử dụng setImageBitmap() trong một Row, tôi đã đưa ra một giải pháp bẩn:

1) Mở rộng một tùy chỉnh ImageView mà bạn sử dụng cho tất cả các hình ảnh trong Danh sách bạn

2) trong ImageView này ghi đè lên các phương pháp

@Override 
public void requestLayout() 
{ 
    return; 
} 

3) Bẩn, nhưng đối với tôi, nó hoạt động

4) Lợi nhuận;)

+1

Khi Hình ảnh trong danh sách có cùng kích thước và số lần xem của bạn cũng là kích thước cố định (không phải nội dung gói), nó có thể được sử dụng. Điều này đã giúp tôi, cảm ơn :) – Mark

Các vấn đề liên quan