CursorAdapter中的多行布局基于下一个项目

时间:2016-04-20 16:55:26

标签: android cursor android-recyclerview android-adapter

我需要在RecyclerView中显示一种访问者日志,要显示的数据来自SQLiteDatabase DESC - 由DateTime排序(enter_time列,UNIX值)。我已经实现了一个简单的CursorAdapter,我可以在RecyclerView中显示数据。

下一步是制作不同的视图,以便每天的第一个条目使用带有日期的附加标题的布局:

enter image description here

CursorAdapter内部,我实施了getItemViewType(int)方法,我需要添加逻辑,选择正确的项目类型。

逻辑很简单:

  • 如果Cursor中的下一项与当前日期相同,则选择简单布局
  • 如果Cursor中的下一项与当前日期不同,请选择带标题的布局

问题出现了:我需要检查Cursor CursorAdapter中的下一项,但显示的结果是错误的,我无法说明原因。代码看起来对我来说是正确的,但布局是随机分配给项目的。

public static class VisitorsRecyclerAdapter extends RecyclerView.Adapter<VisitorsRecyclerAdapter.VisitorViewHolder> {

    CustomCursorAdapter mCursorAdapter;
    Context mContext;
    private static final int NO_HEADER_LAYOUT = 1;
    private static final int HEADER_LAYOUT = 0;

    public VisitorsRecyclerAdapter(Context context, Cursor c) {
        mContext = context;
        mCursorAdapter = new CustomCursorAdapter(mContext, c, 0);
    }

    public static class VisitorViewHolder extends RecyclerView.ViewHolder {

        public TextView textName;
        public TextView textCats;

        public VisitorViewHolder(View v) {
            super(v);
            textName = (TextView) v.findViewById(R.id.item_visitor_name);
            textCats = (TextView) v.findViewById(R.id.item_visitor_category);
        }
    }

    public static class HeaderViewHolder extends RecyclerView.ViewHolder {

        public TextView textName;
        public TextView textCats;

        public HeaderViewHolder(View v) {
            super(v);
            textName = (TextView) v.findViewById(R.id.item_visitor_name);
            textCats = (TextView) v.findViewById(R.id.item_visitor_category);
        }
    }

    private class CustomCursorAdapter extends CursorAdapter {


        public CustomCursorAdapter(Context context, Cursor c, int flags) {
            super(context, c, flags);
        }

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

        @Override
        public int getItemViewType(int position) {

            Cursor cursor = (Cursor) mCursorAdapter.getItem(position);
            cursor.moveToNext();

            // date of current item
            Date date0 = new Date(Long.parseLong(cursor.getString(cursor.getColumnIndex(DatabaseConst.VisitorEntry.COLUMN_NAME_ENTER))) * 1000);

            if(position == -1) return HEADER_LAYOUT;
            cursor = (Cursor) mCursorAdapter.getItem(position - 1);
            cursor.moveToNext();

            // date of item that temporary comes after
            Date date1 = new Date(Long.parseLong(cursor.getString(cursor.getColumnIndex(DatabaseConst.VisitorEntry.COLUMN_NAME_ENTER))) * 1000);

            SimpleDateFormat format = new SimpleDateFormat("ddMMyyyy", Locale.ENGLISH);

            return format.format(date0).equals(format.format(date1)) ? NO_HEADER_LAYOUT : HEADER_LAYOUT;
        }

        @Override
        public View newView(final Context context, Cursor cursor, ViewGroup parent) {
            View v = null;
            int position = cursor.getPosition();
            int type = getItemViewType(position);
            RecyclerView.ViewHolder viewHolder = null;

            switch (type) {
                case HEADER_LAYOUT:
                    v = LayoutInflater.from(parent.getContext())
                            .inflate(R.layout.item_day, parent, false);
                    viewHolder = new HeaderViewHolder(v);
                    break;
                case NO_HEADER_LAYOUT:
                    v = LayoutInflater.from(parent.getContext())
                            .inflate(R.layout.item_visitor, parent, false);
                    viewHolder = new VisitorViewHolder(v);
                    break;
            }
            assert v != null;
            v.setTag(viewHolder);
            return v;
        }

        @Override
        public void bindView(View view, Context context, Cursor cursor) {
            final int viewType = getItemViewType(cursor.getPosition());

            switch (viewType) {
                case HEADER_LAYOUT:
                    HeaderViewHolder holder = (HeaderViewHolder) view.getTag();
                    holder.textName.setText(DateFormat.getDateTimeInstance().format(new Date(Long.parseLong(cursor.getString(cursor.getColumnIndex(DatabaseConst.VisitorEntry.COLUMN_NAME_ENTER))) * 1000)));
                    holder.textCats.setText(cursor.getString(cursor.getColumnIndex(DatabaseConst.VisitorEntry.COLUMN_NAME_FIRST_NAME)));
                    break;
                case NO_HEADER_LAYOUT:
                    VisitorViewHolder holder0 = (VisitorViewHolder) view.getTag();
                    holder0.textName.setText(DateFormat.getDateTimeInstance().format(new Date(Long.parseLong(cursor.getString(cursor.getColumnIndex(DatabaseConst.VisitorEntry.COLUMN_NAME_ENTER))) * 1000)));
                    holder0.textCats.setText(cursor.getString(cursor.getColumnIndex(DatabaseConst.VisitorEntry.COLUMN_NAME_FIRST_NAME)));
                    break;
            }

        }

    }

    @Override
    public int getItemCount() {
        if(mCursorAdapter == null) return 0;

        return mCursorAdapter.getCount();
    }

    @Override
    public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
        mCursorAdapter.getCursor().moveToPosition(position);
        mCursorAdapter.bindView(holder.itemView, mContext, mCursorAdapter.getCursor());
    }

    @Override
    public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        View v = mCursorAdapter.newView(mContext, mCursorAdapter.getCursor(), parent);
        RecyclerView.ViewHolder viewHolder = null;
        switch (viewType){
            case HEADER_LAYOUT:
                viewHolder = new HeaderViewHolder(v);
                break;
            case NO_HEADER_LAYOUT:
                viewHolder = new VisitorViewHolder(v);
                break;
        }

        return viewHolder;
    }
}

这是实际错误的结果:

enter image description here

我查看了SO,但是我找不到需要从相邻项目恢复数据的类似场景(我不确定这是否是问题)。你有什么建议来解决这个问题?

2 个答案:

答案 0 :(得分:1)

直到你从getItemViewType返回价值似乎是对的。您只创建了ViewHolder的单个VisitorViewHolder。  但还有另一种更好的方法来实现这一目标,创建多个ViewHolder类并将其切换为onCreateViewHolder

像这样修改,

@Override
    public VisitorViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {

    switch (viewType){
       case HEADER_LAYOUT:
        View v = mCursorAdapter.newView(mContext, mCursorAdapter.getCursor(), parent);
        final VisitorViewHolder holder = new VisitorViewHolder(v);    
        return holder;

       case NO_HEADER_LAYOUT:
              //Return you another viewholder which contains view with no header
      }
 }

请参阅here以获取更多参考信息,这将使您的任务更加轻松。

答案 1 :(得分:1)

发生了两个不同的问题。

项目回收

适配器正在回收为不同项类型而膨胀的视图,因此绑定时发生异常,因为view.getTag()包含错误的ViewHolder类型。为了解决这个问题,我将StableIds设置为false。

    @Override
    public void setHasStableIds(boolean hasStableIds) {
        super.setHasStableIds(false);
    }

类型分配逻辑

从逻辑上讲,这是正确的,但我在完成后调整了光标的位置而没有将其设置为上一个值。

        @Override
        public int getItemViewType(int pos) {
            Integer position = pos;
            // with -1 position you can return whatever you want
            if(position.equals(-1)) { return HEADER_LAYOUT; }

            Cursor cursor = mCursorAdapter.getCursor();
            if(!position.equals(0)) {
                cursor.moveToPosition(position);
                Date date0 = new Date(Long.parseLong(cursor.getString(cursor.getColumnIndex(DatabaseConst.VisitorEntry.COLUMN_NAME_ENTER))) * 1000);

                cursor.moveToPosition(position - 1);
                Date date1 = new Date(Long.parseLong(cursor.getString(cursor.getColumnIndex(DatabaseConst.VisitorEntry.COLUMN_NAME_ENTER))) * 1000);

                SimpleDateFormat format = new SimpleDateFormat("ddMMyyyy", Locale.ENGLISH);

                cursor.moveToPosition(position);
                return ((format.format(date0).equals(format.format(date1))) ? NO_HEADER_LAYOUT : HEADER_LAYOUT);
            } else {
                // position = 0 -> first item in list always need header
                cursor.moveToPosition(position); // important!
                return HEADER_LAYOUT;
            }
        }

如您所见,在从方法返回之前,我再次设置光标位置。