2012-10-31 14 views
9

थोड़ा सा नौसिखिया प्रश्न। getView() में हमें ViewHolder क्यों शुरू करना चाहिए? हम इसे निर्माता में क्यों शुरू नहीं कर सकते?व्यूहोल्डर - अच्छा अभ्यास

+1

http://www.youtube.com/watch?v=wDBM6wVEO70 के अंत में इसे अद्यतन है। इस वीडियो पर एक नज़र डालें। अपने प्रश्न का उत्तर देना चाहिए। – Raghunandan

उत्तर

29

है आप अस्तित्व में कई ViewHolder वस्तुओं होगा।

ListView अपनी प्रकृति से नई View अपनी प्रत्येक पंक्ति के लिए उदाहरण नहीं बनाता है। ऐसा इसलिए है कि यदि आपके पास लाखों चीजों की ListView है, तो आपको दस लाख चीज़ों के लिए लेआउट जानकारी स्टोर करने की आवश्यकता नहीं है। तो आपको स्टोर करने की क्या ज़रूरत है? बस स्क्रीन पर चीजें हैं। फिर आप उन विचारों को बार-बार पुन: उपयोग कर सकते हैं। इस तरह, आपके ListView लाख वस्तुओं की संख्या में शायद 10 बच्चे के विचार हो सकते हैं।

अपने कस्टम सरणी अनुकूलक में, आप एक समारोह getView() कहा जाता है कि कुछ इस तरह दिखता है:

public View getView(int position, View convertView, ViewGroup parent) { 
    //Here, position is the index in the list, the convertView is the view to be 
    //recycled (or created), and parent is the ListView itself. 

    //Grab the convertView as our row of the ListView 
    View row = convertView; 

    //If the row is null, it means that we aren't recycling anything - so we have 
    //to inflate the layout ourselves. 
    if(row == null) { 
      LayoutInflater inflater = (LayoutInflater)getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE); 
      row = inflater.inflate(R.layout.list_item, parent, false); 
    } 

    //Now either row is the recycled view, or one that we've inflated. All that's left 
    //to do is set the data of the row. In this case, assume that the row is just a 
    //simple TextView 
    TextView textView = (TextView) row.findViewById(R.id.listItemTextView); 

    //Grab the item to be rendered. In this case, I'm just using a string, but 
    //you will use your underlying object type. 
    final String item = getItem(position); 

    textView.setText(item); 

    //and return the row 
    return row; 
} 

यह काम करेंगे, लेकिन एक क्षण ले और देखें कि आप अक्षमता यहाँ पहचान सकते हैं। इस बारे में सोचें कि उपरोक्त में से कौन सा कोड अनावश्यक रूप से बुलाया जाएगा।

समस्या यह है कि हम row.findViewById को बार-बार कॉल कर रहे हैं, भले ही पहली बार हम इसे देखते हैं, यह कभी नहीं बदलेगा। अगर आपकी सूची में केवल TextView है, तो शायद यह बुरा नहीं है, यदि आपके पास जटिल लेआउट है, या आपके पास कई दृश्य हैं जिन्हें आप डेटा सेट करना चाहते हैं, तो आप अपना दृश्य ढूंढने में थोड़ी देर खो सकते हैं और एक बार फिर।

तो हम इसे कैसे ठीक कर सकते हैं? खैर, यह देखने के बाद कहीं भी टेक्स्ट व्यू को स्टोर करना समझदारी होगी। इसलिए हम ViewHolder नामक एक कक्षा पेश करते हैं, जो विचारों को "रखता है"। तो एडाप्टर के अंदर है, तो जैसे एक आंतरिक वर्ग परिचय:

private static class ViewHolder { 
    TextView textView; 
} 

इस वर्ग, निजी है, क्योंकि यह एडाप्टर के लिए सिर्फ एक कैशिंग तंत्र है, और यह स्थिर ताकि हम के लिए एक संदर्भ की आवश्यकता नहीं है इसका उपयोग करने के लिए एडाप्टर।

यह हमारे विचार को संग्रहीत करेगा ताकि हमें row.findViewById कई बार कॉल करने की आवश्यकता न हो। हमें इसे कहां सेट करना चाहिए? जब हम पहली बार दृश्य को बढ़ाते हैं। हम इसे कहां स्टोर करते हैं? दृश्यों में एक कस्टम "टैग" फ़ील्ड होता है, जिसका उपयोग मेटा-सूचना को दृश्य के बारे में स्टोर करने के लिए किया जा सकता है - बिल्कुल वही है जो हम चाहते हैं! तो फिर, अगर हम पहले से ही इस दृश्य को देखा है, हम बस पंक्ति के भीतर से प्रत्येक दृश्य के ऊपर देख के बजाय टैग को देखने के लिए .. है

तो getView() के अंदर अगर बयान हो जाता है:

//If the row is null, it means that we aren't recycling anything - so we have 
//to inflate the layout ourselves. 
ViewHolder holder = null; 
if(row == null) { 
    LayoutInflater inflater = (LayoutInflater)getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE); 
    row = inflater.inflate(R.layout.list_item, parent, false); 
    //Now create the ViewHolder 
    holder = new ViewHolder(); 
    //and set its textView field to the proper value 
    holder.textView = (TextView) row.findViewById(R.id.listItemTextView); 
    //and store it as the 'tag' of our view 
    row.setTag(holder); 
} else { 
    //We've already seen this one before! 
    holder = (ViewHolder) row.getTag(); 
} 

अब, हमें केवल holder.textView के टेक्स्ट मान को अपडेट करना होगा, क्योंकि यह पहले से ही पुनर्नवीनीकरण दृश्य का संदर्भ है! तो हमारा अंतिम एडाप्टर कोड बन जाता है:

public View getView(int position, View convertView, ViewGroup parent) { 
    //Here, position is the index in the list, the convertView is the view to be 
    //recycled (or created), and parent is the ListView itself. 

    //Grab the convertView as our row of the ListView 
    View row = convertView; 

    //If the row is null, it means that we aren't recycling anything - so we have 
    //to inflate the layout ourselves. 
    ViewHolder holder = null; 
    if(row == null) { 
     LayoutInflater inflater = (LayoutInflater)getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE); 
     row = inflater.inflate(R.layout.list_item, parent, false); 
     //Now create the ViewHolder 
     holder = new ViewHolder(); 
     //and set its textView field to the proper value 
     holder.textView = (TextView) row.findViewById(R.id.listItemTextView); 
     //and store it as the 'tag' of our view 
     row.setTag(holder); 
    } else { 
     //We've already seen this one before! 
     holder = (ViewHolder) row.getTag(); 
    } 

    //Grab the item to be rendered. In this case, I'm just using a string, but 
    //you will use your underlying object type. 
    final String item = getItem(position); 

    //And update the ViewHolder for this View's text to the correct text. 
    holder.textView.setText(item); 

    //and return the row 
    return row; 
} 

और हम कर चुके हैं!

कुछ बातें सोचने के लिए के बारे में:

  1. कैसे इस परिवर्तन करता है, तो आप एक पंक्ति है कि आप बदलना चाहते हैं में अनेक दृश्य है? एक चुनौती के रूप में, एक ListView जहां प्रत्येक पंक्ति दो TextView वस्तुओं और है बनाने के एक ImageView
  2. जब आपके ListView डीबगिंग, कुछ चीजें जाँच इतना है कि आप वास्तव में देख सकते हैं क्या हो रहा है:
    1. ViewHolder के निर्माता कितनी बार कहा जाता है ।
    2. क्या holder.textView.getText() का मूल्य इससे पहले कि आप getView()
+0

thnx, भयानक उत्तर – Gorets

+0

कोई समस्या नहीं - अगर सबकुछ समझ में आता है तो मुझे बताएं, व्यूहोल्डर चीज़ ने मुझे थोड़ी देर के लिए भ्रमित कर दिया जब मैंने पहली बार इसके बारे में सीखा। – mindvirus

+0

वाह। उत्कृष्ट जवाब आपके लिए एक चुनौती के रूप में; क्या आप इसे कुछ संदर्भों में सुधार सकते हैं? =) – Qw4z1

2

जैसे ही हम पंक्ति को पॉप्युलेट करते हैं और प्रत्येक पंक्ति के लिए नई पंक्ति दृश्य बनाते हैं, हमें दृश्य धारक को शामिल करने की आवश्यकता होती है। वैसे ही जैसे मैं तो पंक्ति में दो TextView,

static class ViewHolder { 
     protected TextView title; 
     protected TextView type; 

    } 


    public View getView(int position, View convertView, ViewGroup parent) { 
      View view = null; 
      if (convertView == null) { 
       LayoutInflater inflator = context.getLayoutInflater(); 
       view = inflator.inflate(R.layout.feeds_rowview, null); 
       final ViewHolder viewHolder = new ViewHolder(); 
       view.setTag(viewHolder); 
       viewHolder.title = (TextView) view.findViewById(R.id.Title); 
       viewHolder.type = (TextView) view.findViewById(R.id.Type); 

      } else { 
       view = convertView; 
      } 

      ViewHolder holder = (ViewHolder) view.getTag(); 
      holder.title.setText(list.get(position).getTitle()); 
      holder.type.setText(list.get(position).getType()); 

      return view; 
    } 
+0

नहीं, प्रत्येक दर्शक के पास अलग-अलग मान होते हैं। जैसा कि हमारे पास पंक्ति में अलग-अलग डेटा है। –

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