前言
使用RecyclerView的好处不必多说,有更好的性能表现,更好的拓展性等等。比起ListView,更推荐使用RecyclerView。
Android Studio中的准备工作
首先在activity_main.xml的design布局中找到Recycler并点击其右侧的下载图标执行依赖导入。
若找不到,可在build.gradle(有2个,选字最多的那个)的dependencies中,加入以下一行代码
implementation 'androidx.recyclerview:recyclerview:1.1.0'
垂直滚动
创建MyItem.java类
用于列表中每一个item实例持有其自身的文本和图片id。
package com.example.recyclerviewdemo;
public class MyItem {
private String name;
private int imageId;
public MyItem(String name, int imageId){
this.name=name;
this.imageId=imageId;
}
public String getName(){
return name;
}
public int getImageId(){
return imageId;
}
}
创建myitem.xml布局文件
其中android:orientation="horizontal"指定每个item中的布局为水平布局,64dp则限制了ImageView的尺寸
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<ImageView
android:id="@+id/item_image"
android:layout_width="64dp"
android:layout_height="64dp">
</ImageView>
<TextView
android:id="@+id/item_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:layout_marginLeft="10dp">
</TextView>
</LinearLayout>
为MyItem创建其独特的适配器MyItemAdapter.java
package com.example.recyclerviewdemo;
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 MyItemAdapter extends RecyclerView.Adapter<MyItemAdapter.ViewHolder>{
private List<MyItem> myItemList;
/**
* 定义内部类ViewHolder 继承自 RecyclerView.ViewHolder
*/
static class ViewHolder extends RecyclerView.ViewHolder{
ImageView itemImage;
TextView itemName;
/**
* 构造参数中传入参数view
* @param view RecyclerView子项的最外层布局
*/
public ViewHolder(View view){
super(view);
itemImage=view.findViewById(R.id.item_image);
itemName=view.findViewById(R.id.item_name);
}
}
/**
* 传入数据源
* @param itemList
*/
public MyItemAdapter(List<MyItem> itemList){
myItemList=itemList;
}
/**
* 重写onCreateViewHolder方法,用于创建ViewHolder实例,并把加载出来的布局传入到构造函数中,最后将ViewHolder的实例返回
* @param parent
* @param viewType
* @return
*/
@NonNull
@Override
public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
View view= LayoutInflater.from(parent.getContext()).inflate(R.layout.myitem,parent,false);
ViewHolder holder=new ViewHolder(view);
return holder;
}
/**
* 对RecyclerView子项的数据进行赋值,会在每个子项被滚动到屏幕内的时候执行
* @param holder
* @param position
*/
@Override
public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
MyItem item=myItemList.get(position);
holder.itemImage.setImageResource(item.getImageId());
holder.itemName.setText(item.getName());
}
/**
* 告诉RecyclerView有多少子项
* @return
*/
@Override
public int getItemCount() {
return myItemList.size();
}
}
在MainActivity.java中加入相关代码
package com.example.recyclerviewdemo;
import androidx.appcompat.app.AppCompatActivity;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import android.os.Bundle;
import java.util.ArrayList;
import java.util.List;
public class MainActivity extends AppCompatActivity {
private List<MyItem> myItemList=new ArrayList<>();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//初始化列表数据
initItem();
//获取RecyclerView的实例
RecyclerView recyclerView=findViewById(R.id.recycler_view);
//指定RecyclerView的布局方式
LinearLayoutManager layoutManager=new LinearLayoutManager(this);
recyclerView.setLayoutManager(layoutManager);
//为RecyclerView设置Adapter
MyItemAdapter adapter=new MyItemAdapter(myItemList);
recyclerView.setAdapter(adapter);
}
private void initItem(){
for (int i = 0; i < 30; i++) {
MyItem item1=new MyItem("item1",R.drawable.icon);
myItemList.add(item1);
MyItem item2=new MyItem("item2",R.drawable.java);
myItemList.add(item2);
}
}
}
运行效果
水平滚动
修改自定义布局文件myitem.xml
其中<!-- ->中的内容为说明代码的意图,请自行去除,否则可能无法通过编译
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical" <!-- 设置item为垂直布局 -->
android:layout_width="100dp" <!-- 设置item的宽度为100dp,防止有些item过宽 -->
android:layout_height="wrap_content">
<ImageView
android:id="@+id/item_image"
android:layout_gravity="center_horizontal" <!-- 设置图片位于item水平居中 -->
android:layout_width="64dp"
android:layout_height="64dp">
</ImageView>
<TextView
android:id="@+id/item_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal" <!-- 设置文本位于item水平居中 -->
android:layout_marginTop="10dp"> <!-- 设置文本与上方的图片间隔10dp -->
</TextView>
</LinearLayout>
修改MainActivity.java
新增一句代码layoutManager.setOrientation(LinearLayoutManager.HORIZONTAL);
此代码将设置布局管理器layoutManager的布局方向为水平
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//初始化列表数据
initItem();
//获取RecyclerView的实例
RecyclerView recyclerView=findViewById(R.id.recycler_view);
//指定RecyclerView的布局方式 水平布局
LinearLayoutManager layoutManager=new LinearLayoutManager(this);
layoutManager.setOrientation(LinearLayoutManager.HORIZONTAL);//!此句为新增
recyclerView.setLayoutManager(layoutManager);
//为RecyclerView设置Adapter
MyItemAdapter adapter=new MyItemAdapter(myItemList);
recyclerView.setAdapter(adapter);
}
运行效果
实现瀑布流布局
由于Android已经为我们配置好了相关模板,因此我们只需要修改非常小一部分内容就能达到瀑布流布局的效果
修改myitem.xml代码
<?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="wrap_content"
android:layout_margin="5dp" <!-- 每个item之间间隔5dp -->
>
<ImageView
android:id="@+id/item_image"
android:layout_gravity="center_horizontal"
android:layout_width="64dp"
android:layout_height="64dp">
</ImageView>
<TextView
android:id="@+id/item_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:layout_marginTop="10dp">
</TextView>
</LinearLayout>
修改MainActivity.java代码
新增以下方法,用于得到一个随机长度的字符串,用于item的文本显示
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();
}
修改以下代码
在OnCreate方法中修改
LinearLayoutManager layoutManager=new LinearLayoutManager(this);
为
StaggeredGridLayoutManager layoutManager=new StaggeredGridLayoutManager(3,StaggeredGridLayoutManager.VERTICAL);
用于指定RecyclerView的布局方式为 3列纵向瀑布流布局
在initItem方法中调用getRandomLengthName方法
修改后,MainActivity.java代码如下
package com.example.recyclerviewdemo;
import androidx.appcompat.app.AppCompatActivity;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import androidx.recyclerview.widget.StaggeredGridLayoutManager;
import android.os.Bundle;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
public class MainActivity extends AppCompatActivity {
private List<MyItem> myItemList=new ArrayList<>();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//初始化列表数据
initItem();
//获取RecyclerView的实例
RecyclerView recyclerView=findViewById(R.id.recycler_view);
//指定RecyclerView的布局方式为 3列纵向瀑布流布局
StaggeredGridLayoutManager layoutManager=new
StaggeredGridLayoutManager(3,StaggeredGridLayoutManager.VERTICAL);
recyclerView.setLayoutManager(layoutManager);
//为RecyclerView设置Adapter
MyItemAdapter adapter=new MyItemAdapter(myItemList);
recyclerView.setAdapter(adapter);
}
private void initItem(){
for (int i = 0; i < 30; i++) {
MyItem item1=new MyItem(
getRandomLengthName("item1"),R.drawable.icon);
myItemList.add(item1);
MyItem item2=new MyItem(
getRandomLengthName("item2"),R.drawable.java);
myItemList.add(item2);
}
}
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();
}
}
运行效果
为RecyclerView添加OnClickListner实现单击事件监听
与ListView不同的是,RecyclerView没有提供类似于setOnItemClickListener()这样的注册监听器的方法,而是需要我们自己给子项具体的View去注册点击事件,所以实现起来稍微有点复杂。
这样做的好处是,我们可以知道用户单击了item中哪个部分,而ListView不行。ListView对于item中的按钮单击,处理起来远远没有RecycerView方便。所以RecyclerView直接去除了setOnItemClickListener()方法,所有的点击事件都由具体的View去注册。
#修改MyItemAdapter.java中的代码
在类ViewHolder中新增成员View itemView;用于持有item自身的view。
/**
* 定义内部类ViewHolder 继承自 RecyclerView.ViewHolder
*/
static class ViewHolder extends RecyclerView.ViewHolder{
View itemView;//新增
ImageView itemImage;
TextView itemName;
/**
* 构造参数中传入参数view
* @param view RecyclerView子项的最外层布局
*/
public ViewHolder(View view){
super(view);
itemView=view;//新增
itemImage=view.findViewById(R.id.item_image);
itemName=view.findViewById(R.id.item_name);
}
}
修改OnCreateViewHolder()方法
将ViewHolder holder=new ViewHolder(view);
声明为final,final ViewHolder holder=new ViewHolder(view);
这样匿名内部类中可以获取到holder变量
由于前边我们的item的viewholder可以持有自身的view,因此我们为item的view设置单击监听器,实现setOnClickListener()。同理,也为item的Image设置单击监听器。我们没有给item的TextView设置监听器,因此item的文本被单击之后,会被item的最外层布局捕获到,作为view的单击事件处理。
/**
* 重写onCreateViewHolder方法,用于创建ViewHolder实例,并把加载出来的布局传入到构造函数中,最后将ViewHolder的实例返回
* @param parent
* @param viewType
* @return
*/
@NonNull
@Override
public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
View view= LayoutInflater.from(parent.getContext()).inflate(R.layout.myitem,parent,false);
//声明为final,这样匿名内部类中也能获取
final ViewHolder holder=new ViewHolder(view);
//重写onClick方法,为item的视图设置单击监听器
holder.itemView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//获得被单击的item 的实例
int position=holder.getAdapterPosition();
MyItem item=myItemList.get(position);
Toast.makeText(v.getContext(),"单击view,item名称为"+item.getName(),Toast.LENGTH_SHORT).show();
}
});
//重写OnClick方法,为item的图片也设置单击监听器
holder.itemImage.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
int position=holder.getAdapterPosition();
MyItem item=myItemList.get(position);
Toast.makeText(v.getContext(),"单击image,item名称为"+item.getName(),Toast.LENGTH_SHORT).show();
}
});
return holder;
}
运行效果
单击item的图片,Toast显示单击了Image
单击item的文本,Toast显示单击了View
资料参考自《第一行代码》作者:郭霖
由于本人才疏学浅,所以本文仅用于记录及分享相关用法,并将持续更新更多的用法