0

我有一个绑定到 ListView 的自定义 baseadapter。我正在从自定义类加载对象列表。

当数据更改时,我正在更改某些行的布局以创建标题(是的,我知道我仍然需要修复一些逻辑,但它可以工作)。

我的问题是,当我将列表视图滚动到原来可见的内容之外时,应用程序崩溃并在 HeaderHolder 上出现 ClassCastException 错误(如果我在 catch 处理程序中设置了断点,我会看到该错误)。我认为这是由于视图被破坏和重新创建,不确定。有人可以证实这一点吗?

public class MyCustomBaseAdapter extends BaseAdapter {
 private static ArrayList<Appointment> searchArrayList;

 private LayoutInflater mInflater;

 public MyCustomBaseAdapter(Context context, ArrayList<Appointment> results) {
  searchArrayList = results;
  mInflater = LayoutInflater.from(context);
 }

 public int getCount() {
  return searchArrayList.size();
 }

 public Object getItem(int position) {
  return searchArrayList.get(position);
 }

 public long getItemId(int position) {
  return position;
 }

 public String lastDate = null;
 public String thisItemDate = null;

 public View getView(int position, View convertView, ViewGroup parent) 
 {
     try
     {
         thisItemDate = searchArrayList.get(position).GetDTStart().toString().substring(0,10);
         if(!thisItemDate.equals(lastDate))
         {
             HeaderHolder holder;
             if (convertView == null) 
             {
                 convertView = mInflater.inflate(R.layout.calendar_item_header, null);

                 holder = new HeaderHolder();

                 holder.txtHeader = (TextView) convertView.findViewById(R.id.header);

                 convertView.setTag(holder);
                 convertView.setPadding(2,2,2,2);
             } 
             else 
             {
                 holder = (HeaderHolder) convertView.getTag();
             }

             holder.txtHeader.setText(thisItemDate);

             lastDate = thisItemDate;
             return convertView;                 
         }
     }
     catch (Exception e)
     {  //Catch exception if any
        System.err.println("Error: " + e.getMessage());
     }


     ViewHolder holder;
     if (convertView == null) 
     {
         convertView = mInflater.inflate(R.layout.calendar_item, null);

         holder = new ViewHolder();
         holder.txtAttendee = (TextView) convertView.findViewById(R.id.attendee);
         holder.txtSummary = (TextView) convertView.findViewById(R.id.summary);
         holder.txtStarts = (TextView) convertView.findViewById(R.id.starts);
         holder.txtEnds = (TextView) convertView.findViewById(R.id.ends);

         convertView.setTag(holder);
         convertView.setPadding(4, 4, 4, 16);
     } 
     else 
     {
         holder = (ViewHolder) convertView.getTag();
     }

     holder.txtAttendee.setText(searchArrayList.get(position).GetAttendee());
     holder.txtSummary.setText(">" + searchArrayList.get(position).GetSummary());
     String st = searchArrayList.get(position).GetDTStart().toString();
     String en = searchArrayList.get(position).GetDTEnd().toString();

     holder.txtStarts.setText(st.substring(0,16));
     holder.txtEnds.setText(en.substring(0,16));

     return convertView;         
 }

 static class ViewHolder 
 {
     TextView txtAttendee;
     TextView txtSummary;
     TextView txtStarts;
     TextView txtEnds;
 }

 static class HeaderHolder
 {
     TextView txtHeader;
4

2 回答 2

3

You must tell ListView through your Adapter the amount of view types it internally creates / updates and what view type to find at which list position. This is done through the methods:

EDIT: Your implementation is a bit complicated. You should redesign your adapter to explicitly include the section titles as data items of your internal list data structure.

One way for doing that would be to back the adapter with an ArrayList<Object> instead of ArrayList<Appointment>. That way you can have both Appointment objects and section title strings in the same list.

Both methods getView and getItemViewType need to fetch the ArrayList item at the requested position and check the item object for its type:

public int getItemViewType(int position) {
    Object item = getItem(position);

    if(item instanceof Appointment) {
        return 0;
    } else {
        // It's a section title:
        return 1;
    }
}

You would proceed similarly in your getView method:

public View getView(int position, View convertView, ViewGroup parent) {
    Object item = getItem(position);

    if(convertView == null) {
        // Create item view for first time

        if(item instanceof Appointment) {
            convertView = ... // inflate appointment list view item layout
        } else {
            convertView = ... // inflate title section list view item layout
        }
    }

    // Update list view item view according to type:
    if(item instanceof Appointment) {
        Appointment a = (Appointment) item;
        // Retrieve TextViews etc from convertView, cache it as Tag in a ViewHolder
        // and update these views based on Appointment a
    } else {
        // Item is a section header string:
        String label = (String) item;
        // Retrieve label TextView from convertView... etc...
    }

    return convertView;
}

There is actually nothing more to it.

于 2012-07-02T21:34:24.873 回答
0

好吧,我快到了......它工作正常,但有一个例外:当我向下滚动到列表视图的底部时,条目开始乱序。使用调试器,我已经验证了 arraylist 的加载顺序完全正确,并且日期标签在它们应该在的位置。但是,一旦我开始滚动,事情就会变得乱七八糟(例如,listview 条目四处移动,标签向上和/或向下移动。我认为问题是由于我得到了一个空指针异常(在 catch 块中)它右边的注释)。但我不确定为什么会发生这种情况,只有当列表滚动到足以使某些元素弹出而其他元素需要进入视野时。

public class MyCustomBaseAdapter extends BaseAdapter {
 //private static ArrayList<Appointment> searchArrayList;
 private static ArrayList<Object> searchArrayList;

 private LayoutInflater mInflater;

 //public MyCustomBaseAdapter(Context context, ArrayList<Appointment> results) {
 public MyCustomBaseAdapter(Context context, ArrayList<Object> results) {

  searchArrayList = results;
  mInflater = LayoutInflater.from(context);
 }

 public int getCount() {
  return searchArrayList.size();
 }

 public Object getItem(int position) {
  return searchArrayList.get(position);
 }

 public long getItemId(int position) {
  return position;
 }


 // One way for doing that would be to back the adapter with an ArrayList<Object> instead of ArrayList<Appointment>. 
 // That way you can have both Appointment objects and section title strings in the same list.
 // Both methods getView and getItemViewType need to fetch the ArrayList item at the requested position and check the item object for its type:

public int getItemViewType(int position) {
    Object item = getItem(position);

    if(item instanceof Appointment) {
        return 0;
    } else {
        // It's a section title:
        return 1;
    }
}

public View getView(int position, View convertView, ViewGroup parent) 
{
    Object item = getItem(position);

    if(convertView == null) 
    {
        // Create item view for first time
        if(item instanceof Appointment) 
        {   // inflate appointment list view item layout
            convertView = mInflater.inflate(R.layout.calendar_item, null);
        } 
        else 
        {   // inflate title section list view item layout
            convertView = mInflater.inflate(R.layout.calendar_item_header, null); 
        }
    }

    // Update list view item view according to type:
    if(item instanceof Appointment) 
    {
        Appointment a = (Appointment) item;
        // Retrieve TextViews etc from convertView, cache it as Tag in a ViewHolder
        // and update these views based on Appointment a
        ViewHolder holder;

        holder = new ViewHolder();
        holder.txtAttendee = (TextView) convertView.findViewById(R.id.attendee);
        holder.txtSummary = (TextView) convertView.findViewById(R.id.summary);
        holder.txtStarts = (TextView) convertView.findViewById(R.id.starts);
        holder.txtEnds = (TextView) convertView.findViewById(R.id.ends);
        holder.txtCategories = (TextView) convertView.findViewById(R.id.categories);

        convertView.setTag(holder);
        convertView.setPadding(4, 4, 4, 16);

        try
        {
            holder.txtAttendee.setText(a.GetAttendee());
            holder.txtSummary.setText(">" + a.GetSummary());
            String st = a.GetDTStart().toString();
            String en = a.GetDTEnd().toString();

            holder.txtStarts.setText(st.substring(0,16));
            holder.txtEnds.setText(en.substring(0,16)); 

            String cat = a.GetCategories();
            holder.txtCategories.setText(cat);



        }
        catch (Exception e)
        {
            String err = e.getMessage();  // here's the error location

        }
    } 
    else 
    {
        // Item is a section header string:
        String label = (String) item;
        // Retrieve label TextView from convertView... etc...
        HeaderHolder holder;

        holder = new HeaderHolder();

        holder.txtHeader = (TextView) convertView.findViewById(R.id.header);

        convertView.setTag(holder);
        convertView.setPadding(2,2,2,2);
        convertView.setClickable(false);
        convertView.setFocusable(false);

        try
        {
            holder.txtHeader.setText(" " + label);
        }
        catch (Exception e)
        {

        }
    }

    return convertView;
}

 static class ViewHolder 
 {
     TextView txtAttendee;
     TextView txtSummary;
     TextView txtStarts;
     TextView txtEnds;
     TextView txtCategories;
 }

 static class HeaderHolder
 {
     TextView txtHeader;

 }
于 2012-07-04T18:25:09.760 回答