RecyclerView 添加头部和尾部,并实现Item的点击事件

来源:互联网 发布:劈叉的影响身高吗 知乎 编辑:程序博客网 时间:2024/05/17 23:39

RecyclerView是Android中新加入的控件,用来替换ListView和GridView,但在使用时也存在一些不如意的地方,没有Item的点击事件,不能添加头部和尾部,本文就此来实现这些功能:
1.我们先看看ListView是如何添加头部和尾部的,

public void addHeaderView(View v, Object data, boolean isSelectable) {        final FixedViewInfo info = new FixedViewInfo();        info.view = v;        info.data = data;        info.isSelectable = isSelectable;        mHeaderViewInfos.add(info);        mAreAllItemsSelectable &= isSelectable;        // Wrap the adapter if it wasn't already wrapped.        if (mAdapter != null) {            if (!(mAdapter instanceof HeaderViewListAdapter)) {                mAdapter = new HeaderViewListAdapter(mHeaderViewInfos, mFooterViewInfos, mAdapter);            }            // In the case of re-adding a header view, or adding one later on,            // we need to notify the observer.            if (mDataSetObserver != null) {                mDataSetObserver.onChanged();            }        }    }

从这里发现是因为创建了一个HeaderViewListAdapter,打开该类发现其实是在getView时是通过位置判断分别返回不同的Item:

 public View getView(int position, View convertView, ViewGroup parent) {        // Header (negative positions will throw an IndexOutOfBoundsException)        int numHeaders = getHeadersCount();        if (position < numHeaders) {            return mHeaderViewInfos.get(position).view;        }        // Adapter        final int adjPosition = position - numHeaders;        int adapterCount = 0;        if (mAdapter != null) {            adapterCount = mAdapter.getCount();            if (adjPosition < adapterCount) {                return mAdapter.getView(adjPosition, convertView, parent);            }        }        // Footer (off-limits positions will throw an IndexOutOfBoundsException)        return mFooterViewInfos.get(adjPosition - adapterCount).view;    }

由此我们仿照它写一个HeaderViewRecyclerAdapter:

public class HeaderViewRecyclerAdapter extends RecyclerView.Adapter {    private final RecyclerView.Adapter mAdapter;    // These two ArrayList are assumed to NOT be null.    // They are indeed created when declared in ListView and then shared.    ArrayList<View> mHeaderViews;    ArrayList<View> mFooterViews;    // Used as a placeholder in case the provided info views are indeed null.    // Currently only used by some CTS tests, which may be removed.    static final ArrayList<View> EMPTY_INFO_LIST = new ArrayList<>();    private int headerPosition;    private int footerPosition;    public HeaderViewRecyclerAdapter(ArrayList<View> headerViews, ArrayList<View> footerViews,                                     RecyclerView.Adapter adapter,                                     RecyclerView.LayoutManager layoutManager) {        if (null == adapter){            throw new NullPointerException("Adapter is not null");        }        this.mAdapter = adapter;        if (headerViews == null) {            this.mHeaderViews = EMPTY_INFO_LIST;        } else {            this.mHeaderViews = headerViews;        }        if (footerViews == null) {            this.mFooterViews = EMPTY_INFO_LIST;        } else {            this.mFooterViews = footerViews;        }        setFullSpan(layoutManager);    }    private void setFullSpan(RecyclerView.LayoutManager layoutManager) {        if (layoutManager instanceof GridLayoutManager) {            ((GridLayoutManager) layoutManager).setSpanSizeLookup(                    new HeaderSpanSizeLookup((GridLayoutManager) layoutManager));        }    }    public int getHeadersCount() {        return mHeaderViews.size();    }    public int getFootersCount() {        return mFooterViews.size();    }    public boolean removeHeader(View v) {        for (int i = 0; i < mHeaderViews.size(); i++) {            View view = mHeaderViews.get(i);            if (view == v) {                mHeaderViews.remove(i);                return true;            }        }        return false;    }    public boolean removeFooter(View v) {        for (int i = 0; i < mFooterViews.size(); i++) {            View view = mFooterViews.get(i);            if (view == v) {                mFooterViews.remove(i);                return true;            }        }        return false;    }    @Override    public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {        if (viewType == RecyclerView.INVALID_TYPE - 1) {            return new HeaderViewHolder(mHeaderViews.get(headerPosition++));        } else if (viewType == RecyclerView.INVALID_TYPE - 2) {            return new HeaderViewHolder(mFooterViews.get(0));        }        return mAdapter.onCreateViewHolder(parent, viewType);    }    @Override    public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {        int numHeaders = getHeadersCount();        if (position < numHeaders) {            return;        }        int adjPosition = position - numHeaders;        int adapterCount;        if (mAdapter != null) {            adapterCount = mAdapter.getItemCount();            if (adjPosition < adapterCount) {                mAdapter.onBindViewHolder(holder, adjPosition);            }        }    }    @Override    public void onViewAttachedToWindow(RecyclerView.ViewHolder holder) {        super.onViewAttachedToWindow(holder);        int numHeaders = getHeadersCount();        int numItems = mAdapter.getItemCount();        ViewGroup.LayoutParams lparams = holder.itemView.getLayoutParams();        if(lparams != null                && lparams instanceof StaggeredGridLayoutManager.LayoutParams                && (holder.getLayoutPosition() < numHeaders || holder.getLayoutPosition() >= numItems + numHeaders)) {            StaggeredGridLayoutManager.LayoutParams p = (StaggeredGridLayoutManager.LayoutParams) lparams;            p.setFullSpan(true);        }    }    @Override    public int getItemCount() {        if (mAdapter != null) {            return getHeadersCount() + getFootersCount() + mAdapter.getItemCount();        } else {            return getHeadersCount() + getFootersCount();        }    }    @Override    public int getItemViewType(int position) {        int numHeaders = getHeadersCount();        if (position < numHeaders) {            return RecyclerView.INVALID_TYPE - 1;        } else if (position >= numHeaders + mAdapter.getItemCount()) {            return RecyclerView.INVALID_TYPE - 2;        } else {            int adjPosition = position - numHeaders;            return mAdapter.getItemViewType(adjPosition);        }    }    @Override    public long getItemId(int position) {        int numHeaders = getHeadersCount();        if (mAdapter != null && position >= numHeaders) {            int adjPosition = position - numHeaders;            int adapterCount = mAdapter.getItemCount();            if (adjPosition < adapterCount) {                return mAdapter.getItemId(adjPosition);            }        }        return -1;    }    public RecyclerView.Adapter getRecyclerAdapter() {        return mAdapter;    }    @Override    public void unregisterAdapterDataObserver(RecyclerView.AdapterDataObserver observer) {        super.unregisterAdapterDataObserver(observer);    }    @Override    public void registerAdapterDataObserver(RecyclerView.AdapterDataObserver observer) {        super.registerAdapterDataObserver(observer);    }    private class HeaderViewHolder extends RecyclerView.ViewHolder {        public HeaderViewHolder(View itemView) {            super(itemView);        }    }    public class HeaderSpanSizeLookup extends GridLayoutManager.SpanSizeLookup {        private final GridLayoutManager layoutManager;        public HeaderSpanSizeLookup(GridLayoutManager layoutManager) {            this.layoutManager = layoutManager;        }        @Override        public int getSpanSize(int position) {            position = position < mHeaderViews.size() ||                    position > getItemCount() - mHeaderViews.size() ? layoutManager.getSpanCount() :                    1;            return position;        }    }

LinearLayoutManager并不需要特殊处理,但GridLayoutManager需要利用其setSpanSizeLookup()方法设置Item的跨度, 我们自定义一个SpanSizeLookup 来通过位置判断返回Item的跨度值;StaggeredGridLayoutManager没有对Item设置跨度,但提供setFullSpan()方法,只需要在onViewAttachedToWindow()方法中重新设置LayoutParams就可以了。

为了方便使用,我们再重写一下RecyclerView,依旧模仿List的代码:

public class CustomRecyclerView extends RecyclerView {    public ArrayList<View> mHeaderViews = new ArrayList<>();    public ArrayList<View> mFooterViews = new ArrayList<>();    //添加Adapter    public Adapter mAdapter;    public CustomRecyclerView(Context context, AttributeSet attrs, int defStyle) {        super(context, attrs, defStyle);    }    public CustomRecyclerView(Context context, AttributeSet attrs) {        super(context, attrs);    }    public CustomRecyclerView(Context context) {        super(context);    }    @Override    public void setLayoutManager(LayoutManager layout) {        super.setLayoutManager(layout);    }    public void addHeaderView(View view) {        mHeaderViews.add(view);        if (mAdapter != null) {            if (!(mAdapter instanceof HeaderViewRecyclerAdapter)) {                mAdapter = new HeaderViewRecyclerAdapter(mHeaderViews, mFooterViews, mAdapter,getLayoutManager());            }        }    }    public void addFooterView(View view) {        mFooterViews.add(view);        if (mAdapter != null) {            if (!(mAdapter instanceof HeaderViewRecyclerAdapter)) {                mAdapter = new HeaderViewRecyclerAdapter(mHeaderViews, mFooterViews, mAdapter,getLayoutManager());            }        }    }    public void setAdapter(Adapter adapter) {        if (mHeaderViews.isEmpty() && mFooterViews.isEmpty()) {            super.setAdapter(adapter);        } else {            adapter = new HeaderViewRecyclerAdapter(mHeaderViews, mFooterViews, adapter,getLayoutManager());            super.setAdapter(adapter);        }        mAdapter = adapter;    }}

这样就可以使RecyclerView添加头部和尾部了。
那如何实现Item的点击事件呢?,我们写个基类RecyclerAdapter,使用时继承它,主要是在其中实现Item的点击事件,然后将接口暴漏出来:

public abstract class BaseRecyclerAdapter<T> extends RecyclerView.Adapter<RecyclerView.ViewHolder> {    public ArrayList<T> objects;    public BaseRecyclerAdapter(ArrayList<T> objects){        this.objects = objects;    }    @Override    public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {        return onCreateVH(parent,viewType);    }    @Override    public void onBindViewHolder(final RecyclerView.ViewHolder holder, final int position) {        holder.itemView.setOnClickListener(new View.OnClickListener() {            @Override public void onClick(View v) {                if (null == onItemClickListener) return;                onItemClickListener.onItemClick(position,holder.itemView);            }        });        onBindVH(holder,position);    }    public abstract RecyclerView.ViewHolder onCreateVH(ViewGroup parent, int viewType);    public abstract void onBindVH(RecyclerView.ViewHolder holder,int position);    @Override    public int getItemCount() {        return objects.size();    }    private OnItemClickListener onItemClickListener;    public void setOnItemClickListener(OnItemClickListener onItemClickListener){        this.onItemClickListener = onItemClickListener;    }    public interface OnItemClickListener{        public void onItemClick(int position, View itemView);    }}

测试:

public class GridFragment extends BaseFragment {    private CustomRecyclerView mRecyclerView;    private MyAdapter mMyAdapter;    private ArrayList<String> mObjectList = new ArrayList<String>();    public static GridFragment newInstance() {        GridFragment fragment = new GridFragment();        return fragment;    }    @Override    public void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        if (getArguments() != null) {        }    }    @Override    public View onCreateView(LayoutInflater inflater, ViewGroup container,                             Bundle savedInstanceState) {        return inflater.inflate(R.layout.fragment, container, false);    }    @Override    public void onViewCreated(View view, Bundle savedInstanceState) {        super.onViewCreated(view, savedInstanceState);        // Inflate the layout for this fragment        mRecyclerView = (CustomRecyclerView) view.findViewById(R.id.recyclerview);        setupRecyclerView();        requestData();    }    private void setupRecyclerView() {        GridLayoutManager manager = new GridLayoutManager(context, 2);        mRecyclerView.setLayoutManager(manager);        mMyAdapter = new MyAdapter(mObjectList);        mMyAdapter.setOnItemClickListener(new BaseRecyclerAdapter.OnItemClickListener() {            @Override            public void onItemClick(int position, View itemView) {                Toast.makeText(context, position + "", Toast.LENGTH_SHORT).show();            }        });        ImageView imageView1 = new ImageView(context);        imageView1.setImageResource(R.drawable.one);        imageView1.setScaleType(ImageView.ScaleType.CENTER_CROP);        imageView1.setLayoutParams(                new LinearLayoutCompat.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,                        ViewGroup.LayoutParams.WRAP_CONTENT));        ImageView imageView2 = new ImageView(context);        imageView2.setImageResource(R.drawable.one);        imageView2.setScaleType(ImageView.ScaleType.CENTER_CROP);        imageView2.setLayoutParams(                new LinearLayoutCompat.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,                        ViewGroup.LayoutParams.WRAP_CONTENT));        ImageView imageView3 = new ImageView(context);        imageView3.setImageResource(R.drawable.one);        imageView3.setScaleType(ImageView.ScaleType.CENTER_CROP);        imageView3.setLayoutParams(                new LinearLayoutCompat.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,                        ViewGroup.LayoutParams.WRAP_CONTENT));        mRecyclerView.addHeaderView(imageView1);        mRecyclerView.addHeaderView(imageView2);        mRecyclerView.addFooterView(imageView3);        mRecyclerView.setAdapter(mMyAdapter);    }    protected void requestData() {        handler.sendEmptyMessageDelayed(1, 1000);    }    private Handler handler = new Handler() {        @Override        public void handleMessage(Message msg) {            super.handleMessage(msg);            ArrayList<String> list = new ArrayList<String>();            for (int i = 0; i < 20; i++) {                list.add(i + "");            }            mObjectList.addAll(list);            mRecyclerView.getAdapter().notifyDataSetChanged();            //    sendEmptyMessageDelayed(1, 5000);        }    };    private class MyAdapter extends BaseRecyclerAdapter<String> {        public MyAdapter(ArrayList<String> objects) {            super(objects);        }        @Override        public RecyclerView.ViewHolder onCreateVH(ViewGroup parent, int viewType) {            View view = LayoutInflater.from(parent.getContext())                    .inflate(R.layout.item_list, parent, false);            return new MyViewHolder(view);        }        @Override        public void onBindVH(RecyclerView.ViewHolder holder, int position) {        }    }    private class MyViewHolder extends RecyclerView.ViewHolder {        private ImageView imageView;        public MyViewHolder(View itemView) {            super(itemView);        }    }}

注意:目前可以添加多个头部,但尾部只能添加一个,Adapter刷新数据时,请调用recyclerView.getAdapter().notifyDataSetChanged()。

下载地址:
https://github.com/piratecode/RecyclerViewProject

0 0
原创粉丝点击