本篇介绍列表视图中RecyclerView的用法。
RecyclerView用来展现各种列表视图,它不仅是ListView的升级,之前的Gallery、GridView的效果它也都可以实现。下面通过一个示例来展示RecyclerView的用法。
先看最终实现的界面效果:
1、首先添加一个Activity,并编辑布局文件如下。可以看到RecyclerView属于v7-支持库,如果你的androidsdk版本较旧的话,需要单独添加引用。
dependencies {
implementation ‘com.android.support:recyclerview-v7:27.1.1’
}
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<android.support.v7.widget.RecyclerView
android:id="@+id/recyclerView"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</LinearLayout>
2、RecyclerView的基本方法有两个。
mRecyclerView.setLayoutManager(linearLayoutManager);
mRecyclerView.setAdapter(mMyAdapter);
分别需要一个继承自RecyclerView.Adapter的mMyAdapter(需自己编写)和一个继承自RecyclerView.LayoutManager的布局管理器,系统已经给了3个常用的:LinearLayoutManager、GridLayoutManager、StaggeredGridLayoutManager(瀑布布局)。
3、开始编写mMyAdapter的类RecycleAdapter
public class RecycleAdapter extends RecyclerView.Adapter
在继承RecyclerView.Adapter时,发现需要指定一个继承自RecyclerView.ViewHolder的类。这里解释一下ViewHolder,它封装了列表子项中用到的具体控件,对每个列表子项的实例进行缓存,负责显示子项。我们不能再像ListView那样直接操作View并且处理缓存,这里改为操作ViewHolder即可,而且ViewHolder已经帮我们实现了缓存。
4、实现ViewHolder类
从效果图上可以看出列表子项里包括一个ImageView、一个大TextView、一个小TextView。对应的实现代码如下:
public class NormalHolder extends RecyclerView.ViewHolder {
public ImageView icon;
public TextView title;
public TextView des;
public NormalHolder(@NonNull View itemView) {
super(itemView);
icon = itemView.findViewById(R.id.imgIcon);
title = itemView.findViewById(R.id.txtName);
des = itemView.findViewById(R.id.txtDes);
}
}
列表子项的界面代码 custom_listcell.xml :
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="10dp"
android:orientation="horizontal">
<ImageView
android:id="@+id/imgIcon"
android:layout_width="60dp"
android:layout_height="60dp" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:orientation="vertical">
<TextView
android:id="@+id/txtName"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="20dp" />
<TextView
android:id="@+id/txtDes"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="10dp" />
</LinearLayout>
</LinearLayout>
列表子项的实体类代码 ListCellData.java:
public class ListCellData {
private String controlName = "";
private String controlDes = "";
private int imgID;
public ListCellData(String cn, String des, int imgid) {
this.controlName = cn;
this.controlDes = des;
this.imgID = imgid;
}
public String getControlaName() { return controlName;}
public String getControlDes() { return controlDes; }
public void setControlDes(String controlDes) { this.controlDes = controlDes; }
public int getImgID() { return imgID;}
public void setImgID(int imgID) { this.imgID = imgID;}
}
5、具体实现RecycleAdapter (RecycleAdapter.java)
public class RecycleAdapter extends RecyclerView.Adapter<RecycleAdapter.NormalHolder> {
private Context context;
List<ListCellData> datasource;//用来接收列表数据源
public RecycleAdapter(Context ct, List<ListCellData> ds) {
context = ct;
datasource = ds;
}
//创建ViewHolder,这里返回我们自己编写的NormalHolder
@NonNull
@Override
public NormalHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
return new NormalHolder(LayoutInflater.from(context).inflate(R.layout.custom_listcell, null));
}
//填充视图,根据posotion取得具体数据项,并填充到界面上
@Override
public void onBindViewHolder(@NonNull final NormalHolder holder, int position) {
holder.title.setText(datasource.get(position).getControlaName());
holder.des.setText(datasource.get(position).getControlDes());
holder.icon.setImageResource(datasource.get(position).getImgID());
}
@Override
public int getItemCount() {
return datasource.size();
}
}
6、最后给出主Activity的代码(RecyclerActivity.java)。运行之后,就是本篇开始给出的效果了。
private RecyclerView mRecyclerView;
private RecycleAdapter mMyAdapter;
private LinearLayoutManager linearLayoutManager;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_recycler);
initData();
mRecyclerView = findViewById(R.id.recyclerView);
mMyAdapter = new RecycleAdapter(this,datasource);
linearLayoutManager = new LinearLayoutManager(this,OrientationHelper.VERTICAL,false);
mRecyclerView.setLayoutManager(linearLayoutManager);
mRecyclerView.setAdapter(mMyAdapter);
}
添加分割线
mRecyclerView.addItemDecoration(new DividerItemDecoration(
getActivity(), DividerItemDecoration.HORIZONTAL_LIST));
效果图:
当然我们可以自定义分割线的样式,通过查看DividerItemDecoration的源码可知,它会读取系统主题中的@android:attr/listDivider作为 Item 的分割线(实质 Drawable ),同时还提供 setDrawable()方法,允许我们为分割线指定样式。感兴趣的可以自己写一个Drawable,设置代码如下:
//改成如下:
DividerItemDecoration decoration = new DividerItemDecoration(this,DividerItemDecoration.VERTICAL);
decoration.setDrawable(getResources().getDrawable(R.drawable.divider,null));
mRecyclerView.addItemDecoration(decoration);
切换不同的布局样式,只需设定不同的LayoutManager即可。
1、开篇给出了最常用的竖向LinearLayoutManager,下面演示横向的;
linearLayoutManager = new LinearLayoutManager(this,OrientationHelper.HORIZONTAL,false);
效果如图:
2、GridLayoutManager.构造函数里需要指定Grid的列数。
gridLayoutManager = new GridLayoutManager(this,3);
mRecyclerView.setLayoutManager(gridLayoutManager );
效果图:
3、最后是瀑布式布局,构造函数里需要指定列数和方向。
staggeredGridLayoutManager = new StaggeredGridLayoutManager(2,StaggeredGridLayoutManager.VERTICAL);
mRecyclerView.setLayoutManager(staggeredGridLayoutManager);
为了展示出瀑布的效果,我们需要子项有不同的高度,在RecycleAdapter的RecycleAdapter方法里,我们加一个随机高度:
ViewGroup.LayoutParams layoutParams = holder.title.getLayoutParams();
layoutParams.height = new Random().nextInt(100) + 60;
holder.title.setLayoutParams(layoutParams);
RecyclerView 本身不提供点击事件,需要在onBindViewHolder事件里,自己配置控件的点击事件。虽然ListView 提供了点击事件,但是只能针对子项。而RecyclerView 可按需对任意子项 设置点击监听。
1、首先在RecycleAdapter中,添加点击监听事件:
public interface IItemClickListener {
void onItemClick(View view, int position);
void onItemLongClick(View view, int posiition);
}
private IItemClickListener iItemClickListener;
public void setOnItemClickListener(IItemClickListener listener) {
iItemClickListener = listener;
}
2、在onBindViewHolder中,将点击事件注册到控件上:
if (iItemClickListener != null) {
holder.title.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
int pos = holder.getLayoutPosition();
iItemClickListener.onItemClick(holder.title, pos);
}
});
holder.title.setOnLongClickListener(new View.OnLongClickListener() {
@Override
public boolean onLongClick(View v) {
int pos = holder.getLayoutPosition();
iItemClickListener.onItemLongClick(holder.title, pos);
return false;
}
});
}
3、在RecyclerActivity中,设置点击事件的具体实现,并传入RecycleAdapter
mMyAdapter.setOnItemClickListener(new RecycleAdapter.IItemClickListener() {
@Override
public void onItemClick(View view, int position) {
Toast.makeText(RecyclerActivity.this, ((TextView)view).getText() + " click", Toast.LENGTH_SHORT).show();
}
@Override
public void onItemLongClick(View view, int posiition) {
Toast.makeText(RecyclerActivity.this, posiition + " Long click", Toast.LENGTH_SHORT).show();
mMyAdapter.removeData(posiition);
}
});
我们对列表子项中的title(大TextView)控件设置了点击和长按事件,运行后即可看效果。
下拉刷新主要是使用SwipeRefreshLayout控件,在原RecyclerView外包一层SwipeRefreshLayout,然后监听该控件的setOnRefreshListene事件即可。
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<android.support.v4.widget.SwipeRefreshLayout
android:id="@+id/refreshLayout"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Hello World!">
<android.support.v7.widget.RecyclerView
android:id="@+id/recyclerView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:divider="#FFFF0000"
android:dividerHeight="10dp"/>
</android.support.v4.widget.SwipeRefreshLayout>
</LinearLayout>
mSwipeRefreshLayout.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {
@Override
public void onRefresh() {
new Handler().postDelayed(new Runnable() {
@Override
public void run() {
mMyAdapter.addList(InitData2());//每次刷新,往里插入5条记录
mRecyclerView.smoothScrollToPosition(0);
mSwipeRefreshLayout.setRefreshing(false);
}
}, 500);
}
});
public void addList(List<ListCellData> ds){
datasource.addAll(0,ds);
notifyItemRangeInserted(0,ds.size());
}
//设置进度View样式的大小,只有两个值DEFAULT和LARGE
mSwipeRefreshLayout.setSize(CircularProgressDrawable.LARGE );
//设置进度View下拉的起始点和结束点,scale 是指设置是否需要放大或者缩小动画
mSwipeRefreshLayout.setProgressViewOffset(true, -0, 100);
//设置进度View下拉的结束点,scale 是指设置是否需要放大或者缩小动画
mSwipeRefreshLayout.setProgressViewEndTarget(true, 180);
//设置进度View的组合颜色,在手指上下滑时使用第一个颜色,在刷新中,会一个个颜色进行切换
mSwipeRefreshLayout.setColorSchemeResources(android.R.color.holo_blue_light,
android.R.color.holo_red_light,
android.R.color.holo_orange_light);
//设置触发刷新的距离
mSwipeRefreshLayout.setDistanceToTriggerSync(200);