RecyclerView初步使用(非常详细)

开篇

放假回来,闲来无事,继续上次的讲解
这次对RecyclerView的讲解差不多可以满足大部分需求
对RecyclerView的简单介绍在上一篇 传送门
保证干货满满
翻了一下上次的文章,感觉篇幅过于长
这次尽量控制到5k字内
开始

添加头

在上篇结尾时,加了一张这个图
在这里插入图片描述

原理

这个游戏广告是怎么做到独占一行的
去到RecyclerView的源码中,ctrl+f搜索 notifyItem,往下翻可以看到几行注释

 /** *
 	* @see #notifyItemChanged(int)
    * @see #notifyItemInserted(int)
    * @see #notifyItemRemoved(int)
	* @see #notifyItemRangeChanged(int, int)
	* @see #notifyItemRangeInserted(int, int)
  	* @see #notifyItemRangeRemoved(int, int)
  	* */

前三个方法分别是
修改item,新增item,删除item
后三个就是批量修改,批量新增,批量删除
方法里写的都是for循环,比如

 public void notifyChanged() {
    
    
            // since onChanged() is implemented by the app, it could do anything, including
            // removing itself from {@link mObservers} - and that could cause problems if
            // an iterator is used on the ArrayList {@link mObservers}.
            // to avoid such problems, just march thru the list in the reverse order.
            for (int i = mObservers.size() - 1; i >= 0; i--) {
    
    
                mObservers.get(i).onChanged();
            }
        }

for循环里,get一个集合里index为i的对象,这个对象为适配器观察器
Goolean对这个观察者的解释为

 /**
     * Observer base class for watching changes to an {@link Adapter}.
     * See {@link Adapter#registerAdapterDataObserver(AdapterDataObserver)}.
     */

观察者基类,用于观察Adapter的改变
对于观察者的其他解释,阅读菜鸟教程对此的解释观察者模式
在这里只需要知道这个方法的作用是,当这个mObservers改变时,所有依赖于它的对象都得到通知并被自动更新。
这也就解释了,为什么搜索RecyclerViews刷新时,最常见的是用
adapter.notifyDataSetChanged()方法

public final void notifyDataSetChanged() {
    
    
            mObservable.notifyChanged();
        }

 public void notifyChanged() {
    
    
            // since onChanged() is implemented by the app, it could do anything, including
            // removing itself from {@link mObservers} - and that could cause problems if
            // an iterator is used on the ArrayList {@link mObservers}.
            // to avoid such problems, just march thru the list in the reverse order.
            for (int i = mObservers.size() - 1; i >= 0; i--) {
    
    
                mObservers.get(i).onChanged();
            }
        }

总结 : 可以更改mObservable来实现一系列增删刷改

实现步骤

关联外部

首先在自己的Adapter中加一个添加头的方法,供Activity调用

    private View mHeaderView;
 	public void setHeaderView(View headerView) {
    
    
        mHeaderView = headerView;
        notifyItemInserted(0);
    }

其次,给这个view设置一个标识

 	public static final int HEAD = 1;//头类型
    public static final int ITEM = 0;//普通item类型

创建 onCreateViewHolder()

上期说过的onCreateViewHolder()
返回的Holder,这些都是item的布局
头部和item不一样,这时就要返回不一样的Holder
同时更改这个viewType的值

 	 /**
     * 创建布局
     */
    @NonNull
    @Override
    public Holder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
    
    
        //如果添加了头并且类型是头,返回头
        if (mHeaderView != null && viewType == HEAD) return new Holder(mHeaderView);
        //创建ViewHolder,返回每一项的布局
        butView = LayoutInflater.from(context).inflate(R.layout.recy_list2, parent, false);
        RecyclerAdapter3.Holder butViewHolder = new RecyclerAdapter3.Holder(butView);
        butView.setOnClickListener(this);
        return butViewHolder;
    }

创建 onBindViewHolder()

上期也说过onBindViewHolder()传来的两个参数分别是绑定时的Holder和下标position
既然加了头,那position就需要改变
如果添加了头,就要把position-1,否则就会出现普通item第一个的position就会变成1,而头的position就会变成0,对接下来的pos判断造成误差
至于为什么return,因为在上面的setHeaderView就要对头进行处理,挖坑!!

	/**
     * 绑定控件
     */
    @Override
    public void onBindViewHolder(@NonNull Holder holder, int position) {
    
    
        int pos = getRealPosition(holder);
        if (getItemViewType(position) == HEAD) return;
        //将数据和控件绑定
        ....
    }

	public int getRealPosition(RecyclerView.ViewHolder holder) {
    
    
        int position = holder.getLayoutPosition();
        return mHeaderView == null ? position : position - 1;
    }
   

创建 getItemCount()

既然对pos做了处理,那Count也不能落下
需要注意的是,pos是下标,是RecyclerView计算都得出的
Count是数量,是自己定义的
为了方便阅读,我这里不传入list,而是传入一个num,决定显示的item个数

	/**
     * 返回数量
     */
    @Override
    public int getItemCount() {
    
    
        return  mHeaderView == null ? num : num + 1;
    }

绑定类型

创建方法,重写自RecyclerView
对于这个方法,官方给出的解释为(来自谷歌翻译)

返回该项目的视图类型,以用于视图回收。
该方法的默认实现返回0,并假设适配器的单一视图类型。
与ListView适配器不同,类型不需要是连续的。
考虑使用ID资源来唯一标识项目视图类型。

 /**
         * Return the view type of the item at <code>position</code> for the purposes
         * of view recycling.
         *
         * <p>The default implementation of this method returns 0, making the assumption of
         * a single view type for the adapter. Unlike ListView adapters, types need not
         * be contiguous. Consider using id resources to uniquely identify item view types.
         *
         * @param position position to query
         * @return integer value identifying the type of the view needed to represent the item at
         *                 <code>position</code>. Type codes need not be contiguous.
         */
        public int getItemViewType(int position) {
    
    
            return 0;
        }

大致意思就是默认0.可以通过这个方法拿到item的类型
最重要的是,它传来个position

    /**
     * 设置item类型
     */
    @Override
    public int getItemViewType(int position) {
    
    
        //判断是否添加了头
        if (mHeaderView == null) {
    
    
            return ITEM;
        }
        //添加了头,在判断pos是不是0
        //0是第一个item,返回HEAD
        //如果要放其他位置,可以根据其他条件判断后返回
        if (position == 0) {
    
    
            return HEAD;
        }
        //添加了头,可pos不是0,那剩下的都是item
        return ITEM;
    }

细节处理

最后是内部类Holder,要在绑定控件时判断

/**
     * 内部类
     */
    public class Holder extends RecyclerView.ViewHolder {
    
    
        ImageView head;
        LinearLayout layout;

        public Holder(View itemView) {
    
    
            super(itemView);
            //判断
            if (itemView == mHeaderView) return;
            head = itemView.findViewById(R.id.head);
            layout = itemView.findViewById(R.id.layout);
        }
    }

使用

准备工作到此结束
在Activity中使用setHeader方法即可添加头

onCreate中


    private RecyclerView recycler;
    private RecyclerAdapter3 adapter;
    
 	@Override
    protected void onCreate(Bundle savedInstanceState) {
    
    
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
 		recycler = findViewById(R.id.recycler);
 		//传入context和需要显示的条数
 		adapter = new RecyclerAdapter3(this,4);
        //表格布局
        GridLayoutManager manager = new GridLayoutManager(this,2);
        //设置布局
        recycler.setLayoutManager(manager);
        //设置动画
        recycler.setItemAnimator(new DefaultItemAnimator());
        //设置适配器
        recycler.setAdapter(adapter);
        //添加头
        setHeader(recycler);
    }

添加头单独写一个方法
使用上期说的LayoutInflater创建布局
顺带对布局的控件进行修改
(填坑)
这就是为什么挖坑处要return,这里可以独自处理头
又或者你不想独自处理,也可以和item一起处理

	private void setHeader(RecyclerView view) {
    
    
		//创建view,必要
        View header = LayoutInflater.from(context).inflate(R.layout.review_one_inf, view, false);
        //绑定控件,非必要
        TextView text = header.findViewById(R.id.text);
        ImageView img = header.findViewById(R.id.img);
        //修改控件显示数据,非必要
        text.setText(headName);
        Picasso.get().load(headCover).into(img);
        //头部点击事件,非必要
        header.setOnClickListener(new View.OnClickListener() {
    
    
            @Override
            public void onClick(View v) {
    
    
               //点击事件
            }
        });
        //执行adapter的setHeaderView,传入处理好的头,必要
        adapter.setHeaderView(header);
    }

至于怎么把头部点击事件一起放入item点击事件
或将头和item融合在一起写,动手实验一下即可

实现图

在这里插入图片描述

结尾

这次详细写了一下简单的RecyclerView添加头方法
继续徜徉于码海中,要等发现新的有趣知识点再写了
如果有什么有趣的知识点,想我详细描述,欢迎留言或私信

最后
对本文有任何意见或疑问,或者认为其中说法有误,欢迎在评论区留言!!!
转载请注明出处!!

猜你喜欢

转载自blog.csdn.net/weixin_47311938/article/details/116304786