前言
ListView允许用户通过手指上下滑动的方式将屏幕外的数据滚动到屏幕内,同时屏幕上原有的数据则会滚动出屏幕。
比如:查看微信聊天记录、翻阅微博最新消息等等!
新建一个安卓项目,修改 activity_main.xml中的代码,如下所示:
先为 ListView 指定一个 id,然后将宽度和高度都设置为 match_parent,这样 ListView 也就占满了整个布局空间。
接下来修改 MainActivity 中的代码,如下所示:
public class MainActivity extends AppCompatActivity {
private String[] data = {"Apple","Banana","Orange","Watermelon",
"Pear","Grape","Pineapple","Strawberry","Cherry","Mango","Apple","Banana","Orange","Watermelon",
"Pear","Grape","Pineapple","Strawberry","Cherry","Mango"};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ArrayAdapter adapter = new ArrayAdapter(MainActivity.this,android.R.layout.simple_list_item_1,data);
ListView listView = findViewById(R.id.list_view);
listView.setAdapter(adapter);
}
}
既然 ListView 是用于展示大量数据的,那么我们就应该先将数据提供好。这里我们就简单使用了一个 data 数组来测试,里面包含了很多水果的名称。
不过,数组中的数据是无法直接传递给 ListView 的,我们还需要借助适配器来完成。Android 中提供了很多适配器的实现类,其中我认为最好用的就是 ArrayAdapter 。它可以通过泛型来指定要适配的数据类型,然后在构造函数中把要适配的数据传入。ArrayAdapter 有多个构造函数的重载,你应该根据实际情况选择最适合的一种。这里由于我们提供的数据都是字符串,因此将 ArrayAdapter 的泛型指定为 String ,然后在 ArrayAdapter 的构造函数中依次传入当前上下文、ListView 子项布局的 id ,以及要适配的数据。注意,我们使用了 android.R.layout.simple_list_item_1 作为 ListView 子项布局的 id ,这是一个 Android 内置的布局文件,里面只有一个 TextView ,可用于简单地显示一段文本。这样适配器对象就构建好了。
运行结果,如图所示:
只能显示一段文本的 ListView 实在是太单调了,我们现在就来对 ListView 的界面进行定制,让它可以显示更加丰富的内容。
首先定义一个实体类,作为 ListView 适配器的适配类型。新建类 Fruit ,代码如下所示:
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;
}
}
Fruit 类中只有两个字段,name 表示水果的名字,imageId 表示水果对应图片的资源 id 。
然后需要为 ListView 的子项指定一个我们自定义的布局,在 layout 目录下新建 fruit_item.xml ,代码如下所示:
在这个布局中,我们定义了一个 ImageView 用于显示水果的图片,又定义了一个 TextView 用于显示水果的名称,并让 TextView 在垂直方向上居中显示。
接下来需要创建一个自定义的适配器,这个适配器继承自 ArrayAdapter ,并将泛型指定为 Fruit 类。新建类 FruitAdapter ,代码如下所示:
(第一种方式)
public class FruitAdapter extends ArrayAdapter {
private int resourceId;
public FruitAdapter(@NonNull Context context, int textViewResourceId, @NonNull List objects) {
super(context, textViewResourceId, objects);
resourceId = textViewResourceId;
}
@NonNull
@Override
public View getView(int position, @Nullable View convertView, @NonNull ViewGroup parent) {
// 获取当前项的 Fruit 实例
Fruit fruit = getItem(position);
View view = LayoutInflater.from(getContext()).inflate(resourceId, parent, false);
ImageView fruitImage = (ImageView) view.findViewById(R.id.fruit_image);
TextView fruitName = (TextView) view.findViewById(R.id.fruit_name);
// 设置显示的图片
fruitImage.setImageResource(fruit.getImageId());
// 设置显示的文字
fruitName.setText(fruit.getName());
// 返回布局
return view;
}
}
FruitAdapter 重写了父类的一组构造函数,用于将上下文、ListView 子项布局的 id 和数据都传递进来。另外又重写了 getView() 方法,这个方法在每个子项被滚动到屏幕内的时候会被调用。在 getView() 方法中,首先通过 getItem() 方法得到当前项的 Fruit 实例,然后使用 LayoutInflater 来为这个子项加载我们传入的布局。
修改 MainActivity 中的代码,如下所示:
public class MainActivity extends AppCompatActivity {
private List fruitList = new ArrayList<>();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// 初始化水果数据
initFruits();
FruitAdapter adapter = new FruitAdapter(MainActivity.this, R.layout.fruit_item, fruitList);
ListView listView = (ListView) findViewById(R.id.list_view1);
listView.setAdapter(adapter);
}
private void initFruits() {
for (int i = 0; i < 2; i++) {
Fruit apple = new Fruit("Apple", R.mipmap.apple_pic);
fruitList.add(apple);
Fruit banana = new Fruit("Banana", R.mipmap.banana_pic);
fruitList.add(banana);
Fruit orange = new Fruit("Orange", R.mipmap.orange_pic);
fruitList.add(orange);
Fruit watermelon = new Fruit("Watermelon", R.mipmap.watermelon_pic);
fruitList.add(watermelon);
Fruit pear = new Fruit("Pear", R.mipmap.pear_pic);
fruitList.add(pear);
Fruit grape = new Fruit("Grape", R.mipmap.grape_pic);
fruitList.add(grape);
Fruit pineapple = new Fruit("Pineapple", R.mipmap.pineapple_pic);
fruitList.add(pineapple);
Fruit strawberry = new Fruit("Strawberry", R.mipmap.strawberry_pic);
fruitList.add(strawberry);
Fruit cherry = new Fruit("Cherry", R.mipmap.cherry_pic);
fruitList.add(cherry);
Fruit mango = new Fruit("Mango", R.mipmap.mango_pic);
fruitList.add(mango);
}
}
}
可以看到,这里添加了一个 initFruits() 方法,用于初始化所有的水果数据。在 Fruit 类的构造函数中将水果的名字和对应的图片 id 传入,然后把创建好的对象添加到水果列表中。另外我们使用了一个 for 循环将所有的水果数据添加了两遍,这是因为如果只添加一遍的话,数据量还不足以充满整个屏幕。接着在 onCreate() 方法中创建了 FruitAdapter 对象,并将 FruitAdapter 作为适配器传递给 ListView ,这样定制的 ListView 界面的任务就完成了。(我比较喜欢猫所以用猫的啦!)
其实只要修改 fruit_item.xml 中的内容,就可以定制出各种复杂的界面了。
上面的 FruitAdapter 类的写法性能运行效率很低,因为 FruitAdapter 的 getView() 方法中,每次都将布局重新加载一遍了,当 ListView 快速滚动的时候,这就会成为性能的瓶颈。
在 getView() 方法中有一个 convertView 参数,这个参数用于将之前加载好的布局进行缓存,以便之后可以进行重用。修改 FruitAdapter 类的代码,如下所示:
(第二种方式)
public class FruitAdapter extends ArrayAdapter {
private int resourceId;
public FruitAdapter(@NonNull Context context, int textViewResourceId, @NonNull List objects) {
super(context, textViewResourceId, objects);
resourceId = textViewResourceId;
}
@NonNull
@Override
public View getView(int position, @Nullable View convertView, @NonNull ViewGroup parent) {
// 获取当前项的 Fruit 实例
Fruit fruit = getItem(position);
View view;
if(convertView == null){
view = LayoutInflater.from(getContext()).inflate(resourceId, parent, false);
}else {
view = convertView;
}
ImageView fruitImage = (ImageView) view.findViewById(R.id.fruit_image);
TextView fruitName = (TextView) view.findViewById(R.id.fruit_name);
// 设置显示的图片
fruitImage.setImageResource(fruit.getImageId());
// 设置显示的文字
fruitName.setText(fruit.getName());
// 返回布局
return view;
}
}
可以看到,我们在 getView() 方法中进行了判断,如果 convertView 为 null,则使用 LayoutInflater 去加载布局,如果不为 null 则直接对 convertView 进行重用。这样就大大提高了 ListView 的运行效率。
虽然现在已经不会再重复去加载布局,但是每次在 getView() 方法中还是会调用 View 的 findViewById() 方法来获取一次控件的实例。
这时我们可以借助一个 VIewHoleder 来对这部分性能进行优化,修改 FruitAdapter 中的代码,如下所示:
(第三种方式)
public class FruitAdapter extends ArrayAdapter {
private int resourceId;
public FruitAdapter(@NonNull Context context, int textViewResourceId, @NonNull List objects) {
super(context, textViewResourceId, objects);
resourceId = textViewResourceId;
}
@NonNull
@Override
public View getView(int position, @Nullable View convertView, @NonNull ViewGroup parent) {
// 获取当前项的 Fruit 实例
Fruit fruit = getItem(position);
View view;
VIewHoleder viewHoleder;
if (convertView == null) {
// 动态加载布局
view = LayoutInflater.from(getContext()).inflate(resourceId, parent, false);
viewHoleder = new VIewHoleder();
viewHoleder.fruitImage = (ImageView) view.findViewById(R.id.fruit_image);
viewHoleder.fruitName = (TextView) view.findViewById(R.id.fruit_name);
// 将 VIewHoleder 存储在 View 中
view.setTag(viewHoleder);
} else {
view = convertView;
// 重新获取 VIewHoleder
viewHoleder = (VIewHoleder) view.getTag();
}
// 设置显示的图片
viewHoleder.fruitImage.setImageResource(fruit.getImageId());
// 设置显示的文字
viewHoleder.fruitName.setText(fruit.getName());
// 返回布局
return view;
}
class VIewHoleder{
ImageView fruitImage;
TextView fruitName;
}
}
新增加一个内部类 VIewHoleder,用于对控件的实例进行缓存。当 convertView 为 null 的时候,创建一个 VIewHoleder 对象,并将控件的实例都存放在 VIewHoleder 里,然后调用 View 的 setTag() 方法,将 VIewHoleder 对象存储在 View 中。当 convertView 不为 null 的时候,则调用 View 的 getTag() 方法,把 VIewHoleder 重新取出。这样所有控件的实例都缓存在了 VIewHoleder 里,就没有必要每次都通过 findViewById() 方法来获取控件实例了。
ViewHolder通常出现在适配器里,为的是listview滚动的时候快速设置值,而不必每次都重新创建很多对象,从而提升性能。
在android开发中Listview是一个很重要的组件,它以列表的形式根据数据的长自适应展示具体内容,用户可以自由的定义listview每一列的布局,但当listview有大量的数据需要加载的时候,会占据大量内存,影响性能,这时候就需要按需填充并重新使用view来减少对象的创建。
ListView加载数据都是在public View getView(int position, View convertView, ViewGroup parent) {}方法中进行的(要自定义listview都需要重写listadapter:如BaseAdapter,SimpleAdapter,CursorAdapter的等的getvView方法),优化listview的加载速度就要让convertView匹配列表类型,并最大程度上的重新使用convertView。
getview的加载方法一般有以下三种种方式:
最慢的加载方式是每一次都重新定义一个View载入布局,再加载数据**(上面的第一种方式)**
正确的加载方式是当convertView不为空的时候直接重新使用convertView从而减少了很多不必要的View的创建,然后加载数据**(上面的第二种方式)**
最快的方式是定义一个ViewHolder,将convetView的tag设置为ViewHolder,不为空时重新使用即可**(上面的第三种方式)**
三种方式加载效率对比如下图所示:
说明:上述三个例子代码摘自google 2010 I/O大会
当处理一些耗时的资源加载的时候需要做到以下几点,以使你的加载更快更平滑:
适配器在界面主线程中进行修改
可以在任何地方获取数据但应该在另外一个地方请求数据
在主界面的线程中提交适配器的变化并调用notifyDataSetChanged()方法
public class MainActivity extends AppCompatActivity {
private List fruitList = new ArrayList<>();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// 初始化水果数据
initFruits();
FruitAdapter adapter = new FruitAdapter(MainActivity.this, R.layout.fruit_item, fruitList);
ListView listView = (ListView) findViewById(R.id.list_view1);
listView.setAdapter(adapter);
listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView> parent, View view, int position, long id) {
Fruit fruit = fruitList.get(position);
Toast.makeText(MainActivity.this,fruit.getName(),Toast.LENGTH_SHORT).show();
}
});
}
private void initFruits() {
for (int i = 0; i < 2; i++) {
Fruit apple = new Fruit("Apple", R.mipmap.apple_pic);
fruitList.add(apple);
Fruit banana = new Fruit("Banana", R.mipmap.banana_pic);
fruitList.add(banana);
Fruit orange = new Fruit("Orange", R.mipmap.orange_pic);
fruitList.add(orange);
Fruit watermelon = new Fruit("Watermelon", R.mipmap.watermelon_pic);
fruitList.add(watermelon);
Fruit pear = new Fruit("Pear", R.mipmap.pear_pic);
fruitList.add(pear);
Fruit grape = new Fruit("Grape", R.mipmap.grape_pic);
fruitList.add(grape);
Fruit pineapple = new Fruit("Pineapple", R.mipmap.pineapple_pic);
fruitList.add(pineapple);
Fruit strawberry = new Fruit("Strawberry", R.mipmap.strawberry_pic);
fruitList.add(strawberry);
Fruit cherry = new Fruit("Cherry", R.mipmap.cherry_pic);
fruitList.add(cherry);
Fruit mango = new Fruit("Mango", R.mipmap.mango_pic);
fruitList.add(mango);
}
}
}
通过 position 参数判断出用户点击的是哪一个子项,然后获取到响应的水果,并通过 Toast 将水果的名字显示出来。
注:RecyclerView 也属于新增的控件,为了让 RecyclerView 在所有Android 版本上都能使用,因此需要在项目的 build.gradle 中添加相应的依赖库。
依赖库:implementation ‘androidx.recyclerview:recyclerview:1.0.0’
添加完之后记得要点击一下Sync Now 进行同步。
修改 activity_main.xml 中的代码:
如下所示:
我们使用 RecyclerView 实现与 ListView 相同的效果。因此把 图片、Fruit 类 和 fruit_item.xml 复制过来。
接下来为 RecyclerView 准备一个适配器,新建 FruitAdapter 类,让这个适配器继承自 RecyclerView.Adapter ,并将泛型指定为 FruitAdapter.ViewHolder。其中,ViewHolder 是在 FruitAdapter 中定义的一个内部类。代码如下所示:
public class FruitAdapter extends RecyclerView.Adapter {
private List mFruitList;
static class ViewHolder extends RecyclerView.ViewHolder {
ImageView fruitImage;
TextView fruitName;
public ViewHolder(@NonNull View view) {
super(view);
fruitImage = (ImageView) view.findViewById(R.id.fruit_image);
fruitName = (TextView) view.findViewById(R.id.fruit_name);
}
}
// 用于把数据源传进来
public FruitAdapter(List fruitList) {
mFruitList = fruitList;
}
@NonNull
@Override
// onCreateViewHolder()方法用于创建 ViewHolder 实例
public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.fruit_item, parent, false);
ViewHolder holder = new ViewHolder(view);
return holder;
}
@Override
// onBindViewHolder()方法用于对 RecyclerView 子项的数据进行赋值
public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
Fruit fruit = mFruitList.get(position);
holder.fruitImage.setImageResource(fruit.getImageId());
holder.fruitName.setText(fruit.getName());
}
// getItemCount() 方法用于告诉 RecyclerView 一共有多少子项
@Override
public int getItemCount() {
return mFruitList.size();
}
}
首先定义一个内部类 ViewHolder ,ViewHolder 要继承自 RecyclerView.ViewHolder 。然后 ViewHolder 的构造函数中要传入一个 View 参数,这个参数通常就是 RecyclerView 子项的最外层布局,通过 findViewById() 方法获取到布局中的 ImageView 和 TextView 的实例。
FruitAdapter 也有一个构造函数,这个方法用于把要展示的数据源传进来,并赋值给一个全局变量 mFruitList。
由于 FruitAdapter 是继承自 RecyclerView.Adapter 的,因此就必须重写 onCreateViewHolder()、onBindViewHolder()、getItemCount() 这 3 个方法。
onCreateViewHolder():用于创建 ViewHolder 实例
onBindViewHolder():用于对用于对 RecyclerView 子项的数据进行赋值,在每个子项被滚动到屏幕内的时候执行
getItemCount():用于告诉 RecyclerView 一共有多少子项
修改 MainActivity 中的代码,如下所示:
public class MainActivity extends AppCompatActivity {
private List 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);
// layoutManager 用于指定 RecyclerView 的布局方式
// LinearLayoutManager 为线性布局的意思
LinearLayoutManager layoutManager = new LinearLayoutManager(this);
recyclerView.setLayoutManager(layoutManager);
FruitAdapter adapter = new FruitAdapter(fruitList);
recyclerView.setAdapter(adapter);
}
private void initFruits(){
for (int i = 0; i < 2; i++) {
Fruit apple = new Fruit("Apple", R.mipmap.apple_pic);
fruitList.add(apple);
Fruit banana = new Fruit("Banana", R.mipmap.banana_pic);
fruitList.add(banana);
Fruit orange = new Fruit("Orange", R.mipmap.orange_pic);
fruitList.add(orange);
Fruit watermelon = new Fruit("Watermelon", R.mipmap.watermelon_pic);
fruitList.add(watermelon);
Fruit pear = new Fruit("Pear", R.mipmap.pear_pic);
fruitList.add(pear);
Fruit grape = new Fruit("Grape", R.mipmap.grape_pic);
fruitList.add(grape);
Fruit pineapple = new Fruit("Pineapple", R.mipmap.pineapple_pic);
fruitList.add(pineapple);
Fruit strawberry = new Fruit("Strawberry", R.mipmap.strawberry_pic);
fruitList.add(strawberry);
Fruit cherry = new Fruit("Cherry", R.mipmap.cherry_pic);
fruitList.add(cherry);
Fruit mango = new Fruit("Mango", R.mipmap.mango_pic);
fruitList.add(mango);
}
}
}
在 onCreate() 方法中先获取到 RecyclerView 的实例,然后创建一个 LinearLayoutManager 对象,并将它设置到 RecyclerView 当中。LayoutManager 用于指定 RecyclerView 的布局方式,这里使用 LinearLayoutManager 是线性布局的意思,可以实现与ListView 类似的效果。
前言
ListView 的扩展性并不好,它只能实现纵向滚动的效果,如果想进行横向滚动的话,ListView就做不到。而 RecyclerView 能够做到。
首先对 fruit_item.xml 布局进行修改,因为目前这个布局里面的元素是水平排列的,适用于纵向滚动的场景,而如果我们要实现横向滚动的话,应该把 fruit_item.xml 里面的元素改成垂直排列。代码如下所示:
修改 MainActivity 的代码,如下所示:
public class MainActivity extends AppCompatActivity {
private List 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);
// layoutManager 用于指定 RecyclerView 的布局方式
// LinearLayoutManager 为线性布局的意思
LinearLayoutManager layoutManager = new LinearLayoutManager(this);
// 调用 LinearLayoutManager 的 setOrientation() 方法来设置布局的排列方向
// 默认是纵向排列的,传入 LinearLayoutManager.HORIZONTAL 表示让布局横向排列
layoutManager.setOrientation(LinearLayoutManager.HORIZONTAL);
recyclerView.setLayoutManager(layoutManager);
FruitAdapter adapter = new FruitAdapter(fruitList);
recyclerView.setAdapter(adapter);
}
private void initFruits(){
for (int i = 0; i < 2; i++) {
Fruit apple = new Fruit("Apple", R.mipmap.apple_pic);
fruitList.add(apple);
Fruit banana = new Fruit("Banana", R.mipmap.banana_pic);
fruitList.add(banana);
Fruit orange = new Fruit("Orange", R.mipmap.orange_pic);
fruitList.add(orange);
Fruit watermelon = new Fruit("Watermelon", R.mipmap.watermelon_pic);
fruitList.add(watermelon);
Fruit pear = new Fruit("Pear", R.mipmap.pear_pic);
fruitList.add(pear);
Fruit grape = new Fruit("Grape", R.mipmap.grape_pic);
fruitList.add(grape);
Fruit pineapple = new Fruit("Pineapple", R.mipmap.pineapple_pic);
fruitList.add(pineapple);
Fruit strawberry = new Fruit("Strawberry", R.mipmap.strawberry_pic);
fruitList.add(strawberry);
Fruit cherry = new Fruit("Cherry", R.mipmap.cherry_pic);
fruitList.add(cherry);
Fruit mango = new Fruit("Mango", R.mipmap.mango_pic);
fruitList.add(mango);
}
}
}
MainActivity 中只加入了一行代码,调用 LinearLayoutManager 的 setOrientation() 方法来设置布局的排列方向,默认是纵向排列,我们传入 LinearLayoutManager.HORIZONTAL 表示让布局横向排列,这样 RecyclerView 就可以横向滚动了。
运行效果如图:
为什么 ListView 很难或者根本无法实现的效果在 RecyclerView 上这么轻松就实现了?
因为 RecyclerView 主要益于 它出色的设计,ListView 的布局排列是由自身去管理的,而 RecyclerView 则将这个工作交给了 LayoutManager,LayoutManager中制定了一套可扩展的布局排列接口,子类只要按照接口的规范来实现,就能定制出各种不同排列方式的布局。
除了 LinearLayoutManager 之外,RecyclerView 还给我们提供了 GridLayoutManager 和 StaggeredGridLayoutManager 两种内置的布局排列方式,GridLayoutManager 用于实现网格布局,StaggeredGridLayoutManager 用于实现瀑布流布局。
这里我们实现以下瀑布流布局效果:
修改 fruit_item.xml 中的代码,如下所示:
接着修改 MainActivity 中的代码,如下所示:
public class MainActivity extends AppCompatActivity {
private List 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);
// 第一个参数:3 代表会把布局分为 3 列
// 第二个参数:StaggeredGridLayoutManager.VERTICAL 表示会让布局纵向排列
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 < 2; i++) {
Fruit apple = new Fruit(getRandomLengthName("Apple"), R.mipmap.apple_pic);
fruitList.add(apple);
Fruit banana = new Fruit(getRandomLengthName("Banana"), R.mipmap.banana_pic);
fruitList.add(banana);
Fruit orange = new Fruit(getRandomLengthName("Orange"), R.mipmap.orange_pic);
fruitList.add(orange);
Fruit watermelon = new Fruit(getRandomLengthName("Watermelon"), R.mipmap.watermelon_pic);
fruitList.add(watermelon);
Fruit pear = new Fruit(getRandomLengthName("Pear"), R.mipmap.pear_pic);
fruitList.add(pear);
Fruit grape = new Fruit(getRandomLengthName("Grape"), R.mipmap.grape_pic);
fruitList.add(grape);
Fruit pineapple = new Fruit(getRandomLengthName("Pineapple"), R.mipmap.pineapple_pic);
fruitList.add(pineapple);
Fruit strawberry = new Fruit(getRandomLengthName("Strawberry"), R.mipmap.strawberry_pic);
fruitList.add(strawberry);
Fruit cherry = new Fruit(getRandomLengthName("Cherry"), R.mipmap.cherry_pic);
fruitList.add(cherry);
Fruit mango = new Fruit(getRandomLengthName("Mango"), R.mipmap.mango_pic);
fruitList.add(mango);
}
}
// 由于瀑布流布局需要各个子项的高度不一致才看得出明显的效果
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() 方法中,我们创建了一个 StaggeredGridLayoutManager 的实例。StaggeredGridLayoutManager 的构造函数接收两个参数,第一个参数用于指定布局的列数,传入 3 表示会把布局分为 3 列;第二个参数用于指定布局的排列方向,传入 StaggeredGridLayoutManager.VERTICAL 表示会让布局纵向排列,最后再把创建好的实例设置到 RecyclerView 中。
运行效果图:
修改 FruitAdapter 的代码,如下所示:
public class FruitAdapter extends RecyclerView.Adapter {
private List mFruitList;
static class ViewHolder extends RecyclerView.ViewHolder {
// 添加 fruitView 变量来保存子项最外层布局的实例
View fruitView;
ImageView fruitImage;
TextView fruitName;
public ViewHolder(@NonNull View view) {
super(view);
// fruitView 变量来保存子项最外层布局的实例
fruitView = view;
fruitImage = (ImageView) view.findViewById(R.id.fruit_image);
fruitName = (TextView) view.findViewById(R.id.fruit_name);
}
}
// 用于把数据源传进来
public FruitAdapter(List fruitList) {
mFruitList = fruitList;
}
@NonNull
@Override
// onCreateViewHolder()方法用于创建 ViewHolder 实例
public ViewHolder onCreateViewHolder(@NonNull 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();
}
});
// 注册点击事件(Image 的点击事件)
holder.fruitImage.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// 获取用户点击的 position
int position = holder.getAdapterPosition();
// 通过 position 拿到相应的 Fruit 实例
Fruit fruit = mFruitList.get(position);
Toast.makeText(v.getContext(),"you clicked image"+fruit.getName(),Toast.LENGTH_SHORT).show();
}
});
return holder;
}
@Override
// onBindViewHolder()方法用于对 RecyclerView 子项的数据进行赋值
public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
Fruit fruit = mFruitList.get(position);
holder.fruitImage.setImageResource(fruit.getImageId());
holder.fruitName.setText(fruit.getName());
}
// getItemCount() 方法用于告诉 RecyclerView 一共有多少子项
@Override
public int getItemCount() {
return mFruitList.size();
}
}
运行效果图:
摘抄自:第一行代码 Android
与摘抄自:https://blog.csdn.net/jacman/article/details/7087995