2010-05-26 25 views
39

thể trùng lặp:
Android - How do I do a lazy load of images in ListViewHình ảnh tải Lười biếng trên Listview trong android (Sơ cấp)?

tôi đang làm việc trên listview với adapter tùy chỉnh. Tôi muốn tải hình ảnh và chế độ xem văn bản trên đó. Các hình ảnh được tải từ các url internet. Tôi chỉ muốn hiển thị những hình ảnh có thể nhìn thấy danh sách mục để người dùng hte. Tôi đã tham khảo Shelves opensource project example from romainguy, nhưng nó phức tạp để hiểu mã. Đối với cấp độ mới bắt đầu, tôi chỉ muốn biết cách xử lý thẻ giữa bộ điều hợp và hoạt động. Từ commonswareexample Tôi có thể đặt thẻ trên bộ điều hợp nhưng không thể hiển thị hình ảnh tương ứng ở trạng thái chờ của chế độ xem danh sách. Xin hãy giúp tôi với ý tưởng của bạn. Mã mẫu dễ hiểu hơn.

Cảm ơn.

EDIT:

Sự kết hợp của EfficientSlow Adaptor trong ApiDemos là hữu ích hơn để hiểu.

thay đổi được thực hiện trên hiệu quả ví dụ bộ chuyển đổi như thế này:

public class List14 extends ListActivity implements ListView.OnScrollListener { 
// private TextView mStatus; 

private static boolean mBusy = false; 
static ViewHolder holder; 

public static class EfficientAdapter extends BaseAdapter { 
    private LayoutInflater mInflater; 
    private Bitmap mIcon1; 
    private Bitmap mIcon2; 

    public EfficientAdapter(Context context) { 
     // Cache the LayoutInflate to avoid asking for a new one each time. 
     mInflater = LayoutInflater.from(context); 

     // Icons bound to the rows. 
     mIcon1 = BitmapFactory.decodeResource(context.getResources(), 
       R.drawable.icon48x48_1); 
     mIcon2 = BitmapFactory.decodeResource(context.getResources(), 
       R.drawable.icon48x48_2); 
    } 

    /** 
    * The number of items in the list is determined by the number of 
    * speeches in our array. 
    * 
    * @see android.widget.ListAdapter#getCount() 
    */ 
    public int getCount() { 
     return DATA.length; 
    } 

    /** 
    * Since the data comes from an array, just returning the index is 
    * sufficent to get at the data. If we were using a more complex data 
    * structure, we would return whatever object represents one row in the 
    * list. 
    * 
    * @see android.widget.ListAdapter#getItem(int) 
    */ 
    public Object getItem(int position) { 
     return position; 
    } 

    /** 
    * Use the array index as a unique id. 
    * 
    * @see android.widget.ListAdapter#getItemId(int) 
    */ 
    public long getItemId(int position) { 
     return position; 
    } 

    /** 
    * Make a view to hold each row. 
    * 
    * @see android.widget.ListAdapter#getView(int, android.view.View, 
    *  android.view.ViewGroup) 
    */ 
    public View getView(int position, View convertView, ViewGroup parent) { 
     // A ViewHolder keeps references to children views to avoid 
     // unneccessary calls 
     // to findViewById() on each row. 

     // When convertView is not null, we can reuse it directly, there is 
     // no need 
     // to reinflate it. We only inflate a new View when the convertView 
     // supplied 
     // by ListView is null. 
     if (convertView == null) { 
      convertView = mInflater.inflate(R.layout.list_item_icon_text, 
        null); 

      // Creates a ViewHolder and store references to the two children 
      // views 
      // we want to bind data to. 
      holder = new ViewHolder(); 
      holder.text = (TextView) convertView.findViewById(R.id.text); 
      holder.icon = (ImageView) convertView.findViewById(R.id.icon); 

      convertView.setTag(holder); 
     } else { 
      // Get the ViewHolder back to get fast access to the TextView 
      // and the ImageView. 
      holder = (ViewHolder) convertView.getTag(); 
     } 
     if (!mBusy) { 
      holder.icon.setImageBitmap(mIcon1); 

      // Null tag means the view has the correct data 
      holder.icon.setTag(null); 
     } else { 
      holder.icon.setImageBitmap(mIcon2); 

      // Non-null tag means the view still needs to load it's data 
      holder.icon.setTag(this); 
     } 
     holder.text.setText(DATA[position]); 

     // Bind the data efficiently with the holder. 
     // holder.text.setText(DATA[position]); 

     return convertView; 
    } 

    static class ViewHolder { 
     TextView text; 
     ImageView icon; 
    } 
} 

private Bitmap mIcon1; 
private Bitmap mIcon2; 
@Override 
public void onCreate(Bundle savedInstanceState) { 
    mIcon1 = BitmapFactory.decodeResource(this.getResources(), 
      R.drawable.icon48x48_1); 
    mIcon2 = BitmapFactory.decodeResource(this.getResources(), 
      R.drawable.icon48x48_2); 
    super.onCreate(savedInstanceState); 
    setListAdapter(new EfficientAdapter(this)); 
    getListView().setOnScrollListener(this); 
} 

public void onScroll(AbsListView view, int firstVisibleItem, 
     int visibleItemCount, int totalItemCount) { 
} 

public void onScrollStateChanged(AbsListView view, int scrollState) { 
    switch (scrollState) { 
    case OnScrollListener.SCROLL_STATE_IDLE: 
     mBusy = false; 

     int first = view.getFirstVisiblePosition(); 
     int count = view.getChildCount(); 
     for (int i = 0; i < count; i++) { 

      holder.icon = (ImageView) view.getChildAt(i).findViewById(
        R.id.icon); 
      if (holder.icon.getTag() != null) { 
       holder.icon.setImageBitmap(mIcon1); 
       holder.icon.setTag(null); 
      } 
     } 

     // mStatus.setText("Idle"); 
     break; 
    case OnScrollListener.SCROLL_STATE_TOUCH_SCROLL: 
     mBusy = true; 
     // mStatus.setText("Touch scroll"); 
     break; 
    case OnScrollListener.SCROLL_STATE_FLING: 
     mBusy = true; 
     // mStatus.setText("Fling"); 
     break; 
    } 
} 
private static final String[] DATA = { "Abbaye de Belloc", 
     "Abbaye du Mont des Cats", "Abertam", "Abondance", "Ackawi", 
     "Acorn", "Adelost", "Affidelice au Chablis", "Afuega'l Pitu"}; 
} 

Nó hoạt động tốt ngay bây giờ. Nhưng khi trạng thái cuộn nó không tải lại hình ảnh đúng cách. một số khoảng thời gian của các mục không hiển thị hình ảnh2. đó là thứ tự đúng của hình ảnh đang tải. Nhưng không phải trong tất cả các mục trong danh sách. Sự không phù hợp xảy ra giữa các khoảng thời gian cố định. cách sửa lỗi?

Trả lời

5

Praveen -

Như bạn đã tìm thấy bài viết trên blog của tôi về vấn đề này, tôi chỉ muốn đẩy nó trở lại Stackoverflow để những người khác có thể sử dụng nó.

Dưới đây là các cuộc thảo luận cơ bản: http://ballardhack.wordpress.com/2010/04/05/loading-remote-images-in-a-listview-on-android/

Và có một lớp I ghi nhận sau đó sử dụng một sợi và một callback để tải những hình ảnh:

http://ballardhack.wordpress.com/2010/04/10/loading-images-over-http-on-a-separate-thread-on-android/

Cập nhật: Để giải quyết của bạn ngoại lệ cụ thể, tôi nghĩ rằng chế độ xem được trả lại trong danh sách từ getChildAt không phải là ImageView - đó là bất kỳ chế độ xem bố cục nào bạn đang sử dụng để giữ hình ảnh và văn bản.

Cập nhật để bao gồm mã có liên quan: (mỗi @ george-Stocker khuyến nghị của)

Đây là adapter Tôi đã sử dụng:

public class MediaItemAdapter extends ArrayAdapter<MediaItem> { 
    private final static String TAG = "MediaItemAdapter"; 
    private int resourceId = 0; 
    private LayoutInflater inflater; 
    private Context context; 

    private ImageThreadLoader imageLoader = new ImageThreadLoader(); 

    public MediaItemAdapter(Context context, int resourceId, List<MediaItem> mediaItems) { 
    super(context, 0, mediaItems); 
    this.resourceId = resourceId; 
    inflater = (LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); 
    this.context = context; 
    } 

    @Override 
    public View getView(int position, View convertView, ViewGroup parent) { 

    View view; 
    TextView textTitle; 
    TextView textTimer; 
    final ImageView image; 

    view = inflater.inflate(resourceId, parent, false); 

    try { 
     textTitle = (TextView)view.findViewById(R.id.text); 
     image = (ImageView)view.findViewById(R.id.icon); 
    } catch(ClassCastException e) { 
     Log.e(TAG, "Your layout must provide an image and a text view with ID's icon and text.", e); 
     throw e; 
    } 

    MediaItem item = getItem(position); 
    Bitmap cachedImage = null; 
    try { 
     cachedImage = imageLoader.loadImage(item.thumbnail, new ImageLoadedListener() { 
     public void imageLoaded(Bitmap imageBitmap) { 
     image.setImageBitmap(imageBitmap); 
     notifyDataSetChanged();    } 
     }); 
    } catch (MalformedURLException e) { 
     Log.e(TAG, "Bad remote image URL: " + item.thumbnail, e); 
    } 

    textTitle.setText(item.name); 

    if(cachedImage != null) { 
     image.setImageBitmap(cachedImage); 
    } 

    return view; 
    } 
} 
+0

Bạn có biết cách lấy đối tượng xem hình ảnh đó không? Tôi đã thấy và sử dụng mã của bạn. Nhưng điều đó không tải hình ảnh đúng cách. Một hoặc hai hình ảnh không hoàn thành để losd. Và được ngoại lệ Outofmemory quá. tại sao? Bạn có thể? – Praveen

+0

Bạn cần phải theo dõi thông qua phân cấp chế độ xem của mình. Gọi getChildAt (index) trên ListView trả về mục xem danh sách (dạng xem bố trí) cho chỉ mục từ đầu danh sách hiển thị. Trên đối tượng xem đó, bạn có thể gọi hàm findViewById để xem hình ảnh. – jwadsack

+0

Vui lòng đăng các phần liên quan từ blog của bạn đến đây. Nếu không có điều đó, khi những liên kết đó chết thì tính hữu ích của câu trả lời của bạn sẽ chết đi cùng với nó. –

0

Theo như tôi thấy, static ViewHolder không giúp ích gì. Thử đặt toàn bộ chức năng onScrollStateChanged giữa /**/, xóa đường dây static ViewHolder và thay đổi holder = new ViewHolder(); thành ViewHolder holder = new ViewHolder();.

+0

"Thử đặt toàn bộ chức năng onScrollStateChanged giữa/* và * /". nó không có ý nghĩa. thì làm cách nào chúng tôi có thể theo dõi trạng thái cuộn để tải các hàng cụ thể ở chế độ hiển thị. – Praveen

+0

Chúng được tải trong chức năng getView của adapter. –

0

Ah, kiểm tra logcat bạn để chắc chắn isn ứng dụng của bạn không bị giết và khởi động lại.Hầu hết các thiết bị cầm tay giới hạn tổng kích thước ứng dụng của bạn đến 16mb hoặc 24mb. Thật dễ dàng để tải lên một loạt các hình ảnh, chạy qua, bị giết, khởi động lại và có onPause của bạn không tải dữ liệu lớn tắt màn hình. Đó là bộ sưu tập garabage của người đàn ông nghèo.

+0

tôi đã chỉnh sửa câu hỏi của mình. hãy nhìn vào nó. – Praveen

7

Theo như tôi hiểu, bạn cần phải cập nhật danh sách của mình sau khi cuộn xong. Thật dễ dàng. Dưới đây là mã cố định cho bạn:

EfficientAdapter adapter; 

@Override 
public void onCreate(Bundle savedInstanceState) { 
    mIcon1 = BitmapFactory.decodeResource(this.getResources(), 
      R.drawable.icon48x48_1); 
    mIcon2 = BitmapFactory.decodeResource(this.getResources(), 
      R.drawable.icon48x48_2); 
    super.onCreate(savedInstanceState); 
    adapter=new EfficientAdapter(this); 
    setListAdapter(adapter); 
    getListView().setOnScrollListener(this); 
} 

public void onScroll(AbsListView view, int firstVisibleItem, 
    int visibleItemCount, int totalItemCount) { 
} 

public void onScrollStateChanged(AbsListView view, int scrollState) { 
    switch (scrollState) { 
    case OnScrollListener.SCROLL_STATE_IDLE: 
     mBusy = false; 
     adapter.notifyDataSetChanged() 
     break; 
    case OnScrollListener.SCROLL_STATE_TOUCH_SCROLL: 
     mBusy = true; 
     // mStatus.setText("Touch scroll"); 
     break; 
    case OnScrollListener.SCROLL_STATE_FLING: 
     mBusy = true; 
     // mStatus.setText("Fling"); 
     break; 
    } 
} 

notifyDataSetChanged sẽ yêu cầu bộ điều hợp hiển thị lại tất cả các mục hiển thị, để chúng hiển thị với hình ảnh2.

17

Tôi hiểu rồi. Đây là mã hoàn hảo tôi muốn. Tải trọng lười hoạt động với bộ điều hợp tùy chỉnh chỉ các biểu tượng của mục danh sách hiển thị. Hy vọng nó sẽ giúp cho người mới bắt đầu

public class List14 extends ListActivity implements ListView.OnScrollListener { 
// private TextView mStatus; 

private static boolean mBusy = false; 
static ViewHolder holder; 

public static class EfficientAdapter extends BaseAdapter { 
    private LayoutInflater mInflater; 
    private Bitmap mIcon1; 
    private Bitmap mIcon2; 
    private Context mContext; 

    public EfficientAdapter(Context context) { 
     // Cache the LayoutInflate to avoid asking for a new one each time. 
     mInflater = (LayoutInflater) context 
       .getSystemService(Context.LAYOUT_INFLATER_SERVICE); 
     mContext = context; 
     // Icons bound to the rows. 
     mIcon1 = BitmapFactory.decodeResource(context.getResources(), 
       R.drawable.icon48x48_1); 
     mIcon2 = BitmapFactory.decodeResource(context.getResources(), 
       R.drawable.icon48x48_2); 
    } 

    /** 
    * The number of items in the list is determined by the number of 
    * speeches in our array. 
    * 
    * @see android.widget.ListAdapter#getCount() 
    */ 
    public int getCount() { 
     return DATA.length; 
    } 

    /** 
    * Since the data comes from an array, just returning the index is 
    * sufficent to get at the data. If we were using a more complex data 
    * structure, we would return whatever object represents one row in the 
    * list. 
    * 
    * @see android.widget.ListAdapter#getItem(int) 
    */ 
    public Object getItem(int position) { 
     return position; 
    } 

    /** 
    * Use the array index as a unique id. 
    * 
    * @see android.widget.ListAdapter#getItemId(int) 
    */ 
    public long getItemId(int position) { 
     return position; 
    } 

    /** 
    * Make a view to hold each row. 
    * 
    * @see android.widget.ListAdapter#getView(int, android.view.View, 
    *  android.view.ViewGroup) 
    */ 
    public View getView(int position, View convertView, ViewGroup parent) { 
     // A ViewHolder keeps references to children views to avoid 
     // unneccessary calls 
     // to findViewById() on each row. 

     // When convertView is not null, we can reuse it directly, there is 
     // no need 
     // to reinflate it. We only inflate a new View when the convertView 
     // supplied 
     // by ListView is null. 
     if (convertView == null) { 
      convertView = mInflater.inflate(R.layout.list_item_icon_text, 
        parent, false); 

      // Creates a ViewHolder and store references to the two children 
      // views 
      // we want to bind data to. 
      holder = new ViewHolder(); 
      holder.text = (TextView) convertView.findViewById(R.id.text); 
      holder.icon = (ImageView) convertView.findViewById(R.id.icon); 

      convertView.setTag(holder); 
     } else { 
      // Get the ViewHolder back to get fast access to the TextView 
      // and the ImageView. 
      holder = (ViewHolder) convertView.getTag(); 
     } 

     if (!mBusy) { 

      holder.icon.setImageBitmap(mIcon1); 

      // Null tag means the view has the correct data 
      holder.icon.setTag(null); 

     } else { 
      holder.icon.setImageBitmap(mIcon2); 

      // Non-null tag means the view still needs to load it's data 
      holder.icon.setTag(this); 
     } 
     holder.text.setText(DATA[position]); 

     // Bind the data efficiently with the holder. 
     // holder.text.setText(DATA[position]); 

     return convertView; 
    } 

    static class ViewHolder { 
     TextView text; 
     ImageView icon; 
    } 
} 

private Bitmap mIcon1; 
private Bitmap mIcon2; 

@Override 
public void onCreate(Bundle savedInstanceState) { 
    mIcon1 = BitmapFactory.decodeResource(this.getResources(), 
      R.drawable.icon48x48_1); 
    mIcon2 = BitmapFactory.decodeResource(this.getResources(), 
      R.drawable.icon48x48_2); 
    super.onCreate(savedInstanceState); 
    setListAdapter(new EfficientAdapter(this)); 
    getListView().setOnScrollListener(this); 
} 

public void onScroll(AbsListView view, int firstVisibleItem, 
     int visibleItemCount, int totalItemCount) { 
} 

public void onScrollStateChanged(AbsListView view, int scrollState) { 
    switch (scrollState) { 
    case OnScrollListener.SCROLL_STATE_IDLE: 
     mBusy = false; 

     int first = view.getFirstVisiblePosition(); 
     int count = view.getChildCount(); 

     for (int i = 0; i < count; i++) { 

      holder.icon = (ImageView) view.getChildAt(i).findViewById(
        R.id.icon); 
      if (holder.icon.getTag() != null) { 
       holder.icon.setImageBitmap(IMAGE[first+i]);// this is the image url array. 
       holder.icon.setTag(null); 
      } 
     } 

     // mStatus.setText("Idle"); 
     break; 
    case OnScrollListener.SCROLL_STATE_TOUCH_SCROLL: 
     mBusy = true; 
     // mStatus.setText("Touch scroll"); 
     break; 
    case OnScrollListener.SCROLL_STATE_FLING: 
     mBusy = true; 
     // mStatus.setText("Fling"); 
     break; 
    } 
} 

private static final String[] DATA = { "Abbaye de Belloc", 
     "Abbaye du Mont des Cats", "Abertam", "Abondance", "Ackawi", 
     "Acorn", "Adelost", "Affidelice au Chablis", "Afuega'l Pitu", 
     "Yarra Valley Pyramid", "Yorkshire Blue", "Zamorano", 
     "Zanetti Grana Padano", "Zanetti Parmigiano Reggiano" }; 
    } 
+4

nó là một chút lạ mà bạn sử dụng một chủ và sau đó luôn luôn sử dụng findViewById ... bạn có thể làm cho nó đơn giản nếu bạn tránh điều đó chủ hoặc nhanh hơn nếu bạn thiết lập những điều với sự giúp đỡ của chủ ... – Karussell

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