一、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);
}
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);
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);
}
}
@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;
}
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()方法,在其中编写点击事件的逻辑处理
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();
}
});
|