RecyclerView
做一个简单的 recycler
1. 添加 RecyclerView 的依赖项 我们需要先在 APP 的 build.gradle 文件中添加下面的代码,让配置文件将需要的库加载进来。请参考此网页,以获取最新的配置代码
1 2 3 4 dependencies { implementation "androidx.recyclerview:recyclerview:1.1.0" implementation "androidx.recyclerview:recyclerview-selection:1.1.0-rc01" }
2. 在 xml 使用且在 activity 中获取并设置 recyclerview
1 2 3 4 <androidx.recyclerview.widget.RecyclerView android:layout_width ="match_parent" android:layout_height ="wrap_content" android:id ="@+id/recycler" />
1 2 3 4 5 6 7 8 9 10 recyclerView = findViewById(R.id.recycler); recyclerView.setLayoutManager(new LinearLayoutManager (this )); recyclerView.setAdapter(new LinearAdapter (this ));
此时我们做完这些是看不到效果的。我们还需要写一个适配器。
3.适配器 RecyclerView.Adapter 开始之前我们看一下适配器部分源码:
1 2 3 4 5 public static abstract class Adapter <VH extends ViewHolder > { public abstract VH onCreateViewHolder (ViewGroup parent, int viewType) ; public abstract void onBindViewHolder (VH holder, int position) ; public abstract int getItemCount () ; }
从源码得知,我们有 3 个必须要实现的方法。且它有规定一个只接受 ViewHolder 子类的泛型,方法 1 返回该类,方法 2 需要该类的传入。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 class LinearAdapter extends RecyclerView .Adapter<LinearAdapter.LinearHolder> { private Context context; public LinearAdapter (Context context) { this .context = context; } public LinearAdapter.LinearHolder onCreateViewHolder (@NonNull ViewGroup parent, int viewType) { return new LinearHolder (LayoutInflater.from(context) .inflate(R.layout.item,parent,false )); } public void onBindViewHolder (@NonNull LinearAdapter.LinearHolder holder, int position) { holder.textView.setText("Item" ); holder.imageView.setImageResource(R.drawable.ic_launcher_background); } public int getItemCount () { return 10 ; } class LinearHolder extends RecyclerView .ViewHolder{ private ImageView imageView; private TextView textView; public LinearHolder (@NonNull View itemView) { super (itemView); textView = itemView.findViewById(R.id.textView); imageView = itemView.findViewById(R.id.imageView); } } }
这样一个简单的 RecyclerView 就完成了。
4. addItemDecoration 添加分割线
RecyclerView.ItemDecoration:抽象类,主要用于给 Item 之间添加分割线。官方没有实现类,所以如果要添加分割线,我们需要手动实现这个抽象类。这里使用的 getItemOffsets 方法添加,并不是真正的添加了分隔线而是利用给 item 之间 增加间隔 ,让下面的 背景 漏出而产生的分割线。
1 2 3 4 5 6 7 8 9 10 recyclerView.addItemDecoration(new MyDecoration ()); --------------------------------------------------------- class MyDecoration extends RecyclerView .ItemDecoration{ public void getItemOffsets (@NonNull Rect outRect, @NonNull View view, @NonNull RecyclerView parent, @NonNull RecyclerView.State state) { super .getItemOffsets(outRect, view, parent, state); outRect.bottom=1 ; } }
5.事件
最简单的方法:在 onBindViewHolder 中,利用 holder 直接给组件添加事件即可。
1 2 3 4 5 6 7 public void onBindViewHolder (@NonNull LinearAdapter.LinearHolder holder, final int position) { holder.imageView.setOnClickListener(new View .OnClickListener() { public void onClick (View v) { Toast.makeText(context, position+"b被点击" , Toast.LENGTH_SHORT).show(); } }); }
事件回调方法:这样自由度更高,扩展性更高。
在适配器类中定义一个接口和该接口对象,在接口中定义事件方法
构造适配器对象时,可接受一个该接口对象。
且在 onBindViewHolder 中给组件设置事件时方法中可以是具体内容也可以利用该接口对象使用该方法。
进阶与扩展
1 2 3 4 5 LinearLayoutManager layout = new LinearLayoutManager (this );layout.setOrientation(RecyclerView.HORIZONTAL); recyclerHor.setLayoutManager(layout);
1 2 3 4 5 GridLayoutManager grid=new GridLayoutManager (this ,4 ); recyclerGrid.setLayoutManager(grid); recyclerGrid.setAdapter(new GridAdapter ());
1 2 3 4 StaggeredGridLayoutManager stagger=new StaggeredGridLayoutManager (2 ,StaggeredGridLayoutManager.VERTICAL); recyclerStagger.setLayoutManager(stagger);
item 的增删改与移动:(传入的参数都是指定的位置 position)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 list.add(list.size()+"新" ); recyclerAdapter.notifyItemInserted(list.size()-1 ); int i = new Random ().nextInt(list.size());list.remove(i); recyclerAdapter.notifyItemRemoved(i); int i = new Random ().nextInt(list.size());list.set(i,i+"改" ); recyclerAdapter.notifyItemChanged(i); int start = new Random ().nextInt(list.size());int end = new Random ().nextInt(list.size());recyclerAdapter.notifyItemMoved(start,end);
根据需要不同的view使用不同的布局1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 public int getItemViewType (int position) { if (position%2 ==0 ){return 0 ;} return 1 ; } public RecyclerView.ViewHolder onCreateViewHolder (@NonNull ViewGroup parent, int viewType) { if (viewType==0 ) return new ViewHolder (LayoutInflater.from(parent.getContext()) .inflate(R.layout.item,parent,false )); return new ImgViewHolder (LayoutInflater.from(parent.getContext()) .inflate(R.layout.item_stagger,parent,false )); } public void onBindViewHolder (@NonNull RecyclerView.ViewHolder holder, int position) { if (getItemViewType(position)==0 ) { ((ViewHolder) holder).textView.setText("Demo " + position); ((ViewHolder) holder).imageView.setImageResource(R.drawable.ic_launcher_background); } } class ViewHolder extends RecyclerView .ViewHolder{ private TextView textView; private ImageView imageView; public ViewHolder (@NonNull View itemView) { super (itemView); textView = itemView.findViewById(R.id.textView); imageView = itemView.findViewById(R.id.imageView); } } class ImgViewHolder extends RecyclerView .ViewHolder{ private ImageView imageView; public ImgViewHolder (@NonNull View itemView) { super (itemView); imageView = itemView.findViewById(R.id.imageView); } }
RecyclerView会自动获取焦点,导致打开页面时RecyclerView之上的控件被RecyclerView挤出屏幕外。在RecyclerView的外部布局中加入如下配置,表示viewgroup会覆盖子类控件而直接获得焦点。
1 android:descendantFocusability="blocksDescendants"
RecyclerView和ScrollView 一起用,滑动会不流畅,因为两个都有滑动事件,设置下:
1 recyclerView.setNestedScrollingEnabled(false );
RecyclerView和ScrollView 一起用,偶尔出现数据条目显示不全这是RecyclerView获取布局高度不准确导致,建议使用相等布局将其包裹:
1 2 3 4 5 6 7 <RelativeLayout android:layout_width ="match_parent" android:layout_height ="wrap_content" > <androidx.recyclerview.widget.RecyclerView android:layout_width ="match_parent" android:layout_height ="match_parent" /> </RelativeLayout >