ListView只能纵向显示而且还要优化(即手动写代码实现缓存好子项布局文件及其里面控件),而Recycler除了优化好这些,还可以实现横向/纵向/网格/瀑布流布局显示(关键:只需改RecyclerVIew.setLayoutManager(布局类对象实例)中的布局类对象,这个参数的布局不同,就可以实现不同子项显示方式:横向/纵向/网格/瀑布流布局
- 纵向LinearLayoutManager.VERTICAL:
LinearLayoutManager默认VERTICAL排列,setLayoutManager(布局类对象实例)传入LinearLayoutManager实例对象即可
- 横向LinearLayoutManager.HORIZONTAL:
先LinearLayoutManager.setOrientation(LinearLayoutManager.HORIZONTAL),在setLayoutManager(布局类对象)传入LinearLayoutManager实例对象即可
- 网格GridLayoutManager:
setLayoutManager(布局类对象实例)传入GridLayoutManager实例对象即可,可先为GridLayoutManager实例对象设置点属性先
- 瀑布流StaggeredGridLayoutManager:
setLayoutManager(布局类对象实例)传入StaggeredGridLayoutManager实例对象即可,StaggeredGridLayoutManager也可以设置瀑布流的排列是VERTICAL还是HORIZONTAL)
- RecyclerView的基本用法:
①打开app/build.gradle文件,在dependencies闭包内添加RecyclerView的依赖:
implementation 'com.android.support:recyclerview-v7:28.0.0'(V7:28.0.0仅仅是版本),可以通过Android Studio的Design视图直接添加RecyclerView,就会自动提示添加依赖了
②在要使用RecyclerView的XML中添加RecyclerView控件
<android.support.v7.widget.RecyclerView
android:id="@+id/recycler_view"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<android.support.v7.widget.RecyclerView这里要用完整的路径
③定义实体类,作为子项里显示的数据的载体
public class Fruit {
private String name;
private int imageId;
public Fruit(String name, int imageId) {
this.name = name;
this.imageId = imageId;
}
public String getName() {
return name;
}
public int getImageId() {
return imageId;
}
}
④自定义子项布局(这里不同,则子项的样式也不同,而改setLayoutManager(***LayoutManager)仅仅是子项的布局和排列不同)
fruit_item.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"
<ImageView
android:id="@+id/fruit_image"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<TextView
android:id="@+id/fruit_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:layout_marginLeft="10dp" />
</LinearLayout>
⑤自定义适配器类xxxAdapter,并继承RecyclerView.Adapter<泛型为xxxAdapter.ViewHolder>(xxxAdapter.ViewHolder为自定义适配器的静态内部类,用于缓存子项控件的),并重写onCreateViewHolder()、onBindViewHolder()、 getItemCount()
- onCreateViewHolder():创建并返回ViewHolder实例,并且参数是动态加载进来的子项布局
- onBindViewHolder:对RecyclerView的子项的数据进行赋值,当子项滚到屏幕都会被调用
参数说明:
ViewHolder viewHolder:缓存了子项中控件的静态内部类,这里是ImageView fruitImage; TextView fruitName;
int i:i是滚入刚到屏幕内子项的编号 - getItemCount():返回RecyclerView子项总数
public class FruitAdapter extends RecyclerView.Adapter<FruitAdapter.ViewHolder> {
private List<Fruit> mFruitList;//用于存放数据的实体类数组
//定义内部类ViewHolder,并继承RecyclerView.ViewHolder
// 作用:用于缓存RecyclerView的子项布局文件中控件
static class ViewHolder extends RecyclerView.ViewHolder{
ImageView fruitImage; //用于子项中图片控件的缓存
TextView fruitName; //用于子项中名字控件的缓存
//ViewHolder构造函数,findViewById()获取子项布局中控件,然后缓存
// 参数View itemView通常是RecyclerView的子项的最外层布局,或者就是子项布局
public ViewHolder(@NonNull View itemView) {
super(itemView);
fruitImage=(ImageView)itemView.findViewById(R.id.fruit_image);
fruitName=(TextView)itemView.findViewById((R.id.fruit_name));
}
}
//自定义适配器构造函数
//参数List<Fruit> fruitList是要传入的数据
public FruitAdapter(List<Fruit> fruitList){
mFruitList=fruitList;
}
//onCreateViewHolder():创建并返回ViewHolder实例,并且参数是动态加载进来的子项布局
@NonNull
@Override
public ViewHolder onCreateViewHolder(@NonNull ViewGroup viewGroup, int i) {
//动态加载布局
View view= LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.fruit_item,viewGroup,false);
//创建ViewHolder实例,参数为刚加载进来的子项布局
ViewHolder viewHolder=new ViewHolder(view);//这样,子项布局里面的控件就缓存到了ViewHolder
return viewHolder;
}
//onBindViewHolder:对RecyclerView的子项的数据进行赋值,当子项滚到屏幕都会被调用
//参数说明:
//ViewHolder viewHolder:缓存了子项中控件的静态内部类,这里是ImageView fruitImage; TextView fruitName;
//int i:i是滚入刚到屏幕内子项的编号
public void onBindViewHolder(ViewHolder viewHolder, int i) {
Fruit fruit=mFruitList.get(i); //获取实体类数组中数据
//将数据bind到子项中控件(子项控件已缓存到了ViewHolder了)
viewHolder.fruitName.setText(fruit.getName());
viewHolder.fruitImage.setImageResource(fruit.getImageId());
}
//作用:返回RecyclerView子项总数
@Override
public int getItemCount() {
return mFruitList.size();
}
}
⑥使用:
* 思路还是:
* 数据通过适配器Adapter对象(构造参数传入)(即数据放进Adapter对象)
* 仅仅多了这个:RecyclerView.setLayoutManager(***LayoutManager)添加布局管理器
* 然后使用RecyclerView.setAdapter(apdapter)将适配器对象传给RecyclerView对象
public class MainActivity extends AppCompatActivity {
private List<Fruit> fruitList=new ArrayList<>();//用于子项数据的数组
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initFruits();//初始化数据
RecyclerView recyclerView=(RecyclerView)findViewById(R.id.recycler_view);
//设置RecyclerView的布局管理,布局管理者不同,RecyclerView的排列就不同
/*横向滚动
LinearLayoutManager layoutManager=new LinearLayoutManager(this);//LinearLayout默认VERTICAL排列
layoutManager.setOrientation(LinearLayoutManager.HORIZONTAL);//将排列方式设为水平
*/
/*瀑布流滚动*/
//StaggeredGridLayoutManager layoutManager=new StaggeredGridLayoutManager(3,1);
/*网格RecyclerView*/
GridLayoutManager layoutManager=new GridLayoutManager(this,4,0,false);
recyclerView.setLayoutManager(layoutManager);
FruitAdapter fruitAdapter=new FruitAdapter(fruitList);//创建适配器对象,并传入数据
recyclerView.setAdapter(fruitAdapter);//为RecyclerView添加带有数据的适配器,从而传给RecyclerView
}
private void initFruits() {
for (int i = 0; i < 2; i++) {
Fruit apple = new Fruit("Apple", R.drawable.apple_pic);
fruitList.add(apple);
Fruit banana = new Fruit("Banana", R.drawable.banana_pic);
fruitList.add(banana);
Fruit orange = new Fruit("Orange", R.drawable.orange_pic);
fruitList.add(orange);
Fruit watermelon = new Fruit("Watermelon", R.drawable.watermelon_pic);
fruitList.add(watermelon);
Fruit pear = new Fruit("Pear", R.drawable.pear_pic);
fruitList.add(pear);
Fruit grape = new Fruit("Grape", R.drawable.grape_pic);
fruitList.add(grape);
Fruit pineapple = new Fruit("Pineapple", R.drawable.pineapple_pic);
fruitList.add(pineapple);
Fruit strawberry = new Fruit("Strawberry", R.drawable.strawberry_pic);
fruitList.add(strawberry);
Fruit cherry = new Fruit("Cherry", R.drawable.cherry_pic);
fruitList.add(cherry);
Fruit mango = new Fruit("Mango", R.drawable.mango_pic);
fruitList.add(mango);
}
}
这里
LinearLayoutManager layoutManager=new LinearLayoutManager(this);//LinearLayout默认VERTICAL排列
recyclerView.setLayoutManager(layoutManager);
.实现的是纵向滚动RecyclerView,和ListVIew差不多
只需改RecyclerVIew.setLayoutManager(布局类对象实例)中的布局类对象,这个参数的布局不同,就可以实现不同子项显示方式:横向/纵向/网格/瀑布流布局
所以:
- 横向滚动:只需将LinearLayoutManager默认的VERTICAL排列改成HORIZONTAL就行了
LinearLayoutManager.setOrientation(LinearLayoutManager.HORIZONTAL);//设置排列方式为水平
...
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initFruits();//初始化数据
RecyclerView recyclerView=(RecyclerView)findViewById(R.id.recycler_view);
//设置RecyclerView的布局管理,布局管理者不同,RecyclerView的排列就不同
LinearLayoutManager layoutManager=new LinearLayoutManager(this);//LinearLayout默认VERTICAL排列
LinearLayoutManager.setOrientation(LinearLayoutManager.HORIZONTAL);//设置排列方式为水平
recyclerView.setLayoutManager(layoutManager);
FruitAdapter fruitAdapter=new FruitAdapter(fruitList);//创建适配器对象,并传入数据
recyclerView.setAdapter(fruitAdapter);//为RecyclerView添加带有数据的适配器,从而传给RecyclerView
}
...
- 瀑布流RecycleeView:只需改RecyclerVIew.setLayoutManager(布局类对象实例)中的布局类对象改为StaggeredGridLayoutManager即可
...
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initFruits();//初始化数据
RecyclerView recyclerView=(RecyclerView)findViewById(R.id.recycler_view);
//设置RecyclerView的布局管理,布局管理者不同,RecyclerView的排列就不同
StaggerGridLayoutManager layoutManager=new StaggerGridLayoutManager(3,StaggerGridLayoutManager.VERTICAL);//创建3列,垂直排列的瀑布流对象
recyclerView.setLayoutManager(layoutManager);
FruitAdapter fruitAdapter=new FruitAdapter(fruitList);//创建适配器对象,并传入数据
recyclerView.setAdapter(fruitAdapter);//为RecyclerView添加带有数据的适配器,从而传给RecyclerView
}
...
- 网格布局:只需改RecyclerVIew.setLayoutManager(布局类对象实例)中的布局类对象改为GridLayoutManager即可
GridLayoutManager layoutManager=new GridLayoutManager(this,4,0,false);
2. RecyclerView的点击事件:
RecyclerView并没有ListView那setOnItemClickListener()方法,因为这个方法很鸡肋,注册的是子项的点击事件,当如果要点击子项中具体某个控件时,可以实现但麻烦。
所以RecyclerView直接摒弃子项点击事件的监听器,所有的点击事件都有具体View自己去注册。
RecyclerView注册点击事件在自定义适配器中的onCreateViewHolder(ViewGroup viewGroup, int i)中实现就行了,因为这个方法是用来动态加载子项布局文件、创建ViewHolder对象并将子项布局及其里面控件缓存,所以可以为子项布局注册监听器也可为控件注册
public class FruitAdapter extends RecyclerView.Adapter<FruitAdapter.ViewHolder> {
private List<Fruit> mFruitList;//用于存放数据的实体类数组
//定义内部类ViewHolder,并继承RecyclerView.ViewHolder
// 作用:用于缓存RecyclerView的子项布局文件中控件
static class ViewHolder extends RecyclerView.ViewHolder{
View fruitView; //用于存储子项布局
ImageView fruitImage; //用于子项中图片控件的缓存
TextView fruitName; //用于子项中名字控件的缓存
//ViewHolder构造函数,findViewById()获取子项布局中控件,然后缓存
// 参数View itemView通常是RecyclerView的子项的最外层布局,或者就是子项布局
public ViewHolder(@NonNull View itemView) {
super(itemView);
fruitView=itemView;
fruitImage=(ImageView)itemView.findViewById(R.id.fruit_image);
fruitName=(TextView)itemView.findViewById((R.id.fruit_name));
}
}
//自定义适配器构造函数
//参数List<Fruit> fruitList是要传入的数据
public FruitAdapter(List<Fruit> fruitList){
mFruitList=fruitList;
}
//onCreateViewHolder():创建并返回ViewHolder实例,并且参数是动态加载进来的子项布局
@NonNull
@Override
public ViewHolder onCreateViewHolder(@NonNull ViewGroup viewGroup, int i) {
//动态加载布局
View view= LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.fruit_item,viewGroup,false);
//创建ViewHolder实例,参数为刚加载进来的子项布局
final ViewHolder viewHolder=new ViewHolder(view);//这样,子项布局里面的控件就缓存到了ViewHolder
/*为子项布局注册点击事件:*/
viewHolder.fruitView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
int position=viewHolder.getAdapterPosition(); //获取点击子项的位置
Fruit fruit=mFruitList.get(position); //根据位置获取指定的数据实体
Toast.makeText(v.getContext(),"触发的是子项的点击事件:"+fruit.getName(),Toast.LENGTH_SHORT).show();
}
});
/* 为子项中控件注册点击事件 */
viewHolder.fruitImage.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
int position=viewHolder.getAdapterPosition(); //获取点击子项的位置
Fruit fruit=mFruitList.get(position); //根据位置获取指定的数据实体
Toast.makeText(v.getContext(),"触发的是子项中图片的点击事件:"+fruit.getName(),Toast.LENGTH_SHORT).show();
}
});
return viewHolder;
}
//onBindViewHolder:对RecyclerView的子项的数据进行赋值,当子项滚到屏幕都会被调用
//参数说明:
//ViewHolder viewHolder:缓存了子项中控件的静态内部类,这里是ImageView fruitImage; TextView fruitName;
//int i:i是滚入刚到屏幕内子项的编号
public void onBindViewHolder(ViewHolder viewHolder, int i) {
Fruit fruit=mFruitList.get(i); //获取实体类数组中数据
//将数据bind到子项中控件(子项控件已缓存到了ViewHolder了)
viewHolder.fruitName.setText(fruit.getName());
viewHolder.fruitImage.setImageResource(fruit.getImageId());
}
//作用:返回RecyclerView子项总数
@Override
public int getItemCount() {
return mFruitList.size();
}
}
注意:当子项注册了点击事件,而里面有些控件没有。点击这些没注册的控件,触发的是子项布局点击事件,因为会被最外层布局(注册了点击事件)捕获到的
所以这里我注册了子项点击事件,而TextView控件没有,就会触发子项的点击。