【问题标题】:Custom ListView adapter getView() failing on second item自定义 ListView 适配器 getView() 在第二项上失败
【发布时间】:2015-10-19 13:00:02
【问题描述】:

我正在编写一个自定义的ListView 适配器来提供一个项目列表,每个组之间都有一个“标题”项目(特别是带有年份标题的日历)。我已经根据在线信息 (http://android.amberfog.com/?p=296) 编写了自定义类,以包含其他 SO 帖子 (ListView Adapter with multiple Item layouts)。

我认为我拥有我想要的一切(由于结构差异,两个单独的布局文件),但是当适配器被使用时,getView() 中的 convertView 参数在我第二次通过时永远不会为空会期望它是,并且适配器尝试对“事件”项使用非空“标题”视图,并且我得到一个类型转换异常。以下是我的完整适配器代码。我在这里缺少什么 - 这在概念上似乎很简单。

public class CalendarListAdapter extends BaseAdapter {
    private final String TAG = this.getClass().getSimpleName();

    private List<CalendarItem> items;
    private final Context context;

    private final int ITEM_HEADER = 0;
    private final int ITEM_EVENT = 1;

    private LayoutInflater layoutInflater;

    public CalendarListAdapter(Context context, List<CalendarItem> items) {
        this.items = items;
        this.context = context;
        layoutInflater = (LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
    }

    @Override
    public int getCount() {
        return items.size();
    }

    @Override
    public Object getItem(int position) {
        return items.get(position);
    }

    public void addItem(final CalendarItem item) {
        items.add(item);
        notifyDataSetChanged();
    }

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

    public void setItems(List<CalendarItem> items) {
        this.items = items;
        notifyDataSetChanged();
    }

    @Override
    public int getItemViewType(int position) {
        CalendarItem item = items.get(position);
        if (item instanceof CalendarHeader)
            return ITEM_HEADER;
        else
            return ITEM_EVENT;
    }

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

    @Override
    public View getView(final int position, View convertView, ViewGroup parent) {
        Log.i(TAG, "getView " + position + " " + convertView);
        HeaderViewHolder headerViewHolder;
        EventViewHolder eventViewHolder;

        CalendarItem item = items.get(position);
        Log.i(TAG, "Item at position " + position + " is " + item);

        int rowType = getItemViewType(position);
        if (rowType == ITEM_HEADER) {
            CalendarHeader header = (CalendarHeader)item;

            if (convertView != null)
                headerViewHolder = (HeaderViewHolder) convertView.getTag();
            else {
                convertView = layoutInflater.inflate(R.layout.item_calendar_header, null);
                headerViewHolder = new HeaderViewHolder();
                headerViewHolder.headerTextView = (TextView)convertView.findViewById(R.id.calendar_header_textview);
                convertView.setTag(headerViewHolder);
            }

            // Set data in view holder for header

            headerViewHolder.headerTextView.setText(header.getTitle());

        } else {
            CalendarEvent event = (CalendarEvent)item;

            if (convertView != null)
                eventViewHolder = (EventViewHolder) convertView.getTag();
            else {
                convertView = layoutInflater.inflate(R.layout.item_calendar_event, null);
                eventViewHolder = new EventViewHolder();
                eventViewHolder.dateView = (TextView)convertView.findViewById(R.id.row_date_textview);
                eventViewHolder.daysTextView = (TextView)convertView.findViewById(R.id.row_days_textview);
                eventViewHolder.itemTextView = (TextView)convertView.findViewById(R.id.row_item_textview);
                convertView.setTag(eventViewHolder);
            }

            // Set data in view holder for event

            DateTime dateOfEvent = new DateTime(event.getTimestamp());
            DateTimeFormatter formatter = DateTimeFormat.forPattern("MMM dd")
                    .withLocale(Locale.US);
            String text = formatter.print(dateOfEvent);
            eventViewHolder.dateView.setText(text);

            DateTime today = new DateTime();
            int daysBetween = Days.daysBetween(today.toLocalDate(),
                    dateOfEvent.toLocalDate()).getDays();
            eventViewHolder.daysTextView.setText(String.format("%d %s", daysBetween,
                    context.getResources().getString(R.string.days)));

            eventViewHolder.itemTextView.setText(event.getTitle());

        }

        return convertView;
    }

    static class HeaderViewHolder {
        protected TextView headerTextView;
    }

    static class EventViewHolder {
        protected TextView dateView;
        protected TextView daysTextView;
        protected TextView itemTextView;
    }

}

这是一些日志输出:

10-19 08:38:33.971 22700-22700/ I/CalendarListAdapter: getView 0 null
10-19 08:38:38.318 22700-22700/ I/CalendarListAdapter: Item at position 0 is CalendarHeader : {itemType='HEADER'title='2016'}
10-19 08:38:45.728 22700-22700/ I/CalendarListAdapter: getView 1 android.support.v7.widget.AppCompatTextView{1ba49362 V.ED.... ......ID 0,0-0,0 #7f0d0089 app:id/calendar_header_textview}
10-19 08:38:47.130 22700-22700/ I/CalendarListAdapter: Item at position 1 is CalendarEvent : {itemType='EVENT', title='Spring Training (Pitchers & Catchers)', timestamp='1456041600000'}
10-19 08:39:11.149 22700-22700/ E/BaseballAlmanac: Thread.UncaughtExceptionHandler : uncaught exception is: CalendarListAdapter$HeaderViewHolder cannot be cast to CalendarListAdapter$EventViewHolder

适配器的定义:

@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container,
                         @Nullable Bundle savedInstanceState) {
    ...
    calendarListAdapter = new CalendarListAdapter(getActivity(), items);
    calendarListView.setAdapter(calendarListAdapter);
    ...
    return view;
}

适配器的使用:

items = new ArrayList<CalendarItem>();
Set<String> keys = calendarMap.keySet();
for (String key : keys) {

    // Add the header for each year we have data for.  This will be a simple
    // TextView layout
    items.add(new CalendarHeader(key));

    // Add the individual events for the year
    List<CalendarEvent> events = calendarMap.get(key);
    for (CalendarEvent event : events)
        items.add(new CalendarEvent(event.getTitle(), event.getTimestamp()));

}
calendarListAdapter.setItems(items);

当前定义的有问题的 bean 类:

public interface CalendarItem {
    String getTitle();
}

public class CalendarHeader implements CalendarItem {
    private final String title;

    public CalendarHeader(String title) {
        this.title = title;
    }

    @Override
    public String getTitle() {
        return title;
    }

}

public class CalendarEvent implements CalendarItem {

    private String title;
    private long timestamp;

    public CalendarEvent(String title, long timestamp) {
        this.title = title;
        this.timestamp = timestamp;
    }

    public String getTitle() {
        return title;
    }

    public long getTimestamp() {
        return timestamp;
    }

}

以及发送到适配器的一些数据的快照:

【问题讨论】:

  • 你上什么课? CalendarItem 是哪个超类? CalendarHeader 和 CalendarEvent?
  • CalendarItem 继承自CalendarHeader 吗?
  • 在您的 if (rowType == ITEM_HEADER) 中,您将 convertView 设置为 HeaderViewHolder,并使用位置 1 尝试将 HeaderViewHolder 转换为 EventViewHolder
  • 课程添加到原始帖子中。

标签: android listview android-listview android-arrayadapter android-adapter


【解决方案1】:

我认为问题不在于这个类,而在于这个类的用法。还请发布使用您的课程的代码。 (我会发表评论,但我没有足够的声誉。)

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-10-28
    • 1970-01-01
    • 2021-04-19
    • 1970-01-01
    • 2013-04-21
    • 1970-01-01
    相关资源
    最近更新 更多