【问题标题】:Android: EfficientAdapter with two different ViewsAndroid:具有两个不同视图的 EfficientAdapter
【发布时间】:2010-12-12 05:42:15
【问题描述】:

我正在使用基于 SDK 演示示例中的 EfficientAdapter example 的扩展版 BaseAdapter。

我的数据基本上是一个对象 (ListPlaces),其中包含一个带有实际地点列表的ArrayList,可通过listPlaces.getValues() 访问。此 ArrayList 数据按范围排序,ArrayList 由一些特殊项目(分隔符)组成,没有数据,但 separator 标志设置为 true

现在,每当我的EfficientAdapter 获得一个作为分隔符的数据对象时,它会为public boolean isEnabled(int position)public View getView(int position, View convertView, ViewGroup parent) 返回false,这取决于当前数据对象是由真实数据组成还是只是一个分隔符,会膨胀两种不同的布局假人。

如果我每次都放大布局,这会很好用。但是,每次都膨胀布局并调用findViewById 会使ListView 变得非常缓慢。

所以我尝试使用 EfficientAdapter 和 ViewHolder 方法。但这并不是开箱即用的,因为我尝试访问两种不同的视图。因此,每当我的convertView != null(else-case)通过我们的ViewHolder 访问布局上的项目时,当前一个 View 是分隔符时,它当然无法访问仅在“真正的”项目布局。

所以我还强制我的getView() 不仅在convertView == null 时膨胀布局,而且在前一个listRow 与当前不同时: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: android.content.res.Resources@437613e0                                  
  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: android.content.res.Resources@437613e0                                       
  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: android.content.res.Resources@437613e0                                  
  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: android.content.res.Resources@437613e0                                       
  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: android.content.res.Resources@437613e0                                                    
  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: android.content.res.Resources@437613e0                                                    
  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: android.content.res.Resources@437613e0                                                    
  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: android.content.res.Resources@437613e0                                  
  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: android.content.res.Resources@437613e0                                       
  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: android.content.res.Resources@437613e0                                  
  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: android.content.res.Resources@437613e0                                       
  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: android.content.res.Resources@437613e0                                                    
  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: android.content.res.Resources@437613e0                                                    
  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)    

【问题讨论】:

  • 您能否添加一条注释,显示堆栈跟踪中第 300 行的位置?如果是if (curValues.separator >= 0),你应该能够处理列表末尾最多 1 的位置吗?

标签: java android listview view adapter


【解决方案1】:

您忘记了几个需要重写的方法:getViewTypeCount()getItemViewType()。对于所有行都相同的列表,这些不是必需的,但它们对您的方案非常重要。正确实施这些,Android 将为您的标题和详细信息行维护单独的对象池。

或者,你可以看看:

【讨论】:

  • 感谢您的快速回复。 getViewTypeCount() 和 getItemViewType() 的提示非常有用。我喜欢这个适配器的东西。也感谢您分享您和 Jeff Sharkey 的源代码。我上周已经看过了,但是由于我目前正在从事一个封闭源代码项目,因此我至少无法实现 GPL 示例。所以我决定开始我自己的实现,多亏了你的帮助,它终于可以工作了。非常感谢!
  • 更新的再现链接不再有效。找不到仍然有效的链接
【解决方案2】:

感谢 getViewTypeCount() 和 getItemViewType() 的提示,它现在可以完美运行。

这两种方法的实现非常简单:

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

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

正如 commonsware 在他的回答中提到的那样,Android 将为不同的列表项维护不同的对象池,这也意味着您可以在我的示例中删除对 listRow_previous 的检查并更改 if (convertView == null || (listRow != listRow_previous))仅限if (convertView == null)

【讨论】:

    猜你喜欢
    • 2019-01-13
    • 1970-01-01
    • 2016-07-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-04-28
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多