郭霖先生的《第一行代码》第三版已经出来了,系统讲解 Kotlin 开发 Android 的方法。我认为 Android 初学者(比如我)还是要从 Java 开始,故采用 Java 学习一下第一行代码第二版有关知识。
本文讲解 RecyclerView。
在 app 模块下的 build.gradle 文件中需要导入以下依赖。如果是单纯手机开发,使用第一条 implementation
即可。
dependencies {
implementation "androidx.recyclerview:recyclerview:1.1.0"
// For control over item selection of both touch and mouse driven selection
implementation "androidx.recyclerview:recyclerview-selection:1.1.0-rc01"
}
之后,在布局文件中可以用这样的办法使用 RecyclerView:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recycler_veiw"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
LinearLayout>
为了测试,我们编写一个 Person 类,成员变量包含人物的姓名和图片。其中图片不用采用 ImageView 对象,只要给出图片的 id 即可。
public class Person {
private String name;
private int imageId;
// 这个 imageId 实际上就是图片资源文件夹中的某一张图片的名字,系统会给图片分配一个 id,比如 kami.png 对应的 id 是 7759
public Person(String name, int imageId) {
this.name = name;
this.imageId = imageId;
}
String getName() {
return name;
}
int getImageId() {
return imageId;
}
}
设置布局有关的内容不做讲解。
RecyclerView 同样需要使用 Adapter 适配器来传数据。我们一般要自定义适配器,就要让自己创建的 Adapter 类继承 RecyclerView.Adapter。如下,创建一个人物适配器 PersonAdapter,声明框架代码如下:
public class PersonAdapter extends RecyclerView.Adapter<PersonAdapter.ViewHolder> {
static class ViewHolder extends RecyclerView.ViewHolder {
// ViewHolder 的逻辑
}
public PersonAdapter(List<Person> personList) {
// 构造函数
}
}
RecyclerView.Adapter 的泛型类型指定为 PersonAdapter 中的内部类 ViewHolder,需要继承 RecyclerView.ViewHolder 来使用。
ViewHolder 需要控制子项的布局,比如 这里就管理的是人物列表中的一张张卡片,所以构造函数需要传入一个 View 类型的参数,接着就可以通过这个参数调用 findViewById() 方法获取卡片中的各个元素。
给出了类成员和构造函数之后,自定义的 ViewHolder 类完成。
接着,PersonAdapter 类要重写 Recycler.View 中的方法,因为 RecyclerView.View 是抽象类,可以看到其声明原型如下:
/**
* Base class for an Adapter
*
* Adapters provide a binding from an app-specific data set to views that are displayed
* within a {@link RecyclerView}.
*
* @param A class that extends ViewHolder that will be used by the adapter.
*/
public abstract static class Adapter<VH extends ViewHolder> {
}
其泛型是 VH,代表的是一个从抽象类 ViewHolder 继承的一个 viewholder 类型。这里我们指定类型为继承了 RecyclerView.ViewHolder 的 ViewHolder 内部类,即 PersonAdapter.ViewHolder。这里为了防止与抽象类重名,写出了完整类名。
之后需要重写抽象类中的方法,分别是:
可以理解为,Adapter 适配器虽然要向 RecyclerView 传数据,但是我们写的自定义的 Adapter 本身不能识别给它的数据(子项的布局多种多样,Adapter 没有那么智能)。为此需要先做一个 ViewHolder,确定布局中的元素,将它们存在 ViewHolder 对象的成员变量中,Adapter 通过 ViewHolder 对象获取数据。
PersonAdapter 需要重写的第一个方法,onCreateViewHolder,就是创建 ViewHolder 实例的,步骤是:
创建 View 对象,获取子项的布局,再将 View 对象传入 ViewHolder 构造函数,这样创建出的 ViewHolder 对象里就存储了各个子项的信息,再把 ViewHolder 对象返回。代码如下:
public ViewHolder onCreateViewHolder(ViewGroup viewGroup, int viewType) {
// 传入子项布局构造 view
View view = LayoutInflater.from(viewGroup.getContext())
.inflate(R.layout.person_layout, viewGroup, false);
// 传入 view 构造 ViewHolder 对象并返回
return new ViewHolder(view);
}
第二个方法 onBindViewHolder 是用来给 RecyclerView 子项赋值的,获取了 ViewHolder 对象后,一开始各个子项的属性(比如图片、名字等)并未被赋值,在这个函数里面就可以给它们指定值。**这个方法会在每个子项滚动到屏幕内时执行。**代码参考如下:
@Override
public void onBindViewHolder(ViewHolder holder, int position) {
Person person = personList.get(position);
// 给 holder 的图片属性赋值,传入图片 id
holder.personImage.setImageResource(person.getImageId());
// 给 holder 的名字属性赋值,传入一个字符串
holder.personName.setText(person.getName());
}
最后一个方法返回的是列表中子项的总数。我们可以定义一个 Person 类列表提供数据,每一个列表元素都作为一个子项,那么这个方法只要返回列表的长度即可。
int getItemCount() {
// 这个 list 是 适配器中的 Person 列表
return List.size;
}
主函数比较简单,可以先创建一个 Person 列表,加载数据进去,接着获取 RecyclerView 实例,在设置一个 LinearLayoutManager 实例(子项的布局是线性布局)传给 RecyclerView,指定 RectclerView 为线性布局方式,接着创建一个 PersonAadapter 类型的适配器,传入 Person 列表,再给 RecyclerView 设置好这个适配器就可以了,代码展示如下:
public class MainActivity extends AppCompatActivity {
private List<Person> personList = new ArrayList<>();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initPersons();
RecyclerView recyclerView = findViewById(R.id.recycler_veiw);
LinearLayoutManager layoutManager = new LinearLayoutManager(this);
recyclerView.setLayoutManager(layoutManager);
PersonAdapter adapter = new PersonAdapter(personList);
recyclerView.setAdapter(adapter);
}
private void initPersons() {
for (int i = 0; i < 9; i++) {
Person kamiyama = new Person("神山識", R.drawable.kamiyama_satoru);
personList.add(kamiyama);
Person shari = new Person("夏莉", R.drawable.shari);
personList.add(shari);
}
}
}
RecyclerView 的难点在于理解自定义适配器的过程,这里做一下总结,也方便我之后复习:
自定义一个 Adapter 继承自 RecyclerView.Adapter,RecyclerView.Adapter 需指定一个 ViewHolder 用来获取子项内部的数据,将自己的 ViewHolder 定义为内部类比较方便;
自定义 Adapter 需要重写三个方法,onCreateViewHolder,用来创建一个 ViewHolder,参数类型为 View,代表子项的布局,布局 view 传给 ViewHolder 的构造函数,构造出一个 ViewHolder 对象并返回该对象;
onBindViewHodler 方法,获取了 ViewHolder 对象之后,要给其中的成员变量赋值,即给子项对应的各个布局参数赋值,为此 ViewHolder 每个对象都有一个 setResource 方法;
getItemCount 方法,返回的是 RecyclerView 子项的数目。
接着来看 ViewHolder 怎么写。Adapter 只是一个 存储数据的适配器,具体各个数据的对应还需要使用 ViewHolder。ViewHolder 的思路是,先获取子项的布局,再将子项的布局中的各个元素分别对应到 它的成员变量中(构造函数完成),这样,通过修改 ViewHolder 的成员变量,等于我们也对子项布局的内容进行了操作。
上面的 Person 类的例子我补充了一下,做了一个简单的 demo 出来,放在了 GitHub 上。
点我直达
希望对大家有所帮助。感谢郭霖先生带我走进 Android 开发的大门。