说说 Android UI 的滚动控件(RecyclerView)

ListView 由于其强大的功能,在过去的 Andorid 开发中使用非常广泛。不过 ListView 需要优化来提升运行效率,就像我们之前所优化的那样,否则性能将很差。还有就是只能够纵向滚动,如果要想实现横向移动,用 ListView 是做不到的。

RecyclerView 可以说是一个增强版的 ListView 。它不仅实现了和 ListView 同样的效果,而且还优化了 ListView 存在的各种不足。 RecyclerView 现在可是官方推荐使用的滚动控件哦O(∩_∩)O~

1 基本用法

RecyclerView 也是新增的控件,所以必须先在项目的 build.gradle 中添加相应的依赖库才能使用:

 compile 'com.android.support:recyclerview-v7:24.2.1'

注意不要拼错哦O(∩_∩)O~

添加后,记得点击 Sync Now 链接哦。

接着,修改布局文件:

<?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="match_parent">

    <android.support.v7.widget.RecyclerView
        android:id="@+id/recycler_view"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />
</LinearLayout>

这里把宽度和高度都定义为 match_parent,这样 RecyclerView 就能占满整个屏幕。因为 RecyclerView 也不是系统内置的 SDK,所以这里引用的是完整的包路径。

然后为 RecyclerView 创建一个适配器(继承自 RecyclerView.Adapter<CatAdapter.ViewHolder>):

public class CatAdapter extends RecyclerView.Adapter<CatAdapter.ViewHolder> {

    private List<Cat> cats;

    static class ViewHolder extends RecyclerView.ViewHolder {
        ImageView image;
        TextView name;

        public ViewHolder(View view) {
            super(view);
            image = (ImageView) view.findViewById(R.id.image);
            name = (TextView) view.findViewById(R.id.name);
        }
    }

    public CatAdapter(List<Cat> cats) {
        this.cats = cats;
    }


    @Override
    public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.cat_item, parent, false);
        return new ViewHolder(view);
    }

    @Override
    public void onBindViewHolder(ViewHolder holder, int position) {
        Cat cat = cats.get(position);
        holder.image.setImageResource(cat.getImageId());
        holder.name.setText(cat.getName());
    }

    @Override
    public int getItemCount() {
        return cats.size();
    }
}

在代码中,我们先定义了一个内部类 ViewHolder,它继承自 RecyclerView.ViewHolder。然后在 ViewHolder 的构造函数中传入一个 View 参数,它是 RecyclerView 子项的最外层布局,所以我们可以通过它来取得布局中的 ImageView 和 TextView 的实例。

CatAdapter 的构造函数用于把要展示的数据源传递进来,并赋值给一个类变量 cats。

因为 CatAdapter 继承自 RecyclerView.Adapter,所以必须重写以下三个方法:

  • onCreateViewHolder - 创建 ViewHolder 实例,我们把 cat_item 的布局加载进来,创建了一个 ViewHolder 实例。
  • onBindViewHolder - 对 RecyclerView 的子项数据进行赋值,这个方法会在每个子项被滚动到屏幕内时进行。
  • getItemCount - 返回 RecyclerView 的子项总数。

最后,我们在活动类中使用 RecyclerView :

public class MainActivity extends AppCompatActivity {

    private List<Cat> cats = new ArrayList<>();

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        init();

        RecyclerView recyclerView=(RecyclerView)findViewById(R.id.recycler_view);
        LinearLayoutManager layoutManager=new LinearLayoutManager(this);
        recyclerView.setLayoutManager(layoutManager);
        CatAdapter adapter=new CatAdapter(cats);
        recyclerView.setAdapter(adapter);
    }

    /**
     * 初始化数据
     */
    private void init() {
        cats.add(new Cat("暹罗猫", R.drawable.cat1));
        cats.add(new Cat("布偶猫", R.drawable.cat2));
        cats.add(new Cat("苏格兰折耳猫", R.drawable.cat3));
        cats.add(new Cat("英国短毛猫", R.drawable.cat4));
        cats.add(new Cat("波斯猫", R.drawable.cat5));
        cats.add(new Cat("俄罗斯蓝猫", R.drawable.cat6));
        cats.add(new Cat("美国短毛猫", R.drawable.cat7));
        cats.add(new Cat("异国短毛猫", R.drawable.cat8));
        cats.add(new Cat("挪威森林猫", R.drawable.cat9));
        cats.add(new Cat("孟买猫", R.drawable.cat10));
        cats.add(new Cat("缅因猫", R.drawable.cat11));
        cats.add(new Cat("埃及猫", R.drawable.cat12));
    }

}

这里创建了 LinearLayoutManager 的线性布局对象,传递给了 recyclerView.setLayoutManager() 方法。

recyclerView 示例

我们使用了 recyclerView 创建出了 ListView 的效果,而且代码逻辑更清晰咯。

2 横向滚动

现在让我们把这些猫变为 “横向滚动” 吧。

把 cat_item 中的元素变为垂直排列:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="110dp"
    android:layout_height="wrap_content"
    android:orientation="vertical"
    >

    <ImageView
        android:id="@+id/image"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center_horizontal"
        />

    <TextView
        android:id="@+id/name"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center_horizontal"
        android:layout_marginTop="10dp" />

</LinearLayout>

这里还把 LinearLayout 的宽度设为 110dp,即固定的值。因为每种猫的文字长度不同,如果用 wrap_content 的话,可能造成子项的长短不一致;而如果用 match_parent 的话,又会导致一个子项就占满了整个屏幕。

我们把 ImageView 与 TextView 都设置为水平居中,而且用 android:layout_marginTop,让文字与图片保持了一段距离,这样更美观。

接下来,修改活动类的代码:


@Override
protected void onCreate(Bundle savedInstanceState) {
    ...
    layoutManager.setOrientation(LinearLayoutManager.HORIZONTAL);
    ...
}

把 LinearLayoutManager 的布局改为横向排列(默认是纵向排列)。

横向排列

我们可以在水平方向上滑动来查看屏幕外的 “猫” 啦。

ListView 的布局排列是由自身来管理的,所以存在一定的局限性;而 RecyclerView 把布局的工作交给了 LayoutManager,LayoutManager 制定了一系列可扩展的布局排列接口,所以我们只要按照接口的规范来实现,就能够定制出各种不同排列方式的布局啦O(∩_∩)O~

3 瀑布流布局

让我们使用 StaggeredGridLayoutManager 来实现酷炫的瀑布流布局吧O(∩_∩)O~

首先修改 cat_item 布局文件:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:orientation="vertical"
    android:layout_margin="5dp"
    >

    <ImageView
        android:id="@+id/image"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center_horizontal"
        />

    <TextView
        android:id="@+id/name"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="left"
        android:layout_marginTop="10dp" />

</LinearLayout>

这里把 LinearLayout 的宽度改为 wrap_content,这样宽度会根据实际的布局列数自动适配。 还使用 layout_margin 让子项之间留出一定的间距。最后将 TextView 改为居左对齐,因为下面的说明文件内容可能会很长哟O(∩_∩)O~

修改活动类的代码:

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    init();

    RecyclerView recyclerView = (RecyclerView) findViewById(R.id.recycler_view);

    StaggeredGridLayoutManager layoutManager=new StaggeredGridLayoutManager(3,StaggeredGridLayoutManager.VERTICAL);

    recyclerView.setLayoutManager(layoutManager);
    CatAdapter adapter = new CatAdapter(cats);
    recyclerView.setAdapter(adapter);
}

在此,我们创建了 StaggeredGridLayoutManager 的实例,它的构造函数接受两个参数,第一个参数用于指定布局的列数,第二个参数用于指定布局的排列方向。

瀑布流示例

4 点击事件

RecyclerView 没有像 ListView 一样的 setOnItemClickListener() 事件,所以需要我们自己给子项具体的 View 注册点击事件。

ListView 的 setOnItemClickListener() 注册的是子项的点击事件,但如果想要注册点击的是子项里具体的某个按钮时,使用 ListView 实现起来就比较麻烦。所以 RecyclerView 直接摈弃了子项点击事件的监听器,把所有的点击事件都交给具体的 View 去注册实现咯O(∩_∩)O

修改适配器:

static class ViewHolder extends RecyclerView.ViewHolder {
    View catView;
    ImageView image;
    TextView name;

    public ViewHolder(View view) {
        super(view);
        catView = view;
        image = (ImageView) view.findViewById(R.id.image);
        name = (TextView) view.findViewById(R.id.name);
        Log.d(TAG, "ViewHolder: image:" + image);
        Log.d(TAG, "ViewHolder: name:" + name);
    }
}

public CatAdapter(List<Cat> cats) {
    this.cats = cats;
}


@Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
    View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.cat_item, parent, false);

    final ViewHolder holder = new ViewHolder(view);
    holder.catView.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            int position = holder.getAdapterPosition();
            Cat cat = cats.get(position);
            Toast.makeText(v.getContext(), "你点击了 View " + cat.getName(), Toast.LENGTH_SHORT).show();
        }
    });

    holder.image.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            int position = holder.getAdapterPosition();
            Log.d(TAG, "onClick: position:" + position);
            Cat cat = cats.get(position);
            Toast.makeText(v.getContext(), "你点击了图片 " + cat.getName(), Toast.LENGTH_SHORT).show();
        }
    });
    return holder;
}

我们为最外层的布局与 ImageView 都注册了点击事件,这就是 RecyclerView 的灵活之处。

触发 RecyclerView 点击事件

如果点击了图片下方的文字,会触发 ImageView 的点击事件,因为事件会向外传播哦O(∩_∩)O~

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 199,064评论 5 466
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 83,606评论 2 376
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 146,011评论 0 328
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,550评论 1 269
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,465评论 5 359
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 47,919评论 1 275
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,428评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,075评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,208评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,185评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,191评论 1 328
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,914评论 3 316
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,482评论 3 302
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,585评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,825评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,194评论 2 344
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,703评论 2 339

推荐阅读更多精彩内容