RecyclerView adds Header and Footer

This blog uses for reference. Qi Bin The Great God's,  The correct way to add headers to RecyclerView

I want to make a contact interface similar to Wechat. The solution I came up with is to add header and footer to recyclerView.
The results are as follows: (The interface is ugly, spray lightly)


The pit was also encountered when adding footer. At first, the getItemViewType method was modified directly to get footer type, and the onCreateViewHolder method was added to create footer's ViewHolder.
    @Override
    public int getItemViewType(int position) {
        if (mHeaderView == null) return TYPE_NORMAL;
        if (position == 0) return TYPE_HEADER;
        if (mFooterView == null) return TYPE_NORMAL;
        if (position == getItemCount() - 1) return TYPE_FOOTER;
        return TYPE_NORMAL;
    }
    @Override
    public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, final int viewType) {
        if (mFooterView != null && viewType == TYPE_FOOTER) return new ViewHolder(mFooterView);
        if (mHeaderView != null && viewType == TYPE_HEADER) return new ViewHolder(mHeaderView);
        return onCreate(parent, viewType);
    }




After making 10 false data, dragging can only see 8 data. Imagine that there must be a problem with the return value of the total getItemCount method.
    @Override
    public int getItemCount() {
        int itemCount = mDatas.size();
        if (mHeaderView != null ) {
            itemCount ++ ;
        }
        if (mFooterView != null) {
            itemCount ++;
        }
        return itemCount;
    }
After the modification, continue to run again, and the 8th Item running to recyclerView is dragged to report ClassCastException.
I haven't found the problem for a long time, but debug found that the first line of code in onBindViewHolder carried out the return of header type Item, and I think I should add it myself.
    @Override
    public void onBindViewHolder(RecyclerView.ViewHolder viewHolder, int position) {
        //Prevent ClassCastException
        if (getItemViewType(position) == TYPE_HEADER) return;
        if (getItemViewType(position) == TYPE_FOOTER) return;

        int pos = getRealPosition(viewHolder);
        T data = null;
        //Empty the data to avoid anomalies
        if (mDatas != null && mDatas.size() > 0) {
            data = mDatas.get(pos);
            onBind(viewHolder, pos, data);
        }
        if (mListener != null) {
            final T finalData = data;
            final int finalPos = pos;
            viewHolder.itemView.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    mListener.onItemClick(finalPos, finalData);
                }
            });
        }
    }
Because what I do is contacts, I need to rebind the data after adding contacts, so I add onBindData method to reassign the data of recyclerView.

The overall code is as follows:

public abstract class BaseRecyclerAdapter<T> extends RecyclerView.Adapter<RecyclerView.ViewHolder> {

    public static final int TYPE_HEADER = 0;
    public static final int TYPE_NORMAL = 1;
    public static final int TYPE_FOOTER = 2;

    public Context context;

    public List<T> mDatas;

    public BaseRecyclerAdapter(Context context, List<T> datas) {
        this.context = context;
        this.mDatas = datas;
    }

    private View mHeaderView;
    public View mFooterView;

    private OnItemClickListener mListener;

    public void setOnItemClickListener(OnItemClickListener listener) {
        mListener = listener;
    }

    /**
     * You can add headerView only once
     * @param headerView
     */
    public void setHeaderView(View headerView) {
        mHeaderView = headerView;
        notifyItemInserted(0);
    }

    public View getHeaderView() {
        return mHeaderView;
    }

    /**
     * You can add footView only once
     * @param footerView
     */
    public void setFooterView(View footerView) {
        mFooterView = footerView;
        notifyItemInserted(getItemCount());
    }

    public View getFooterView() {
        return mFooterView;
    }

    @Override
    public int getItemViewType(int position) {
        if (mHeaderView == null) return TYPE_NORMAL;
        if (position == 0) return TYPE_HEADER;
        if (mFooterView == null) return TYPE_NORMAL;
        if (position == getItemCount() - 1) return TYPE_FOOTER;
        return TYPE_NORMAL;
    }

    @Override
    public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, final int viewType) {
        if (mFooterView != null && viewType == TYPE_FOOTER) return new ViewHolder(mFooterView);
        if (mHeaderView != null && viewType == TYPE_HEADER) return new ViewHolder(mHeaderView);
        return onCreate(parent, viewType);
    }

    @Override
    public void onBindViewHolder(RecyclerView.ViewHolder viewHolder, int position) {
        //Prevent ClassCastException
        if (getItemViewType(position) == TYPE_HEADER) return;
        if (getItemViewType(position) == TYPE_FOOTER) return;

        int pos = getRealPosition(viewHolder);
        T data = null;
        //Empty the data to avoid anomalies
        if (mDatas != null && mDatas.size() > 0) {
            data = mDatas.get(pos);
            onBind(viewHolder, pos, data);
        }
        if (mListener != null) {
            final T finalData = data;
            final int finalPos = pos;
            viewHolder.itemView.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    mListener.onItemClick(finalPos, finalData);
                }
            });
        }
    }

    @Override
    public void onAttachedToRecyclerView(RecyclerView recyclerView) {
        super.onAttachedToRecyclerView(recyclerView);

        RecyclerView.LayoutManager manager = recyclerView.getLayoutManager();
        if (manager instanceof GridLayoutManager) {
            final GridLayoutManager gridManager = ((GridLayoutManager) manager);
            gridManager.setSpanSizeLookup(new GridLayoutManager.SpanSizeLookup() {
                @Override
                public int getSpanSize(int position) {
                    return getItemViewType(position) == TYPE_HEADER
                            ? gridManager.getSpanCount() : 1;
                }
            });
        }
    }

    @Override
    public void onViewAttachedToWindow(RecyclerView.ViewHolder viewHolder) {
        super.onViewAttachedToWindow(viewHolder);
        ViewGroup.LayoutParams lp = viewHolder.itemView.getLayoutParams();
        if (lp != null
                && lp instanceof StaggeredGridLayoutManager.LayoutParams) {
            StaggeredGridLayoutManager.LayoutParams p = (StaggeredGridLayoutManager.LayoutParams) lp;
            p.setFullSpan(viewHolder.getLayoutPosition() == 0);
        }
    }

    /**
     * Get the real position
     * @param holder
     * @return
     */
    public int getRealPosition(RecyclerView.ViewHolder holder) {
        int position = holder.getLayoutPosition();
        return mHeaderView == null ? position : position - 1;
    }
    /**
     * Get the total number of item s in recyclerView
     */
    @Override
    public int getItemCount() {
        int itemCount = mDatas.size();
        if (mHeaderView != null ) {
            itemCount ++ ;
        }
        if (mFooterView != null) {
            itemCount ++;
        }
        return itemCount;
    }
    /**
     * Get the total number of real data in recyclerView
     */
    public int getRealItemCount(){
        return mDatas.size();
    }

    public abstract RecyclerView.ViewHolder onCreate(ViewGroup parent, final int viewType);

    public abstract void onBind(RecyclerView.ViewHolder viewHolder, int realPosition, T data);

    public abstract void bindDatas(List<T> allNewFriend);

    public class ViewHolder extends RecyclerView.ViewHolder {
        public ViewHolder(View itemView) {
            super(itemView);
        }
    }

    public interface OnItemClickListener<T> {
        void onItemClick(int position, T data);
    }
}
Usage method:
public class AddressListPagerAdapter extends BaseRecyclerAdapter<Friend> {

    public AddressListPagerAdapter(Context context, List<Friend> datas) {
        super(context, datas);
    }

    @Override
    public RecyclerView.ViewHolder onCreate(ViewGroup parent, int viewType) {
        View itemView = LayoutInflater.from(parent.getContext()).inflate(R.layout.address_list_item, parent, false);
        return new ViewHolder(itemView);
    }

    @Override
    public void onBind(RecyclerView.ViewHolder viewHolder, int realPosition, Friend data) {
        ((ViewHolder) viewHolder).tv_friend_name.setText(mDatas.get(realPosition).getUsername());
        RequestOptions requestOptions = new RequestOptions()
                .placeholder(R.drawable.head)
                .error(R.drawable.default_head);
        Glide.with(context).load(mDatas.get(realPosition).getHeaderImage())
                .apply(requestOptions)
                .into(((ViewHolder) viewHolder).iv_head_portrait);
    }

    @Override
    public void bindDatas(List<Friend> allNewFriend) {
        mDatas = allNewFriend;
    }

    public class ViewHolder extends BaseRecyclerAdapter.ViewHolder {

        private ImageView iv_head_portrait;
        private TextView tv_friend_name;

        public ViewHolder(View itemView) {
            super(itemView);
            iv_head_portrait = (ImageView) itemView.findViewById(R.id.iv_head_portrait);
            tv_friend_name = (TextView) itemView.findViewById(R.id.tv_friend_name);
        }
    }
}
Adding Header and Footer direct calls, setHeaderView and setFooterView methods can be used. Header and footer click events are not processed in recyclerView, they need to be set up by themselves. Personally, it is better to handle the click events separated from the header and footer of recyclerView. Personal views may be a bit one-sided, but it should be analyzed in detail.
The only pity is that viewHolder needs to be forced to bind data in onBind. In addition, such a method can only add a header and a footer, if you want to add more, you can add the corresponding view to the header and footer. If you still can't meet your needs, you can learn from it. The Method of Hongyang-Dashen

Posted by tapupartforpres on Sat, 08 Jun 2019 14:33:06 -0700