2009-11-02 17 views
10

SDKデモサンプルのEfficientAdapter exampleに基づくBaseAdapterの拡張バージョンを使用しています。Android:2つの異なるビューを持つ効率的なアダプタ

私のデータは基本的にはArrayListの実際の場所のリストを持っていて、listPlaces.getValues()でアクセスできるオブジェクト(ListPlaces)です。このArrayListデータは範囲でソートされ、ArrayListはデータなしの特別な項目(区切り文字)で構成されますが、に設定されたseparatorフラグが設定されています。私EfficientAdapterは、それがpublic boolean isEnabled(int position)ためfalse返し、public View getView(int position, View convertView, ViewGroup parent)は、現在のデータ・オブジェクトは、実際のデータからなるか、またはちょうど区切りのダミーである場合に応じて、2つの異なるレイアウトを膨張させたセパレータであるデータオブジェクトを取得します今たび

毎回レイアウトを膨らませると、これは効果的です。しかし、毎回レイアウトを膨らませてfindViewByIdと呼ぶと、ListViewはほとんど異常に遅くなります。

私はViewHolderというアプローチでEfficientAdapterを使用しようとしました。しかし、それは私がアクセスしようとする2つの異なる見解のために、すぐには機能しませんでした。だから私のconvertView != null(else-case)がViewHolder経由でレイアウト上のアイテムにアクセスし、以前のビューがセパレータだったときにはもちろん、実際のアイテムレイアウトでのみ利用可能なTextViewにアクセスすることはできません。以前listRowが現在のものとは異なる場合

は、だから私はまた私のgetView()はレイアウトだけでなくconvertView == nullを膨らませるために強制的に、しかし:if (convertView == null || (listRow != listRow_previous)) { [....] }

これは、ほぼ今動作しているようです。少なくとも、それは最初から正しくクラッシュしません。しかし、それはまだクラッシュし、私は何をしなければならないのか分からない。私はconvertView.getID()convertView.getResources()を調べようとしましたが、それはこれまで本当に役に立たなかった。他の人が私の現在のconvertViewがリストアイテムのレイアウトかリストセパレータのレイアウトと一致するかどうかをどうやって確認できるか考えているかもしれません。ありがとう。

ここにコードがあります。

private class EfficientAdapter extends BaseAdapter { 
    private LayoutInflater mInflater; 
    private ListPlaces listPlaces; 

    private ListRow listRow; 
    private ListRow listRow_previous; 


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

     // Data 
     this.listPlaces = listPlaces; 
    } 

    /** 
    * The number of items in the list is determined by the number of items 
    * in our ArrayList 
    * 
    * @see android.widget.ListAdapter#getCount() 
    */ 
    public int getCount() { 
     return listPlaces.getValues().size(); 
    } 

    /** 
    * 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; 
    } 

    @Override 
    public boolean isEnabled(int position) { 
     // return false if item is a separator: 
     if(listPlaces.getValues().get(position).separator >= 0) 
      return false; 
     else 
      return true; 
    } 

    @Override 
    public boolean areAllItemsEnabled() { 
     return false;   
    } 



    /** 
    * 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) { 

     // Get the values for the current list element 
     ListPlacesValues curValues = listPlaces.getValues().get(position); 
     if (curValues.separator >= 0) 
      listRow = ListRow.SEPARATOR; 
     else 
      listRow = ListRow.ITEM; 
     Log.i(TAG,"Adapter: getView("+position+") " + listRow + " (" + listRow_previous + ") -> START"); 

     // A ViewHolder keeps references to children views to avoid unneccessary calls 
     // to findViewById() on each row. 
     ViewHolder holder; 

     // 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 || (listRow != listRow_previous)) { 
      Log.i(TAG, "--> (convertView == null) at position: " + position); 
      // Creates a ViewHolder and store references to the two children views 
      // we want to bind data to. 
      holder = new ViewHolder(); 

      if (listRow == ListRow.SEPARATOR) { 
       convertView = mInflater.inflate(R.layout.taxonomy_list_separator, null); 
       holder.separatorText = (TextView) convertView.findViewById(R.id.separatorText); 
       convertView.setTag(holder); 
       Log.i(TAG,"\tCREATE SEPARATOR: convertView ID: " + convertView.getId() + " Resource: " + convertView.getResources()); 

      } 
      else { 

       convertView = mInflater.inflate(R.layout.taxonomy_listitem, null); 
       holder.name = (TextView) convertView.findViewById(R.id.name); 
       holder.category = (TextView) convertView.findViewById(R.id.category); 
       // [...] 

       convertView.setTag(holder); 

       Log.i(TAG,"\tCREATE ITEM: convertView ID: " + convertView.getId() + " Resource: " + convertView.getResources()); 
      } 
     } else { 
      // Get the ViewHolder back to get fast access to the TextView 
      // and the ImageView. 
      Log.i(TAG,"\tconvertView ID: " + convertView.getId() + " Resource: " + convertView.getResources()); 

      holder = (ViewHolder) convertView.getTag(); 
      convertView.setAnimation(null); 
     } 

     /* Bind the data efficiently with the holder */ 
     if (listRow == ListRow.SEPARATOR) { 
      String separatorText; 
      switch (curValues.separator) { 
      case 0: separatorText="case 0"; break; 
      case 1: separatorText="case 1"; break; 
      case 2: separatorText="case 2"; break; 
      // [...] 
     default: separatorText="[ERROR]"; break; 
      } 
      holder.separatorText.setText(separatorText); 
     } 
     else { 
      // Set the name: 
      holder.name.setText(curValues.name); 
      // Set category 
      String cat = curValues.classification.toString(); 
      cat = cat.substring(1,cat.length()-1); // removing "[" and "]" 
      if (cat.length() > 35) { 
       cat = cat.substring(0, 35); 
       cat = cat + "..."; 
      } 
      holder.category.setText(cat); 

      // [...] (and many more TextViews and ImageViews to be set) 

     } 

     listRow_previous = listRow; 
     Log.i(TAG,"Adapter: getView("+position+") -> DONE"); 
     return convertView; 
    } 

    private class ViewHolder { 
     TextView name; 
     TextView category; 
     // [...] -> many more TextViews and ImageViews 

     TextView separatorText; 
    } 
} 

をそして、ここで私のLogcat出力:そこまで[...]私はそれが読みやすく、理解しやすくするために、いくつかの重要度の低いコードを取り出している場合は

755  ListPlaces_Activity I onPostExecute: notifyDataSetChanged()                         
    755  ListPlaces_Activity I Adapter: getView(0) SEPARATOR (null) -> START                       
    755  ListPlaces_Activity I --> (convertView == null) at position: 0                        
    755  ListPlaces_Activity I  CREATE SEPARATOR: convertView ID: 2131296317 Resource: [email protected]         
    755  ListPlaces_Activity I Adapter: getView(0) -> DONE                           
    755  ListPlaces_Activity I Adapter: getView(1) ITEM (SEPARATOR) -> START                       
    755  ListPlaces_Activity I --> (convertView == null) at position: 1                        
    755  ListPlaces_Activity I  CREATE ITEM: convertView ID: 2131296317 Resource: [email protected]          
    755  ListPlaces_Activity I Adapter: getView(1) -> DONE                           
    755  ListPlaces_Activity I Adapter: getView(2) SEPARATOR (ITEM) -> START                       
    755  ListPlaces_Activity I --> (convertView == null) at position: 2                        
    755  ListPlaces_Activity I  CREATE SEPARATOR: convertView ID: 2131296317 Resource: [email protected]         
    755  ListPlaces_Activity I Adapter: getView(2) -> DONE                           
    755  ListPlaces_Activity I Adapter: getView(3) ITEM (SEPARATOR) -> START                       
    755  ListPlaces_Activity I --> (convertView == null) at position: 3                        
    755  ListPlaces_Activity I  CREATE ITEM: convertView ID: 2131296317 Resource: [email protected]          
    755  ListPlaces_Activity I Adapter: getView(3) -> DONE                           
    755  ListPlaces_Activity I Adapter: getView(4) ITEM (ITEM) -> START                        
    755  ListPlaces_Activity I  convertView ID: 2131296317 Resource: [email protected]              
    755  ListPlaces_Activity I Adapter: getView(4) -> DONE                           
    755  ListPlaces_Activity I Adapter: getView(5) ITEM (ITEM) -> START                        
    755  ListPlaces_Activity I  convertView ID: 2131296317 Resource: [email protected]              
    755  ListPlaces_Activity I Adapter: getView(5) -> DONE                           
    755  ListPlaces_Activity I Adapter: getView(6) ITEM (ITEM) -> START                        
    755  ListPlaces_Activity I  convertView ID: 2131296317 Resource: [email protected]              
    755  ListPlaces_Activity I Adapter: getView(6) -> DONE                           
    755  ListPlaces_Activity I Adapter: getView(0) SEPARATOR (ITEM) -> START                       
    755  ListPlaces_Activity I --> (convertView == null) at position: 0                        
    755  ListPlaces_Activity I  CREATE SEPARATOR: convertView ID: 2131296317 Resource: [email protected]         
    755  ListPlaces_Activity I Adapter: getView(0) -> DONE                           
    755  ListPlaces_Activity I Adapter: getView(1) ITEM (SEPARATOR) -> START                       
    755  ListPlaces_Activity I --> (convertView == null) at position: 1                        
    755  ListPlaces_Activity I  CREATE ITEM: convertView ID: 2131296317 Resource: [email protected]          
    755  ListPlaces_Activity I Adapter: getView(1) -> DONE                           
    755  ListPlaces_Activity I Adapter: getView(2) SEPARATOR (ITEM) -> START                       
    755  ListPlaces_Activity I --> (convertView == null) at position: 2                        
    755  ListPlaces_Activity I  CREATE SEPARATOR: convertView ID: 2131296317 Resource: [email protected]         
    755  ListPlaces_Activity I Adapter: getView(2) -> DONE                           
    755  ListPlaces_Activity I Adapter: getView(3) ITEM (SEPARATOR) -> START                       
    755  ListPlaces_Activity I --> (convertView == null) at position: 3                        
    755  ListPlaces_Activity I  CREATE ITEM: convertView ID: 2131296317 Resource: [email protected]          
    755  ListPlaces_Activity I Adapter: getView(3) -> DONE                           
    755  ListPlaces_Activity I Adapter: getView(4) ITEM (ITEM) -> START                        
    755  ListPlaces_Activity I  convertView ID: 2131296317 Resource: [email protected]              
    755  ListPlaces_Activity I Adapter: getView(4) -> DONE                           
    755  ListPlaces_Activity I Adapter: getView(5) ITEM (ITEM) -> START                        
    755  ListPlaces_Activity I  convertView ID: 2131296317 Resource: [email protected]              
    755   AndroidRuntime D Shutting down VM                              
    755    dalvikvm W threadid=3: thread exiting with uncaught exception (group=0x4001aa28)                 
    755   AndroidRuntime E Uncaught handler: thread main exiting due to uncaught exception                  
    755   AndroidRuntime E java.lang.NullPointerException                          
    755   AndroidRuntime E  at com.tato.main.ListPlaces_Activity$EfficientAdapter.getView(ListPlaces_Activity.java:330)          
    755   AndroidRuntime E  at android.widget.HeaderViewListAdapter.getView(HeaderViewListAdapter.java:191)             
    755   AndroidRuntime E  at android.widget.AbsListView.obtainView(AbsListView.java:1255)                 
    755   AndroidRuntime E  at android.widget.ListView.makeAndAddView(ListView.java:1658)                 
    755   AndroidRuntime E  at android.widget.ListView.fillDown(ListView.java:637)                   
    755   AndroidRuntime E  at android.widget.ListView.fillFromTop(ListView.java:694)                  
    755   AndroidRuntime E  at android.widget.ListView.layoutChildren(ListView.java:1502)                 
    755   AndroidRuntime E  at android.widget.AbsListView.onLayout(AbsListView.java:1112)                 
    755   AndroidRuntime E  at android.view.View.layout(View.java:6569)                      
    755   AndroidRuntime E  at android.widget.LinearLayout.setChildFrame(LinearLayout.java:1119)               
    755   AndroidRuntime E  at android.widget.LinearLayout.layoutVertical(LinearLayout.java:998)               
    755   AndroidRuntime E  at android.widget.LinearLayout.onLayout(LinearLayout.java:918)                 
    755   AndroidRuntime E  at android.view.View.layout(View.java:6569)                      
    755   AndroidRuntime E  at android.widget.FrameLayout.onLayout(FrameLayout.java:333)                 
    755   AndroidRuntime E  at android.view.View.layout(View.java:6569)                      
    755   AndroidRuntime E  at android.widget.LinearLayout.setChildFrame(LinearLayout.java:1119)               
    755   AndroidRuntime E  at android.widget.LinearLayout.layoutVertical(LinearLayout.java:998)               
    755   AndroidRuntime E  at android.widget.LinearLayout.onLayout(LinearLayout.java:918)                 
    755   AndroidRuntime E  at android.view.View.layout(View.java:6569)                      
    755   AndroidRuntime E  at android.widget.FrameLayout.onLayout(FrameLayout.java:333)                 
    755   AndroidRuntime E  at android.view.View.layout(View.java:6569)                      
    755   AndroidRuntime E  at android.view.ViewRoot.performTraversals(ViewRoot.java:979)                 
    755   AndroidRuntime E  at android.view.ViewRoot.handleMessage(ViewRoot.java:1613)                  
    755   AndroidRuntime E  at android.os.Handler.dispatchMessage(Handler.java:99)                   
    755   AndroidRuntime E  at android.os.Looper.loop(Looper.java:123)                      
    755   AndroidRuntime E  at android.app.ActivityThread.main(ActivityThread.java:4203)                 
    755   AndroidRuntime E  at java.lang.reflect.Method.invokeNative(Native Method)                   
    755   AndroidRuntime E  at java.lang.reflect.Method.invoke(Method.java:521)                    
    755   AndroidRuntime E  at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:791)            
    755   AndroidRuntime E  at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:549)                 
    755   AndroidRuntime E  at dalvik.system.NativeStart.main(Native Method)  
+0

行300がスタックトレースにある場所を示すコメントを追加できますか? 'if(curValues.separator> = 0)'であれば、リストの最後まで位置を1つまで扱えるはずですか? – rsp

答えて

12

あなたは忘れてしまいましたオーバーライドする必要がある方法は、getViewTypeCount()getItemViewType()の2つです。これらはすべての行が同じリストには必要ありませんが、シナリオにとっては非常に重要です。これらを適切に実装すると、Androidはヘッダーと詳細行に別々のオブジェクトプールを保持します。

それとも、あなたが見て可能性:

+1

あなたの迅速な対応に感謝します。 getViewTypeCount()とgetItemViewType()のヒントは非常に便利でした。私はこのアダプタのものが大好きです。 Jeff Sharkeyのソースコードを共有してくれてありがとう。私は先週それを見てきましたが、私は現在クローズドソースプロジェクトに取り組んでいるので、少なくともGPLの例は実装できません。だから自分の実装を開始することにしました。 Muchas gracias! – znq

+0

更新されたレンディションリンクはもう機能していません。まだ動作するリンクを見つけることができませんでした – rtack

6

getViewTypeCount()およびgetItemViewType()のヒントのおかげで、今すぐ完全に動作します。これら2つのメソッドを実装する

は非常に簡単だった:彼の答えにAndroidはまた、あなたがlistRow_previousのチェックを外すことができることを意味、別のリスト項目の異なるオブジェクトプールを維持するこの方法を述べたcommonswareとして

@Override 
public int getViewTypeCount() { 
    return 2; 
} 

@Override 
public int getItemViewType(int position) { 
if(listPlaces.getValues().get(position).separator >= 0) 
    return 0; 
else 
    return 1; 
} 

私の例ではif (convertView == null || (listRow != listRow_previous))if (convertView == null)に変更するだけです。

関連する問題