安卓前端研习——RecycleView轻松解决消息列表
消息列表是大部分应用都会使用到的一个组件,诸如聊天列表,通知列表,资源列表等都可以看到类消息列表的不定条目的列表组件,很多教程会使用到listview来实现消息列表,但是使用较新的RecycleView可以更为轻松地实现这个组件的布局。
实现后效果如图:

添加支持库到项目中
按照惯例我们需要将所需的依赖添加到项目中,在build中加入以下代码:
dependencies {
implementation 'com.android.support:recyclerview-v7:28.0.0'
}
配置布局文件资源
首先我们要创建一个xml文件,其中包含一个RecycleView,我们使用一个简单的布局,布局文件仅包含一个RecycleView,例如:
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
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">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/new_src_container"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:padding="15dp"
app:layout_constraintWidth_percent="1"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
实例化控件以及其他储存
首先需要获取布局文件中的控件,并进行实例化,初始化包含item的列表。代码如下:
private void initRecyclerView(){
new_src_container = (RecyclerView) getActivity().findViewById(R.id.new_src_container);
new_src_container.setHasFixedSize(true);
layoutManager = new LinearLayoutManager(getActivity());
new_src_container.setLayoutManager(layoutManager);
src_list = new ArrayList<>();
}
然后我们需要构造消息item的布局以及实例化代码。
item布局的设置
创建一个新的xml文件对每一个公式化的item进行布局。
我创建的item布局大概如图,有一个显示文件类型的图标,一个显示文件大小的文本,一个显示文件标题的文本,一个显示描述的文本,和一个标识是否免费的组合控件,和一个覆盖整个布局的按钮。

具体布局代码如下:
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="100dp"
android:layout_marginTop="20dp"
android:id="@+id/src_item"
xmlns:app="http://schemas.android.com/apk/res-auto">
<ImageView
android:id="@+id/src_item_bg"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scaleType="fitXY"
android:src="@drawable/src_item_bg"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<ImageView
android:id="@+id/src_type_pic"
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_marginTop="16dp"
android:src="@drawable/txt"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHeight_percent="0.5"
app:layout_constraintHorizontal_bias="0.068"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintWidth_percent="0.1277" />
<TextView
android:id="@+id/src_size"
android:layout_width="0dp"
android:layout_height="0dp"
android:text="Unknown"
android:textSize="12sp"
android:gravity="center"
android:singleLine="true"
android:textColor="#818181"
app:layout_constraintEnd_toEndOf="@+id/src_type_pic"
app:layout_constraintHeight_percent="0.2084"
app:layout_constraintStart_toStartOf="@+id/src_type_pic"
app:layout_constraintTop_toBottomOf="@+id/src_type_pic"
app:layout_constraintWidth_percent="0.1265" />
<TextView
android:id="@+id/src_title"
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_marginStart="20dp"
android:layout_marginLeft="20dp"
android:singleLine="true"
android:text="Unknown title"
android:textColor="#616161"
android:textSize="16sp"
app:layout_constraintBottom_toBottomOf="@+id/src_type_pic"
app:layout_constraintHeight_percent="0.2455"
app:layout_constraintStart_toEndOf="@+id/src_type_pic"
app:layout_constraintTop_toTopOf="@+id/src_type_pic"
app:layout_constraintVertical_bias="0.038"
app:layout_constraintWidth_percent="0.5576" />
<TextView
android:id="@+id/src_describe"
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_marginStart="20dp"
android:layout_marginLeft="20dp"
android:text="Undefine describe"
android:textColor="#818181"
android:textSize="12sp"
app:layout_constraintBottom_toTopOf="@+id/src_size"
app:layout_constraintHeight_percent="0.2055"
app:layout_constraintStart_toEndOf="@+id/src_type_pic"
app:layout_constraintTop_toTopOf="@+id/src_type_pic"
app:layout_constraintVertical_bias="1.0"
app:layout_constraintWidth_percent="0.5576" />
<ImageView
android:id="@+id/free_tag"
android:layout_width="0dp"
android:layout_height="0dp"
android:src="@drawable/free_bg"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHeight_percent="0.2083"
app:layout_constraintHorizontal_bias="0.876"
app:layout_constraintStart_toEndOf="@+id/src_type_pic"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintWidth_percent="0.0833" />
<ImageView
android:id="@+id/free_tag_text"
android:layout_width="0dp"
android:layout_height="0dp"
android:src="@drawable/free_text"
app:layout_constraintBottom_toBottomOf="@+id/free_tag"
app:layout_constraintEnd_toEndOf="@+id/free_tag"
app:layout_constraintHeight_percent="0.12"
app:layout_constraintStart_toStartOf="@+id/free_tag"
app:layout_constraintTop_toTopOf="@+id/free_tag"
app:layout_constraintWidth_percent="0.0444" />
<ImageView
android:id="@+id/src_more"
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_marginStart="48dp"
android:layout_marginLeft="48dp"
android:scaleType="fitCenter"
android:src="@drawable/src_item_more"
app:layout_constraintBottom_toBottomOf="@+id/src_size"
app:layout_constraintHeight_percent="0.1667"
app:layout_constraintStart_toEndOf="@+id/src_title"
app:layout_constraintTop_toTopOf="@+id/src_type_pic"
app:layout_constraintVertical_bias="0.463"
app:layout_constraintWidth_percent="0.037" />
<Button
android:id="@+id/src_more_btn"
android:layout_width="0dp"
android:layout_height="0dp"
android:background="@null"
style="?android:attr/borderlessButtonStyle"
android:theme="@style/BigRippleWhite"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHeight_percent="1"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintWidth_percent="1" />
</androidx.constraintlayout.widget.ConstraintLayout>
这里需要注意的是,布局文件中的android:layout_height不能使用默认的march_parent,因为这是一个item的大小,高度要根据需求进行调整。若要实现每个item中的间隔只需要给布局文件中加入 android:layout_marginTop="20dp"即可,具体间隔多少可自行调整。
创建item的实例化Java代码
首先明确item中有什么需要设置值的控件,然后对每一个控件实现set和get方法即可,代码如下:
package com.example.quanta;
import android.graphics.drawable.Drawable;
public class SrcItem {
private Drawable typePic;
private String title;
private String describe;
private String size;
private boolean isFree;
public SrcItem(Drawable typePic, String title, String describe, String size, boolean isFree){
this.typePic = typePic;
this.title = title;
this.describe = describe;
this.size = size;
this.isFree = isFree;
}
public Drawable getTypePic() {
return typePic;
}
public void setTypePic(Drawable typePic) {
this.typePic = typePic;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getDescribe() {
return describe;
}
public void setDescribe(String describe) {
this.describe = describe;
}
public String getSize() {
return size;
}
public void setSize(String size) {
this.size = size;
}
public boolean isFree() {
return isFree;
}
public void setFree(boolean free) {
isFree = free;
}
}
实现创建item并加入储存中的逻辑
因为我的控件需要对不同的文件类型使用不同的图片(资源文件),所以代码中会有对不同类型的处理,具体代码如下:
public void createItem(String type, String title, String describe, String size, boolean isFree){
Drawable typePic = null;
Resources resources = getResources();
switch (type){
case "TXT":
typePic = resources.getDrawable(R.drawable.txt);
break;
case "DOC":
typePic = resources.getDrawable(R.drawable.doc);
break;
case "PPT":
typePic = resources.getDrawable(R.drawable.ppt);
break;
case "RAR":
typePic = resources.getDrawable(R.drawable.rar);
break;
default:
break;
}
SrcItem srcItem = new SrcItem(typePic, title, describe, size, isFree);
src_list.add(srcItem);
addIntoContainer();
}
public void addIntoContainer(){
mAdapter = new SrcListRecycleViewAdapter(src_list,getActivity());
new_src_container.setAdapter(mAdapter);
}
需要注意的是SrcListRecycleViewAdapter是自定义的RecycleViewAdapter,接下来将会讲解配置自定义RecycleViewAdapter的方法。
配置自定义RecycleViewAdapter
因为内置的RecycleViewAdapter并不能完成我们需要的所有功能,所以我们要自定义一个RecycleViewAdapter继承自原版的RecycleViewAdapter。
具体代码如下:
package com.example.quanta;
import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
import java.util.List;
public class SrcListRecycleViewAdapter extends RecyclerView.Adapter<SrcListRecycleViewAdapter.MyViewHolder> {
private List<SrcItem> itemList;
private Context context;
public static class MyViewHolder extends RecyclerView.ViewHolder{
TextView src_title;
TextView src_describe;
TextView src_size;
ImageView isFreeBG;
ImageView isFreeText;
ImageView typePic;
public MyViewHolder(@NonNull View itemView) {
super(itemView);
src_title = itemView.findViewById(R.id.src_title);
src_describe = itemView.findViewById(R.id.src_describe);
src_size = itemView.findViewById(R.id.src_size);
isFreeBG = itemView.findViewById(R.id.free_tag);
isFreeText = itemView.findViewById(R.id.free_tag_text);
typePic = itemView.findViewById(R.id.src_type_pic);
}
}
public SrcListRecycleViewAdapter(List<SrcItem> itemList, Context context){
this.context = context;
this.itemList = itemList;
}
@NonNull
@Override
public MyViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.src_item,
parent, false);
return new MyViewHolder(v);
}
@Override
public void onBindViewHolder(@NonNull MyViewHolder holder, int position) {
SrcItem srcItem = this.itemList.get(position);
holder.src_title.setText(srcItem.getTitle());
holder.src_size.setText(srcItem.getSize());
holder.src_describe.setText(srcItem.getDescribe());
holder.typePic.setImageDrawable(srcItem.getTypePic());
if (!srcItem.isFree()){
holder.isFreeBG.setVisibility(View.INVISIBLE);
holder.isFreeText.setVisibility(View.INVISIBLE);
}
}
@Override
public int getItemCount() {
return this.itemList.size();
}
}
其中onCreateViewHolder方法中的inflate应填入自己的item布局的标识,这个方法是用于创建一个自定义的view并且返回。
在方法onBindViewHolder中会调用item对象的相关set方法,对指定的控件的值进行修改。因为我的布局的特殊性,isfree字段仅用于判断是否免费,并不对布局中的组件进行赋值操作,而是进行显示或者隐藏的操作,因此使用到了判断语句并实现功能,具体代码为:
if (!srcItem.isFree()){
holder.isFreeBG.setVisibility(View.INVISIBLE);
holder.isFreeText.setVisibility(View.INVISIBLE);
}
创建item并导入RecycleView中
我们只需要简单的调用createItem方法即可完成所有一系列的操作,其中createItem中传入的值即修改item布局中值所需要的值,简单的代码如下:
for (int i = 0; i < 20; i++){
createItem("DOC", "测试", "小测试代码", "189KB", false);
}
这段代码便可以创建20个一样的item并显示出来。效果如图:


