0.准备工作
RecyclerView
是一个滚动控件,
想要使用RecyclerView
这个控件,需要将 v7 支持库添加到项目中,
recyclerview 官方文档
首先需要在项目的build.gradle
中添加相应的依赖库才行。
打开app/build.gradle
文件,在dependencies
闭包中添加如下内容:
implementation 'com.android.support:recyclerview-v7:28.0.0'
gradle
文件自上次同步之后又发生了变化,需要再次同步才能使项目正常工作。这里只需要点击Sync Now就可以了,然后gradle会开始进行同步。
1.简单用法
(1).修改 activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app = "http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent">
<!--
官网示例,运行出错
<android.support.v7.widget.RecyclerView
android:id="@+id/my_recycler_view"
android:scrollbars="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
-->
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/my_recycler_view"
android:scrollbars="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</LinearLayout>
(2).创建Fruit
类
public class Fruit {
private String name;
private int imageId;
public Fruit(int imageId, String name){
this.name = name;
this.imageId = imageId;
}
public String getName() {
return name;
}
public int getImageId() {
return imageId;
}
}
(3)创建Adapter
1).FruitAdapter
继承 RecyclerView.Adapter<FruitViewHolder>
2).FruitViewHolder
是自定义内部类
3).要实现三个接口:
onCreateViewHolder
, onBindViewHolder
,getItemCount
public class FruitAdapter extends RecyclerView.Adapter<FruitAdapter.FruitViewHolder> {
private List<Fruit> fruits;
static class FruitViewHolder extends RecyclerView.ViewHolder{
ImageView fruitImage;
TextView fruitName;
/**
* ViewHolder的构造函数中要传入一个View参数,
* 这个参数通常就是RecyclerView子项的最外层布局,
* 那么我们就可以通过findViewById()方法来获取到布局中的ImageView和TextView的实例
* */
public FruitViewHolder(@NonNull View itemView) {
super(itemView);
fruitImage = itemView.findViewById(R.id.fruit_image);
fruitName = itemView.findViewById(R.id.fruit_name);
}
}
public FruitAdapter(List<Fruit> fruits) {
this.fruits = fruits;
}
@NonNull
@Override
public FruitViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext())
.inflate(R.layout.fruit_item, parent, false);
FruitViewHolder holder = new FruitViewHolder(view);
return holder;
}
/**
*用于对RecyclerView子项的数据进行赋值的,会在每个子项被滚动到屏幕内的时候执行
* */
@Override
public void onBindViewHolder(@NonNull FruitViewHolder holder, int position) {
Fruit fruit = fruits.get(position);
holder.fruitImage.setImageResource(fruit.getImageId());
holder.fruitName.setText(fruit.getName());
}
@Override
public int getItemCount() {
return fruits.size();
}
}
(4)绑定数据
public class MainActivity extends AppCompatActivity {
private List<Fruit> fruits = new ArrayList<>();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
InitFruits();
RecyclerView recyclerView = findViewById(R.id.my_recycler_view);
LinearLayoutManager linearLayoutManager = new LinearLayoutManager(this);
recyclerView.setLayoutManager(linearLayoutManager);
FruitAdapter fruitAdapter = new FruitAdapter(fruits);
recyclerView.setAdapter(fruitAdapter);
}
private void InitFruits(){
for (int i= 0; i < 10 ; i++){
Fruit taozi = new Fruit(R.drawable.taozi, "桃子");
fruits.add(taozi);
Fruit putao = new Fruit(R.drawable.putao, "葡萄");
fruits.add(putao);
Fruit huolongguo = new Fruit(R.drawable.huolongguo, "火龙果");
fruits.add(huolongguo);
Fruit lamei = new Fruit(R.drawable.lanmei, "蓝莓");
fruits.add(lamei);
Fruit xiguang = new Fruit(R.drawable.xigua, "西瓜");
fruits.add(xiguang);
}
}
}
效果:
把图片放在res/drawable
文件夹,启动App时,可能出现的bug:
...
java.lang.RuntimeException: Canvas: trying to draw too large(110250000bytes) bitmap.
...
参见:解决方案
新建 drawable-nodpi
文件夹把图片移动到该文件夹即可。
2 实现横向滚动
1).修改fruit_item.xml 布局:
图片和文字垂直排列: android:orientation="vertical"
调整每个Fruit宽度: android:layout_width="wrap_content"
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<ImageView android:id="@+id/fruit_image"
android:layout_width="50dp"
android:layout_height="50dp"
android:layout_gravity="center_vertical"/>
<TextView android:id="@+id/fruit_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center_vertical"
android:layout_gravity="center_vertical"
android:layout_marginLeft="10dp"/>
</LinearLayout>
2)修改滚动方向:
...
linearLayoutManager.setOrientation(LinearLayoutManager.HORIZONTAL);
...

3.瀑布流布局
ListView
的布局排列是由自身去管理的,
而RecyclerView
则将这个工作交给了LayoutManager,
LayoutManager
中制定了一套可扩展的布局排列接口,子类只要按照接口的规范来实现,就能定制出各种不同排列方式的布局了。
除了LinearLayoutManager之外,
RecyclerView
还给我们提供了GridLayoutManager
和StaggeredGridLayoutManager
这两种内置的布局排列方式。
GridLayoutManager
可以用于实现网格布局,
StaggeredGridLayoutManager
可以用于实现瀑布流布局。
1)修改fruit_item.xml 布局:
修改宽度: android:layout_width="120dp"
<!--瀑布流布局-->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<ImageView android:id="@+id/fruit_image"
android:layout_width="120dp"
android:layout_height="100dp"
android:layout_gravity="center_vertical"/>
<TextView android:id="@+id/fruit_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center_vertical"
android:layout_gravity="center_vertical"
android:layout_marginLeft="10dp"/>
</LinearLayout>
2)使用瀑布流布局
RecyclerView recyclerView = findViewById(R.id.my_recycler_view);
/*
LinearLayoutManager linearLayoutManager = new LinearLayoutManager(this);
linearLayoutManager.setOrientation(LinearLayoutManager.HORIZONTAL); //横向布局
recyclerView.setLayoutManager(linearLayoutManager);
*/
//瀑布流布局:其中参数StaggeredGridLayoutManager.VERTICAL表示 从左往右依次排开,满行另起一行
StaggeredGridLayoutManager layoutManager = new StaggeredGridLayoutManager(6, StaggeredGridLayoutManager.VERTICAL);
recyclerView.setLayoutManager(layoutManager);
FruitAdapter fruitAdapter = new FruitAdapter(fruits);
recyclerView.setAdapter(fruitAdapter);

4.RecyclerView的点击事件
不过不同于ListView的是,RecyclerView
并没有提供类似于setOnItemClickListener()
这样的注册监听器方法,而是需要我们自己给子项具体的View去注册点击事件,
ListView
在点击事件上的处理并不人性化,setOnItemClickListener()
方法注册的是子项的点击事件,但如果我想点击的是子项里具体的某一个按钮呢?虽然ListView
也是能做到的,但是实现起来就相对比较麻烦了。
为此,RecyclerView
干脆直接摒弃了子项点击事件的监听器,所有的点击事件都由具体的View
去注册,就再没有这个困扰了。
1)FruitViewHolder
新增变量 View fruitView;
用于保存子项的最外层布局的实例
static class FruitViewHolder extends RecyclerView.ViewHolder{
View fruitView; //子项的最外层布局的实例
ImageView fruitImage;
TextView fruitName;
/**
* ViewHolder的构造函数中要传入一个View参数,
* 这个参数通常就是RecyclerView子项的最外层布局,
* 那么我们就可以通过findViewById()方法来获取到布局中的ImageView和TextView的实例
* */
public FruitViewHolder(@NonNull View itemView) {
super(itemView);
fruitView = itemView; //子项的最外层布局的实例
fruitImage = itemView.findViewById(R.id.fruit_image);
fruitName = itemView.findViewById(R.id.fruit_name);
}
}
FruitAdapter
在方法onCreateViewHolder
中注册点击事件
@NonNull
@Override
public FruitViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext())
.inflate(R.layout.fruit_item, parent, false);
final FruitViewHolder holder = new FruitViewHolder(view);
//为子项的最外层布局注册点击事件:
holder.fruitView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
int position = holder.getAdapterPosition();
Fruit fruit = fruits.get(position);
Toast.makeText( v.getContext(), "点击子项View:"+ fruit.getName(), Toast.LENGTH_SHORT).show();
}
});
//为子项的图片注册点击事件:
holder.fruitImage.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
int position = holder.getAdapterPosition();
Fruit fruit = fruits.get(position);
Toast.makeText(v.getContext(), "点击子项View中的ImageView:"+ fruit.getName(), Toast.LENGTH_SHORT).show();
}
});
return holder;
}
点击图片
点击文字,文字没有注册点击事件,最终会被最外层布局捕获