按照书上的说法就是RecycleView更强大!而且目前来看ListView基本上已经淘汰了,所以让我们来看看RecycleView吧。
导包
因为版本更新,书上的已经过时了,所以我百度了一下,发现了这个两个版本。
v7:implementation 'com.android.support:recyclerview-v7:28.0.0'
x:implementation 'androidx.recyclerview:recyclerview:1.1.0'
参考文章:Android Studio新版本导入Recyclerview库的依赖
总是听到有人说AndroidX,到底什么是AndroidX?
简单来说就是Androidx比较新,v7比较老,同时两个包不能同时使用,会在某些地方出现冲突。一般现在都用x了
案例
第一步:修改activity_ main.xml
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/list_view"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
其实就是把之前的ListView改成RecyclerView,但是RecyclerView需要全类名
第二步:给RecyclerView新建一个适配器
先把ListView项目的Fruit类和fruit_item布局复制过来,可以去上一个章节寻找相关资料。
package com.firstcode.customuicontrols;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;
import java.util.List;
import androidx.recyclerview.widget.RecyclerView;
public class FruitRecyclerViewAdapter extends RecyclerView.Adapter<FruitRecyclerViewAdapter.ViewHolder> {
private List<Fruit> mFruitList;
static class ViewHolder extends RecyclerView.ViewHolder {
ImageView fruitImage;
TextView fruitName;
public ViewHolder(View view) {
super(view);
fruitImage = (ImageView) view.findViewById(R.id.image_view);
fruitName = (TextView) view.findViewById(R.id.text_view);
}
}
public FruitRecyclerViewAdapter(List<Fruit> fruitList) {
mFruitList = fruitList;
}
@Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.layout_fruit, parent, false);
ViewHolder holder = new ViewHolder(view);
return holder;
}
@Override
public void onBindViewHolder( ViewHolder holder, int position) {
Fruit fruit = mFruitList.get(position);
holder.fruitImage.setImageResource(fruit.getImageId());
holder.fruitName.setText(fruit.getName());
}
@Override
public int getItemCount() {
return mFruitList.size();
}
}
因为避免和上一个案例重复,我自己修改了类名,其他的都是一样的。
这里说一下笔者的理解,首先就是使用了一个内部类,实现了RecyclerView每个子项的初始化。这个用法理解起来还是比较复杂的,笔者个人的理解就是内部类的形式实例化一个View,然后可以对其进行很多的操作。
第二部分就是整个适配器的构造函数,可以看出RecyclerView的适配器只需要传入一个数组即可。因为它把子项控件的初始化和布局的加载通过其他函数内置了,就是内部类和onCreateViewHolder。
onCreateViewHolder就是初始化,加载布局的。同样是使用LayoutInflater方法,但是我发现只要在Activity中,环境变量才能使用Activity或者this,而在其他的类中大多数使用View的方法getContext。这也间接的说明View和Activity就是环境。
onBindViewHolder通过书上的介绍就是在每个子项展示的时候调用,就是很简单的通过位置设置值。
最后一个函数就是返回数组长度。
但是笔者这里发现inflate的参数和之前自定义控件时候使用的不同。搜索了一下,发现其有四个构造函数。
这里有个新的概念就是ViewGroup,其实简单的来说就是像LinearLayout 之类的布局就是ViewGroup,而那些控件像Button就是一种View,View是ViewGroup的父类。到这里我就明白inflate的含义了。
先说为什么之前使用的inflate是两个参数的,它使用的是图中第一个构造函数,int类型的是布局id,而ViewGroup就是父布局,当时传入的是当前的Activity,其实Activity也是一个View。
而Adapter为啥要传入三个参数,因为Adapter是为list服务的,list自己有一个总体的布局,而他的子项也是一个布局,需要加载其中。所以使用图中第三个构造函数,最后的布尔型参数取消新建布局,把子布局放到list的子项里面。
参考资料
基础篇——View和ViewGroup的区别
Android LayoutInflate深度解析
Android View原理浅析——View的工作原理
Android View详解
第三步:修改MainActivity
RecyclerView recyclerView = (RecyclerView) findViewById(R.id.list_view);
LinearLayoutManager layoutManager = new LinearLayoutManager(this);
recyclerView.setLayoutManager(layoutManager);
FruitRecyclerViewAdapter adapter = new FruitRecyclerViewAdapter(fruitList);
recyclerView.setAdapter(adapter);
横向滑动
第一步 修改布局
其实就是把原来的横向布局改为竖向,文字和图片调整大小
<?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/image_view"
android:layout_width="100dp"
android:layout_height="100dp"
android:layout_gravity="center_horizontal"/>
<TextView
android:id="@+id/text_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_vertical"
android:layout_gravity="center_horizontal"
android:layout_marginTop="10dp"/>
</LinearLayout>
第二步:加载布局
只有一个操作,在之前加载布局前设置横向设置
layoutManager.setOrientation(LinearLayoutManager.HORIZONTAL);
瀑布布局
第一步还是修改布局
<?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"
android:layout_margin="5dp"
android:orientation="vertical">
<ImageView
android:id="@+id/image_view"
android:layout_width="100dp"
android:layout_height="100dp"
android:layout_gravity="center_horizontal" />
<TextView
android:id="@+id/text_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="left"
android:layout_marginTop="10dp" />
</LinearLayout>
第二步是也是一样的加载布局,同样的只需要一行代码就行了。
StaggeredGridLayoutManager staggeredGridLayoutManager = new StaggeredGridLayoutManager(3,StaggeredGridLayoutManager.VERTICAL);
recyclerView.setLayoutManager(staggeredGridLayoutManager);
网格布局
到这里我已经感受到了RecycleView的强大之处,在于可以在List之上添加布局,虽然有些地方还是不明白。书中并没有网格布局的代码,也是留了个作业,所以我们就在这做一下,练练手。
在网上查阅了资料发现GridLayoutManager有两个参数,第一个就是上下文,第二个就是列数。
所以修改代码如下
GridLayoutManager gridLayoutManager = new GridLayoutManager(this, 2);
recyclerView.setLayoutManager(gridLayoutManager);
点击事件
第一步:修改适配器代码
具体代码修改不多,和之前的按钮的点击事件相似。大家看看书本上的讲解应该就能明白。
规范
- 在实际开发中一般不使用构造函数来赋值,会写一个setData()方法来传入数据。这样可以保证实例化和赋值分开,减少错误。
- 点击事情写在ViewHolder里面,在onBindView方法中调用,这样结构更加清晰。