2010-05-26 20 views
39

संभव डुप्लिकेट:
Android - How do I do a lazy load of images in ListViewएंड्रॉइड (शुरुआती स्तर) में लिस्टव्यू पर आलसी लोड छवियां?

मैं कस्टम एडाप्टर के साथ सूचीदृश्य पर काम कर रहा हूँ। मैं छवियों और पाठ दृश्य को लोड करना चाहता हूं। छवियों को इंटरनेट यूआरएल से लोड किया जाता है। मैं सिर्फ उन छवियों को दिखाना चाहता हूं जो एचटीई उपयोगकर्ता के लिए दृश्य सूची आइटम हैं। मैंने Shelves opensource project example from romainguy प्रस्तुत किया, लेकिन यह कोड को समझने के लिए जटिल है। शुरुआती स्तर के लिए, मैं सिर्फ एडाप्टर और गतिविधि के बीच टैग को संभालने के बारे में जानना चाहता हूं। commonswareexample से मैं एडाप्टर पर टैग सेट कर सकता हूं, लेकिन सूचीदृश्य की निष्क्रिय स्थिति पर संबंधित छवि नहीं दिखा सकता। कृपया अपने विचारों के साथ मेरी मदद करें। नमूना कोड अधिक समझने योग्य हैं।

धन्यवाद।

संपादित करें:

ApiDemos में Efficient और Slow एडाप्टर के संयोजन अधिक समझने के लिए उपयोगी है।

इस तरह कुशल एडाप्टर उदाहरण पर किया परिवर्तन:

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"}; 
} 

अब यह ठीक काम करता है। लेकिन जब स्क्रॉलिंग स्थिति यह छवि को ठीक से लोड नहीं कर रही है। वस्तुओं के कुछ अंतराल छवियों को नहीं दिखाता 2। छवियों का सही क्रम लोड हो रहा है। लेकिन सूची के सभी वस्तुओं में नहीं। ठोस आइटम अंतराल के बीच विसंगति हो रही है। इसे कैसे ठीक करें?

उत्तर

5

प्रवीण -

आप पहले से ही इस पर अपना ब्लॉग पोस्ट पाया के रूप में, मैं सिर्फ इसे वापस Stackoverflow करने के लिए पुश करने के लिए है ताकि अन्य लोग इसका इस्तेमाल कर सकते हैं चाहता था।

यहाँ बुनियादी चर्चा है:

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

अपडेट:: http://ballardhack.wordpress.com/2010/04/05/loading-remote-images-in-a-listview-on-android/

और वहाँ एक वर्ग मैं प्रलेखित है और बाद में उस छवियों को लोड करने के लिए एक धागा और एक कॉलबैक का उपयोग करता समाधान करने के लिए अपने विशिष्ट अपवाद, मुझे लगता है कि getChildAt से सूची में लौटाया गया दृश्य ImageView नहीं है - यह छवि और पाठ को पकड़ने के लिए आप जो भी लेआउट व्यू उपयोग कर रहे हैं वह है।

अद्यतन प्रासंगिक कोड शामिल करने के लिए: (प्रति @ जॉर्ज-stocker की सिफारिश)

यहाँ अनुकूलक मैं उपयोग कर रहा था है:

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

क्या आप जानते हैं कि उस छवि दृश्य वस्तु को कैसे प्राप्त करें? मैंने देखा और आपका कोड इस्तेमाल किया। लेकिन वह छवियों को ठीक से लोड नहीं कर रहा है। एक या दो छवियों को पूरा करने के लिए पूरा नहीं किया गया है। और आउटफमेमरी अपवाद भी प्राप्त करता है। क्यूं कर? क्या आप? – Praveen

+0

आपको अपने दृश्य पदानुक्रम के माध्यम से पता लगाने की आवश्यकता होगी। ListView पर GetChildAt (अनुक्रमणिका) को कॉल करना दृश्य सूची के शीर्ष से अनुक्रमणिका के लिए सूची दृश्य आइटम (एक लेआउट व्यू) देता है। उस दृश्य ऑब्जेक्ट पर आप छवि दृश्य प्राप्त करने के लिए findViewById को कॉल कर सकते हैं। – jwadsack

+0

कृपया अपने ब्लॉग से संबंधित अनुभाग यहां पोस्ट करें। इसके बिना, जब वे लिंक मर जाते हैं तो आपके उत्तर की उपयोगिता इसके साथ मर जाती है। –

0

जहाँ तक मैं देख सकता हूं, स्थिर ViewHolder कुछ भी मदद नहीं कर रहा है। /* और */ के बीच फ़ंक्शन को static ViewHolder लाइन को हटाने और holder = new ViewHolder(); को ViewHolder holder = new ViewHolder(); पर बदलने का प्रयास करें।

+0

"पूरे ऑनस्कोलस्टेट चेंज फ़ंक्शन को/* और * /" के बीच डालने का प्रयास करें। इसका कोई मतलब नहीं है। तो हम दृश्यमान पंक्तियों को लोड करने के लिए स्क्रॉल की स्थिति का पता कैसे लगा सकते हैं। – Praveen

+0

वे एडाप्टर के getView फ़ंक्शन में लोड हो जाते हैं। –

0

आह, अपने logcat जाँच सुनिश्चित करें कि आपके एप्लिकेशन प्रतिसाद नहीं बनाने के लिए मार डाला और फिर से शुरू नहीं किया जा रहा है।अधिकांश हैंडसेट आपके कुल एप्लिकेशन आकार को 16 एमबी या 24 एमबी तक सीमित करते हैं। छवियों का एक गुच्छा लोड करना, दौड़ना, मारना, पुनरारंभ करना, और अपना चालू होना आसान है क्योंकि स्क्रीन से बड़े डेटा को लोड न करें। यह गरीब आदमी का कचरा संग्रह है।

+0

मैंने अपना प्रश्न संपादित किया। कृपया इसे देखो। – Praveen

7

जहां तक ​​मैं समझता हूं कि स्क्रॉलिंग समाप्त होने के बाद आपको अपनी सूची अपडेट करने की आवश्यकता है। यह आसान है। यहाँ आप के लिए निश्चित कोड है:

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 फिर से प्रदर्शित करने के लिए सभी दृश्यमान आइटम एडाप्टर बता देंगे, तो वे Image2 के साथ प्रदर्शित किया जाएगा।

17

मुझे मिल गया। यह एकदम सही कोड है जिसे मैं चाहता हूं। आलसी लोडिंग कस्टम एडाप्टर को केवल दृश्य सूची आइटम के आइकन पर काम करता है। उम्मीद है कि यह शुरुआती

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

यह थोड़ा अजीब बात है कि आप धारक का उपयोग करते हैं और फिर हमेशा findViewById का उपयोग करते हैं ... यदि आप धारक की मदद से चीजों को सेट करते हैं तो आप उस धारक चीज़ या तेज़ी से बचने के लिए इसे सरल बना सकते हैं ... – Karussell

संबंधित मुद्दे