UI控件之RecyclerView

一、RecyclerView的基本概念
1、RecyclerView控件是ListView的增强版,不仅可以轻松实现和ListView同样的效果,还优化了ListView中存在的不足之处。
2、不过相比于ListView,RecyclerView也有一定的缺点:设置列表的分割线时需要自定义;列表的点击事件需要自己去实现
二、 RecyclerView的基本用法
1、导入RecyclerView的依赖 implementation 'com.android.support:recyclerview-v7:28.0.0'
2、同ListView一样,创建一个实体Bean类,并且为子项定义一个布局文件

<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content">

    <TextView
        android:id="@+id/name"
        android:layout_width="match_parent"
        android:layout_height="100px"
        android:gravity="center"
        android:layout_margin="10px"
        />
</LinearLayout>

3、在我们想要的布局文件中加入RecyclerView控件,代码如下:

		     <android.support.v7.widget.RecyclerView
		        android:id="@+id/list_name"
		        android:layout_width="match_parent"
		        android:layout_height="match_parent"
		        tools:ignore="MissingConstraints" />

4、自定义适配器

		public class SoccerAdapter extends RecyclerView.Adapter<SoccerAdapter.ViewHolder> {
    
    
		    private List<Soccer> soccerList;
		
		    static class ViewHolder extends RecyclerView.ViewHolder{
    
    
		        TextView name;
		        public ViewHolder(@NonNull View itemView) {
    
    
		            super(itemView);
		            name = itemView.findViewById(R.id.name);
		        }
		    }
		    public SoccerAdapter(List<Soccer> soccerList){
    
    
		        this.soccerList = soccerList;
		    }
		    @NonNull
		    @Override
		    public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
    
    
		        View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.name_item,parent,false);
		        ViewHolder viewHolder = new ViewHolder(view);
		        return viewHolder;
		    }
		
		    @Override
		    public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
    
    
		        Soccer soccer = soccerList.get(position);
		        holder.name.setText(soccer.getName());
		
		    }
		
		    @Override
		    public int getItemCount() {
    
    
		        return soccerList.size();
		    }
		   	public void removeData(int position){
    
    
        		soccerList.remove(position);
     		    notifyItemRemoved(position);//删除元素
    		}
    	}
1、首先我们定义了一个内部类ViewHolder,并让他继承RecyclerView.ViewHolder类
2、然后在构造函数中传入一个View参数,这个参数通常就是RecyclerView子项的最外层布局
3、获取控件的实例
4、SoccerAdapter类中的构造函数用于把要展示的数据源传进来,并赋予给一个全局变量List集合,以便于后续调用
5、三个被重写的方法中第一个方法用于创建ViewHolder的实例
6、第二个方法用于对RecyclerView的子项的数据源进行赋值
7、第三个返回数据源长度
8、该适配器要继承自 RecyclerView.Adapter 并且 泛型指定为 SoccerAdapter.ViewHolder

5、修改MainActivity中的代码:

		public class MainActivity extends AppCompatActivity {
    
    
		    List<Soccer> soccerList = new ArrayList<>();
		    @Override
		    protected void onCreate(Bundle savedInstanceState) {
    
    
		        super.onCreate(savedInstanceState);
		        setContentView(R.layout.activity_main);
		        initName();
		        RecyclerView recyclerView = findViewById(R.id.list_name);
		        LinearLayoutManager linearLayout = new LinearLayoutManager(this);
		        recyclerView.setLayoutManager(linearLayout);
		        SoccerAdapter soccerAdapter = new SoccerAdapter(soccerList);
		        recyclerView.setAdapter(soccerAdapter);
		
		    }
		     public void initName() {
    
    
				.......
			 }
		 }
	1、在onCreate()方法中我们先获取到RecyclerView的实例
	2、然后创建了一个LinearLayoutManager对象,并将他设置到RecyclerView当中
	3、LinearLayoutManager用于指定RecyclerView的布局方式,这里指定的是线性布局的方式,此外还有滚动和瀑布流布局
	4、创建SoccerAdapter实例,并将数据传入到其构造函数当中
	5、调用recyclerView的setAdapter()方法完成适配器的设置

运行结果:
在这里插入图片描述
三、设置分割线
核心方法:

public class DividerItemDecoration extends RecyclerView.ItemDecoration {
    
    
    private static final int[] ATTRS = new int[]{
    
    
            android.R.attr.listDivider
    };
    public static final int HORIZONTAL_LIST = LinearLayoutManager.HORIZONTAL;
    public static final int VERTICAL_LIST = LinearLayoutManager.VERTICAL;
    private Drawable divider;
    private int orientation;

    public DividerItemDecoration(Context context,int orientation){
    
    
        final TypedArray a = context.obtainStyledAttributes(ATTRS);
        divider = a.getDrawable(0);
        a.recycle();
        setOrientation(orientation);
    }

    //获取RecyclerView的排列方式
    public void setOrientation(int orientation){
    
    
        if(orientation != HORIZONTAL_LIST && orientation != VERTICAL_LIST){
    
    
            throw new IllegalArgumentException("非法排列方式");
        }
        this.orientation = orientation;
    }

    //核心方法
    @Override
    public void onDraw(@NonNull Canvas c, @NonNull RecyclerView parent, @NonNull RecyclerView.State state) {
    
    
        if (orientation == VERTICAL_LIST){
    
    
            drawVertical(c,parent);
        }else {
    
    
            drawHorizontal(c,parent);
        }
    }

    public void drawVertical(Canvas c,RecyclerView parent){
    
    
        final int left = parent.getPaddingLeft();
        final int right = parent.getWidth() - parent.getPaddingRight();
        final int childCount = parent.getChildCount();
        for (int i=0;i<childCount;i++){
    
    
            final View child = parent.getChildAt(i);
//            RecyclerView v = new RecyclerView(parent.getContext());
            final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child.getLayoutParams();
            final int top = child.getBottom() + params.bottomMargin;
            final int bottom = top + divider.getIntrinsicHeight();
            divider.setBounds(left,top,right,bottom);
            divider.draw(c);
        }
    }
    public void drawHorizontal(Canvas c,RecyclerView parent){
    
    
        final int top = parent.getPaddingTop();
        final int bottom = parent.getHeight() - parent.getPaddingBottom();
        final int childCount = parent.getChildCount();
        for (int i=0;i<childCount;i++){
    
    
            final View child = parent.getChildAt(i);
            final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child.getLayoutParams();
            final int left = child.getRight() + params.rightMargin;
            final int right = left + divider.getIntrinsicHeight();
            divider.setBounds(left,top,right,bottom);
            divider.draw(c);
        }
    }

    //用于设置item的padding属性
    @Override
    public void getItemOffsets(@NonNull Rect outRect, @NonNull View view, @NonNull RecyclerView parent, @NonNull RecyclerView.State state) {
    
    
        if (orientation == VERTICAL_LIST){
    
    
            outRect.set(0,0,0,divider.getIntrinsicHeight());
        }else {
    
    
            outRect.set(0,0,divider.getIntrinsicHeight(),0);
        }
    }
}

接着在适配器传入集合数据前调用分割线绘图即可

    recyclerView.addItemDecoration(new DividerItemDecoration(MainActivity.this,DividerItemDecoration.VERTICAL_LIST));

四、 实现横向滚动和瀑布流布局
(一)如何实现横向滚动?

  • 1、首先要对子项布局进行修改,将子项里的元素的排列方式改成垂直排列,并且设置布局的宽度
  • 2、接下来我们修改MainActivity中的代码,改变布局排列方向,在声明了LinearLayoutManager对象后加入如下代码
 	linearLayout.setOrientation(LinearLayoutManager.HORIZONTAL);
  • 3、为什么RecyclerView能够如此轻松实现这种布局而ListView几乎不能实现?——因为前者继承了LayoutManager接口,将工作交给了这个接口,子类只要按照接口的规范来实现,这样就能实现多种样式;而后者的布局排列是由自身去管理的。
  • 4、除此之外,我们还可以实现网格布局(GridLayoutManager)和瀑布流布局(StaggeredGridLayoutManager)

(二)如何实现瀑布流布局?

  • 1、首先要对子项布局进行修改,将布局的宽度设置为match_parent
  • 2、修改MainActivity中的代码
        StaggeredGridLayoutManager gridLayoutManager = new StaggeredGridLayoutManager(3,StaggeredGridLayoutManager.VERTICAL);
	其中,第一个参数 3 表示是列数,第二个参数表示排列方式,上面是垂直排列

在这里插入图片描述
四、 RecyclerView的点击事件
与ListView不同的是,RecyclerView需要自己给子项具体的View去注册点击事件
(一)最基础的注册方法如下:

	static class ViewHolder extends RecyclerView.ViewHolder{
    
    
	        View soccerView;
	        
	        TextView name;
	        public ViewHolder(@NonNull View itemView) {
    
    
	            super(itemView);
	            
	            soccerView = itemView;
	            
	            name = itemView.findViewById(R.id.name);
	        }
	    }
	
	    @NonNull
	    @Override
	    public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
    
    
	        View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.name_item,parent,false);
	        
	        final ViewHolder viewHolder = new ViewHolder(view);
	        viewHolder.soccerView.setOnClickListener(new View.OnClickListener() {
    
    
	            @Override
	            public void onClick(View view) {
    
    
	                //具体的逻辑
	            }
	        });
	        return viewHolder;
	    }
	1、在ViewHolder添加一个soccerView变量用于保存子项最外层布局的实例
	2、然后在onCreateViewHolder()方法中声明一个final类型的ViewHolder实例,然后注册监听器
	3、RecyclerView点击事件优势:可以为整个子项注册监听器,如上述代码的soccerView,也可以为子项中的单个元素注册监听器,如直接把上面代码的 soccerView 更改成 name,即为TextView元素注册了一个监听器

(二) 更高级的点击事件和长按事件

  • (1)首先在适配器中定义两个回调接口,分别为点击事件和长按事件的回调接口,并实现set方法已获取实例
    //定义两个回调接口
    public interface OnItemClickListener {
    
    
        void onItemClick(View view,int position);
    }
    public interface OnItemLongClickListener{
    
    
        void onItemLongClick(View view,int position);
    }
    //获取回调接口的实例对象
    public void setOnItemClickListener(OnItemClickListener onItemClickListener){
    
    
        this.onItemClickListener = onItemClickListener;
    }
    public void setOnLongClickListener(OnItemLongClickListener onLongClickListener){
    
    
        this.onItemLongClickListener = onLongClickListener;
    }
  • (2)修改适配器里的ViewHolder类
static class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener, View.OnLongClickListener {
    
    
        View soccerView;
        private OnItemClickListener onItemClickListener1 = null;
        private OnItemLongClickListener onItemLongClickListener1 = null;
        TextView name;
        
        public ViewHolder(@NonNull View itemView,OnItemClickListener onItemClickListener,OnItemLongClickListener onItemLongClickListener) {
    
    
            super(itemView);
            onItemClickListener1 = onItemClickListener;
            onItemLongClickListener1 = onItemLongClickListener;
            soccerView = itemView;
            name = itemView.findViewById(R.id.name);
            itemView.setOnClickListener(this);
            itemView.setOnLongClickListener(this);
        }

        @Override
        public void onClick(View v) {
    
    
            if(onItemClickListener1 != null){
    
    
                onItemClickListener1.onItemClick(v,getAdapterPosition());
            }
        }
        @Override
        public boolean onLongClick(View v) {
    
    
            if(onItemLongClickListener1 != null){
    
    
                onItemLongClickListener1.onItemLongClick(v, getPosition());
            }
            return true;
        }
    }

1、首先要对VIewHolder的构造方法进行修改,传入两个参数,分别为点击事件和长按事件的接口
2、然后调用setOnClickListener()和setOnLongClickListener()方法设置点击事件的监听器,并让ViewHolder继承两个接口:View.OnClickListener, View.OnLongClickListener
3、重写onClick()和onLongClick()方法,在其中编写点击事件的逻辑处理

扫描二维码关注公众号,回复: 13231620 查看本文章
  • (3)接着编写点击后的逻辑
		soccerAdapter.setOnItemClickListener(new SoccerAdapter.OnItemClickListener() {
    
    
            @Override
            public void onItemClick(View view, int position) {
    
    
                Toast.makeText(MainActivity.this,"点击第 " + (position + 1)+" 条",Toast.LENGTH_SHORT).show();
            }
        });
        soccerAdapter.setOnLongClickListener(new SoccerAdapter.OnItemLongClickListener() {
    
    
            @Override
            public void onItemLongClick(View view, int position) {
    
    
                new AlertDialog.Builder(MainActivity.this)
                        .setTitle("确认删除?")
                        .setNegativeButton("取消",null)
                        .setPositiveButton("确定", new DialogInterface.OnClickListener() {
    
    
                            @Override
                            public void onClick(DialogInterface dialog, int which) {
    
    
                                soccerAdapter.removeData(position);
                            }
                        }).show();
            }
        });

猜你喜欢

转载自blog.csdn.net/Cristiano_san/article/details/120242795