概要:RecyclerView是一个强大的滚动控件,可以看成是增强版的ListView。比如ListView只能实现数据的纵向滚动,而RecyclerView可以实现横向滚动。
一、基本用法:
首先在app/build.gradle下添加与appcompat相对应的依赖包
如图,版本号需一致。
然后,在布局中加入RecyclerView控件,由于RecyclerView没有内置在系统SDK,故需要写出完整包路径
<?xml version="1.0" encoding="utf-8"?> <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="com.zjc.recyclerviewtest.MainActivity"> <android.support.v7.widget.RecyclerView android:id="@+id/recycler_view" android:layout_width="match_parent" android:layout_height="match_parent"/> </LinearLayout>
参照ListView的滚动,需要实体类及为其子项自定义的布局文件
package com.zjc.recyclerviewtest; /** * Created by ZJC on 2018-03-31. */ 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; } }
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" 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_marginLeft="10dp"/> </LinearLayout>
接下来是最重要的,新建适配器继承于RecyclerView.Adapter,指定泛型为类名.ViewHolder,其中,ViewHolder是在这个适配器中自定义的内部类。撰写类及ViewHolder的的构造函数并重写其所有方法。
package com.zjc.recyclerviewtest; import android.support.v7.widget.RecyclerView; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.ImageView; import android.widget.TextView; import java.util.List; /** * Created by ZJC on 2018-03-31. */ public class FruitAdapter extends RecyclerView.Adapter<FruitAdapter.ViewHolder> { private List<Fruit> mFruitList; //创建内部类ViewHolder static class ViewHolder extends RecyclerView.ViewHolder{ ImageView fruitIamge; TextView fruitName; public ViewHolder(View view){ super(view); fruitIamge = view.findViewById(R.id.fruit_image); fruitName = view.findViewById(R.id.fruit_name); } } //构造函数,用于将要展示的数据源传进来,并赋值给全局变量mFruitList public FruitAdapter(List<Fruit> fruitList) { mFruitList = fruitList; } /** * 加载fruit_item布局并将其传入构造函数,返回ViewHolder实例 */ @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); return holder; } /** * 对Recycler子项数据赋值,在每个子项滚动到屏幕内时执行 */ @Override public void onBindViewHolder(ViewHolder holder, int position) { Fruit fruit = mFruitList.get(position); holder.fruitIamge.setImageResource(fruit.getImageId()); holder.fruitName.setText(fruit.getName()); } /** * 告诉RecyclerView一共有多少子项,返回数据源长度 */ @Override public int getItemCount() { return mFruitList.size(); } }
内部类ViewHolder的构造函数中传入view参数——RecyclerView子项XX_item的最外层布局,通过findViewById()获取实例并储存在ViewHolder中。
适配器的构造函数目的是传入我们需要展示的数据源并赋值给全局变量方便调用。
由于继承了RecyclerView.Adapter,那下面的三个方法就是重写其所有方法。
运行程序,得到与ListView相同的纵向滚动效果:
二、横向滚动和瀑布流布局
1.横向布局
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="100dp" android:layout_height="wrap_content" android:orientation="vertical"> <ImageView android:id="@+id/fruit_image" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center_horizontal"/> <TextView android:id="@+id/fruit_name" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginLeft="10dp" android:layout_marginTop="10dp" android:layout_gravity="center_horizontal"/> </LinearLayout>
LinearLayout设置为垂直排列,否则就是下面这种情况
限定宽度是因为每个item文字不一致,若为wrap_content就会有长有短;两个view都设置为布局水平居中,marginTop是使文字与图片保持一致。
最后实现美观的横向滚动
2.瀑布流
布局文件和实体类不变,在原有Adapter的基础上进行修改:
package com.zjc.recyclerviewtest; import android.support.v7.widget.RecyclerView; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.ImageView; import android.widget.TextView; import android.widget.Toast; import java.util.List; /** * Created by ZJC on 2018-03-31. */ public class FruitAdapter extends RecyclerView.Adapter<FruitAdapter.ViewHolder> { private List<Fruit> mFruitList; //创建内部类ViewHolder static class ViewHolder extends RecyclerView.ViewHolder{ View fruitView; //保存最外层布局的实例 ImageView fruitIamge; TextView fruitName; public ViewHolder(View view){ super(view); fruitView = view; fruitIamge = view.findViewById(R.id.fruit_image); fruitName = view.findViewById(R.id.fruit_name); } } //构造函数,用于将要展示的数据源传进来,并赋值给全局变量mFruitList public FruitAdapter(List<Fruit> fruitList) { mFruitList = fruitList; } /** * 加载fruit_item布局并将其传入构造函数,返回ViewHolder实例 * 注册最外层布局view和imageView的点击事件,点击空白区和imageView显示 */ @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 clicked view "+fruit.getName(),Toast.LENGTH_SHORT).show(); } }); holder.fruitIamge.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { int position = holder.getAdapterPosition(); Fruit fruit = mFruitList.get(position); Toast.makeText(v.getContext(),"you clicked image "+fruit.getName(),Toast.LENGTH_SHORT).show(); } }); return holder; } /** * 对Recycler子项数据赋值,在每个子项滚动到屏幕内时执行 */ @Override public void onBindViewHolder(ViewHolder holder, int position) { Fruit fruit = mFruitList.get(position); holder.fruitIamge.setImageResource(fruit.getImageId()); holder.fruitName.setText(fruit.getName()); } /** * 告诉RecyclerView一共有多少子项,返回数据源长度 */ @Override public int getItemCount() { return mFruitList.size(); } }
可以看出,我们添加了一个用于保存最外层布局的实例,并将view赋值给它,在onCreateViewHolder()中注册了view和imageview的点击事件,由于未注册name,所以实际效果为点击图片产生image点击效果,name会被最外层布局捕捉到,故产生view点击效果。
再看MainActivity中做出的改变:
package com.zjc.recyclerviewtest; import android.support.v7.app.AppCompatActivity; import android.os.Bundle; import android.support.v7.widget.LinearLayoutManager; import android.support.v7.widget.RecyclerView; import android.support.v7.widget.StaggeredGridLayoutManager; import java.util.ArrayList; import java.util.List; import java.util.Random; 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 = findViewById(R.id.recycler_view); //定义瀑布流管理器,第一个参数是列数,第二个是方向。 StaggeredGridLayoutManager layoutManager = new StaggeredGridLayoutManager(3, StaggeredGridLayoutManager.VERTICAL); recyclerView.setLayoutManager(layoutManager); FruitAdapter adapter = new FruitAdapter(fruitList); recyclerView.setAdapter(adapter); } private void initFruits() { for (int i = 0; i < 5; i++){ Fruit apple = new Fruit(("apple"),R.drawable.apple); fruitList.add(apple); Fruit banana = new Fruit(getRandomLengthName("banana"),R.drawable.banana); fruitList.add(banana); Fruit grape = new Fruit(getRandomLengthName("grape"),R.drawable.grape); fruitList.add(grape); Fruit orange = new Fruit(getRandomLengthName("orange"),R.drawable.orange); fruitList.add(orange); Fruit strawberry = new Fruit(getRandomLengthName("strawberry"),R.drawable.strawberry); fruitList.add(strawberry); } } 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(); } }
初始化数据,在初始化过程中通过getRandomLengthName()随机产生名字达到满页效果(apple处做了对比处理);定义并设置了瀑布流管理器StaggeredGridLayoutManager及自定义适配器adpter。
运行程序,产生一个简单的瀑布流视图。