本文是 郭霖. 第一行代码[M]. 人民邮电出版社, 2014. 第三章第六节的笔记
A flexible view for providing a limited window into a large data set.
为大型数据集提供有限窗口的灵活视图。
首先,在 app/build.gradle 里面的 dependence 里面添加:
compile 'com.android.support:recyclerview-v7:27.0.0'
布局文件:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical" android:layout_width="match_parent"
android:layout_height="match_parent">
<android.support.v7.widget.RecyclerView
android:id="@+id/recycle_view"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</LinearLayout>
适配器:
Adapter: A subclass of RecyclerView.Adapter responsible for providing views that represent items in a data set.
适配器: RecyclerView.Adapter 的一个子类,负责提供表示数据集中项目的视图。
RecyclerView.Adapter 的 api:
Base class for an Adapter
Adapters provide a binding from an app-specific data set to views that are displayed within a RecyclerView.
适配器的基类
适配器提供从特定于应用程序的数据集到在 RecyclerView 中显示的视图的绑定
接下来需要为 RecylerView 准备一个适配器 FruitRecyclerAdapter 类,让这个适配器继承自 RecyclerView.Adapter ,并将泛型指定为 ViewHolder ,其中 ViewHolder 代码如下:
public class ViewHolder extends RecyclerView.ViewHolder{
ImageView fruitImage;
TextView fruitText;
public ViewHolder(View view) {
super(view);
fruitImage = (ImageView)view.findViewById(R.id.fruit_image);
fruitText = (TextView)view.findViewById(R.id.fruit_text);
}
}
RecyclerView.ViewHolder 的 API :
A ViewHolder describes an item view and metadata about its place within the RecyclerView.
ViewHolder 描述了一个项目视图和关于它在 RecyclerView 中的位置的元数据。
RecyclerView.Adapter implementations should subclass ViewHolder and add fields for caching potentially expensive findViewById(int) results.
While RecyclerView.LayoutParams belong to the RecyclerView.LayoutManager, ViewHolders belong to the adapter. Adapters should feel free to use their own custom ViewHolder implementations to store data that makes binding view contents easier. Implementations should assume that individual item views will hold strong references to ViewHolder objects and that RecyclerView instances may hold strong references to extra off-screen item views for caching purposes
RecyclerView.ViewHolder 的构造函数需要传入 view 参数.这个 view 参数通常是 RecyclerView 子项的最外层布局。那么我们就可以通过 view 的 findViewById 找到控件的实例。
public class FruitRecycleAdapter extends RecyclerView.Adapter<ViewHolder> {
private List<Fruit> mFruitList;
public FruitRecycleAdapter(List<Fruit> mFruitList) {
this.mFruitList = mFruitList;
}
@Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext())
.inflate(R.layout.fruit_item, parent, false);
ViewHolder holder = new ViewHolder(view);
Log.i("FruitRecycleAdapter", "onCreateViewHolder");
return holder;
}
@Override
public void onBindViewHolder(ViewHolder holder, int position) {
Fruit fruit = mFruitList.get(position);
holder.fruitImage.setImageResource(fruit.getImgId());
holder.fruitText.setText(fruit.getName());
Log.i("FruitRecycleAdapter", "onBindViewHolder" + "加载数据" + position);
}
@Override
public int getItemCount() {
return mFruitList.size();
}
}
利用构造函数将要展示的数据源传入,并赋值给全局变量。因为继承自 RecyclerView.Adapter ,所以必须重写 onCreateViewHolder(),onBindViewHolder(),getItemCount()方法。
onCreateViewHolder():
This method calls onCreateViewHolder(ViewGroup, int) to create a new RecyclerView.ViewHolder and initializes some private fields to be used by RecyclerView.
此方法调用 onCreateViewHolder ( ViewGroup , int )来创建一个新的 RecyclerView.ViewHolder ,并初始化 RecyclerView 要使用的一些专用字段。
我们将 fruit_item 布局文件加载进 view 中,用 view 创建一个 ViewHolder 实例。然后将这个实例返回。这个实例将在 onBindViewHolder()方法中被调用。
onBindViewHolder():
Called by RecyclerView to display the data at the specified position.
由 RecyclerView 调用以在指定位置显示数据。(即对子项的数据进行赋值)
onBindViewHolder()方法会在每个子项被滚动到屏幕内的时候被执行,这里我们通过 position 参数得到当前的 fruit 实例。然后再将数据设置到 ViewHolder 的控件实例中去即可。
getItemCount()
Returns the total number of items in the data set held by the adapter.
返回适配器持有的数据集中的项目总数。
Activity :
public class RecyclerActivity extends Activity {
List<Fruit> fruitList = new ArrayList<>();
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_recyclerview);
initFruits();
RecyclerView recyclerView = (RecyclerView) findViewById(R.id.recycle_view);
LinearLayoutManager layoutManager = new LinearLayoutManager(this);
recyclerView.setLayoutManager(layoutManager);
FruitRecycleAdapter fruitRecycleAdapter = new FruitRecycleAdapter(fruitList);
recyclerView.setAdapter(fruitRecycleAdapter);
}
private void initFruits() {
for (int i = 0; i < 2; i++) {
Fruit apple = new Fruit("Apple", R.drawable.apple_pic);
fruitList.add(apple);
...
Fruit mango = new Fruit("Mango", R.drawable.mango_pic);
fruitList.add(mango);
}
}
}
这里初始化数据之后,获取 RecyclerView 实例,然后创建一个 LinearLayoutManager 对象,并将它设置到 RecyclerView 当中.
RecyclerView.LayoutManager 的 API :
A LayoutManager is responsible for measuring and positioning item views within a RecyclerView as well as determining the policy for when to recycle item views that are no longer visible to the user. By changing the LayoutManager a RecyclerView can be used to implement a standard vertically scrolling list, a uniform grid, staggered grids, horizontally scrolling collections and more. Several stock layout managers are provided for general use.
If the LayoutManager specifies a default constructor or one with the signature (Context, AttributeSet, int, int), RecyclerView will instantiate and set the LayoutManager when being inflated. Most used properties can be then obtained from getProperties(Context, AttributeSet, int, int). In case a LayoutManager specifies both constructors, the non-default constructor will take precedence.
LayoutManager 负责测量和定位 RecyclerView 中的项目视图,并确定何时回收不再可见的项目视图的策略。通过更改 LayoutManager , RecyclerView 可用于实现标准垂直滚动列表,统一网格,交错网格,水平滚动集合等等。提供一些股票布局经理供一般使用。 如果 LayoutManager 指定默认构造函数或带有签名( Context , AttributeSet , int , int )的构造函数,则 RecyclerView 将在充气时实例化并设置 LayoutManager 。大多数使用的属性可以从 getProperties ( Context , AttributeSet , int , int )中获取。如果一个 LayoutManager 指定了两个构造函数,那么非默认构造函数将优先。
LayoutManager 用于指定 RecyclerView 的布局方式,这里使用线性布局。最后调用 RecyclerView 的 setAdapter 方法来完成适配器设置,这样 RecyclerView 和数据之间的关联就建立完成了。
运行结果:

04-10 17:10:06.989 12710-12710/com.androidstudy I/FruitRecycleAdapter: onCreateViewHolder
04-10 17:10:06.993 12710-12710/com.androidstudy I/FruitRecycleAdapter: onBindViewHolder 加载数据 0
04-10 17:10:06.995 12710-12710/com.androidstudy I/FruitRecycleAdapter: onCreateViewHolder
04-10 17:10:06.998 12710-12710/com.androidstudy I/FruitRecycleAdapter: onBindViewHolder 加载数据 1
04-10 17:10:06.999 12710-12710/com.androidstudy I/FruitRecycleAdapter: onCreateViewHolder
04-10 17:10:07.001 12710-12710/com.androidstudy I/FruitRecycleAdapter: onBindViewHolder 加载数据 2
04-10 17:10:07.003 12710-12710/com.androidstudy I/FruitRecycleAdapter: onCreateViewHolder
04-10 17:10:07.005 12710-12710/com.androidstudy I/FruitRecycleAdapter: onBindViewHolder 加载数据 3
04-10 17:10:07.006 12710-12710/com.androidstudy I/FruitRecycleAdapter: onCreateViewHolder
04-10 17:10:07.008 12710-12710/com.androidstudy I/FruitRecycleAdapter: onBindViewHolder 加载数据 4
04-10 17:10:07.009 12710-12710/com.androidstudy I/FruitRecycleAdapter: onCreateViewHolder
04-10 17:10:07.012 12710-12710/com.androidstudy I/FruitRecycleAdapter: onBindViewHolder 加载数据 5
04-10 17:10:07.013 12710-12710/com.androidstudy I/FruitRecycleAdapter: onCreateViewHolder
04-10 17:10:07.015 12710-12710/com.androidstudy I/FruitRecycleAdapter: onBindViewHolder 加载数据 6
04-10 17:10:07.017 12710-12710/com.androidstudy I/FruitRecycleAdapter: onCreateViewHolder
04-10 17:10:07.019 12710-12710/com.androidstudy I/FruitRecycleAdapter: onBindViewHolder 加载数据 7
04-10 17:10:18.510 12710-12710/com.androidstudy I/FruitRecycleAdapter: onCreateViewHolder
04-10 17:10:18.516 12710-12710/com.androidstudy I/FruitRecycleAdapter: onBindViewHolder 加载数据 8
04-10 17:10:18.580 12710-12710/com.androidstudy I/FruitRecycleAdapter: onCreateViewHolder
04-10 17:10:18.585 12710-12710/com.androidstudy I/FruitRecycleAdapter: onBindViewHolder 加载数据 9
04-10 17:10:19.021 12710-12710/com.androidstudy I/FruitRecycleAdapter: onCreateViewHolder
04-10 17:10:19.022 12710-12710/com.androidstudy I/FruitRecycleAdapter: onBindViewHolder 加载数据 10
04-10 17:10:19.514 12710-12710/com.androidstudy I/FruitRecycleAdapter: onCreateViewHolder
04-10 17:10:19.514 12710-12710/com.androidstudy I/FruitRecycleAdapter: onBindViewHolder 加载数据 11
04-10 17:10:26.093 12710-12710/com.androidstudy I/FruitRecycleAdapter: onBindViewHolder 加载数据 0
04-10 17:10:36.262 12710-12710/com.androidstudy I/FruitRecycleAdapter: onBindViewHolder 加载数据 11
04-10 17:10:36.365 12710-12710/com.androidstudy I/FruitRecycleAdapter: onBindViewHolder 加载数据 12
04-10 17:10:41.062 12710-12710/com.androidstudy I/FruitRecycleAdapter: onBindViewHolder 加载数据 13
04-10 17:10:42.420 12710-12710/com.androidstudy I/FruitRecycleAdapter: onBindViewHolder 加载数据 2
04-10 17:10:42.489 12710-12710/com.androidstudy I/FruitRecycleAdapter: onBindViewHolder 加载数据 1
04-10 17:10:42.543 12710-12710/com.androidstudy I/FruitRecycleAdapter: onBindViewHolder 加载数据 0
04-10 17:10:44.798 12710-12710/com.androidstudy I/FruitRecycleAdapter: onBindViewHolder 加载数据 11
04-10 17:10:45.902 12710-12710/com.androidstudy I/FruitRecycleAdapter: onBindViewHolder 加载数据 12
04-10 17:10:46.261 12710-12710/com.androidstudy I/FruitRecycleAdapter: onBindViewHolder 加载数据 13
04-10 17:10:47.125 12710-12710/com.androidstudy I/FruitRecycleAdapter: onBindViewHolder 加载数据 2
04-10 17:10:47.600 12710-12710/com.androidstudy I/FruitRecycleAdapter: onBindViewHolder 加载数据 1
04-10 17:10:47.687 12710-12710/com.androidstudy I/FruitRecycleAdapter: onBindViewHolder 加载数据 0
04-10 17:10:48.552 12710-12710/com.androidstudy I/FruitRecycleAdapter: onBindViewHolder 加载数据 11
04-10 17:10:48.620 12710-12710/com.androidstudy I/FruitRecycleAdapter: onBindViewHolder 加载数据 12
可见在加载数据的时候 onCreateViewHolder 不需要每次都调用。
横屏滚动
- 修改布局文件
<?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="100dp">
<ImageView
android:id="@+id/fruit_image"
android:layout_gravity="center_horizontal"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<TextView
android:id="@+id/fruit_text"
android:layout_gravity="center_horizontal"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</LinearLayout>

layoutManager.setOrientation(LinearLayoutManager.HORIZONTAL);
使得布局横向排列。
public class RecyclerActivity extends Activity {
List<Fruit> fruitList = new ArrayList<>();
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_recyclerview);
initFruits();
RecyclerView recyclerView = (RecyclerView) findViewById(R.id.recycle_view);
LinearLayoutManager layoutManager = new LinearLayoutManager(this);
layoutManager.setOrientation(LinearLayoutManager.HORIZONTAL);
recyclerView.setLayoutManager(layoutManager);
FruitRecycleAdapter fruitRecycleAdapter = new FruitRecycleAdapter(fruitList);
recyclerView.setAdapter(fruitRecycleAdapter);
}
...
}
网格布局
public class RecyclerActivity extends Activity {
List<Fruit> fruitList = new ArrayList<>();
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_recyclerview);
initFruits();
RecyclerView recyclerView = (RecyclerView) findViewById(R.id.recycle_view);
GridLayoutManager layoutManager = new GridLayoutManager(this,4);
// layoutManager.setOrientation(LinearLayoutManager.HORIZONTAL);
recyclerView.setLayoutManager(layoutManager);
FruitRecycleAdapter fruitRecycleAdapter = new FruitRecycleAdapter(fruitList);
recyclerView.setAdapter(fruitRecycleAdapter);
}
...
}
将 LinearLayoutManager 改为,设置每行 4 格


android:layout_width="match_parent"
使用 layout_margin 是的子项之间互留一点间距( layout_margin 设置 view 的上下左右边框的额外空间)。设置 TextView 居左对齐。
public class RecyclerActivity extends Activity {
List<Fruit> fruitList = new ArrayList<>();
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_recyclerview);
initFruits();
RecyclerView recyclerView = (RecyclerView) findViewById(R.id.recycle_view);
StaggeredGridLayoutManager layoutManager = new StaggeredGridLayoutManager(3,
StaggeredGridLayoutManager.VERTICAL);
recyclerView.setLayoutManager(layoutManager);
FruitRecycleAdapter fruitRecycleAdapter = new FruitRecycleAdapter(fruitList);
recyclerView.setAdapter(fruitRecycleAdapter);
}
private void initFruits() {
for (int i = 0; i < 2; i++) {
Fruit apple = new Fruit(getRandomLengthName("Apple"), R.drawable.apple_pic);
fruitList.add(apple);
Fruit banana = new Fruit(getRandomLengthName("Banana"), R.drawable.banana_pic);
fruitList.add(banana);
Fruit orange = new Fruit(getRandomLengthName("Orange"), R.drawable.orange_pic);
fruitList.add(orange);
Fruit watermelon = new Fruit(getRandomLengthName("Watermelon"), R.drawable.watermelon_pic);
fruitList.add(watermelon);
Fruit pear = new Fruit(getRandomLengthName("Pear"), R.drawable.pear_pic);
fruitList.add(pear);
Fruit grape = new Fruit(getRandomLengthName("Grape"), R.drawable.grape_pic);
fruitList.add(grape);
Fruit pineapple = new Fruit(getRandomLengthName("Pineapple"), R.drawable.pineapple_pic);
fruitList.add(pineapple);
Fruit strawberry = new Fruit(getRandomLengthName("Strawberry"), R.drawable.strawberry_pic);
fruitList.add(strawberry);
Fruit cherry = new Fruit(getRandomLengthName("Cherry"), R.drawable.cherry_pic);
fruitList.add(cherry);
Fruit mango = new Fruit(getRandomLengthName("Mango"), R.drawable.mango_pic);
fruitList.add(mango);
}
}
private String getRandomLengthName(String name) {
Random random = new Random();
int length = random.nextInt(20) + 1;
StringBuilder builder = new StringBuilder();
for (int i = 0; i < length; i++) {
builder.append(name);
}
return builder.toString();
}
}
设置 StaggeredGridLayoutManager 排列方向为垂直方向排列,即为每行 3 个,一行排列完成再排列另外一行。
效果:

如果设置为水平方向排列,效果:

android:layout_width="match_parent" android:layout_height="wrap_content"
所以每列 3 个没有平均分配,每行一个也布满整个行。
RecyclerView 的点击事件
RecyclerView 所有的点击事件都是由具体的 view 注册.
首先修改 ViewHolder :
public class ViewHolder extends RecyclerView.ViewHolder{
View fruitView;
ImageView fruitImage;
TextView fruitText;
public ViewHolder(View view) {
super(view);
fruitView = view;
fruitImage = (ImageView)view.findViewById(R.id.fruit_image);
fruitText = (TextView)view.findViewById(R.id.fruit_text);
}
}
在 ViewHolder 中添加 fruitView 变量,保存子项最外层布局的实例.然后在 onCreateViewHolder 中注册点击事件,这里为最外层布局和 ImageView 都注册了点击事件。
public class FruitRecycleAdapter extends RecyclerView.Adapter<ViewHolder> {
private List<Fruit> mFruitList;
public FruitRecycleAdapter(List<Fruit> mFruitList) {
this.mFruitList = mFruitList;
}
@Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext())
.inflate(R.layout.fruit_item, parent, false);
final ViewHolder holder = new ViewHolder(view);
holder.fruitView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
int position = holder.getAdapterPosition();
Fruit fruit = mFruitList.get(position);
Toast.makeText(v.getContext(), "you click 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 = mFruitList.get(position);
Toast.makeText(v.getContext(), "you click image " + fruit.getName(),
Toast.LENGTH_SHORT).show();
}
});
Log.i("FruitRecycleAdapter", "onCreateViewHolder");
return holder;
}
...
}
