RecyclerView控件实现横向滚动和瀑布流布局,以及RecyclerView的点击监听(项目已上传GitHub)

先附上我的GitHub项目地址:

https://github.com/Skymqq/RecyclerViewTest.git

 

我们已经知道,ListView的拓展性不好,它只能实现纵向的滚动的效果,如果想进行横向滚动的效果,ListView就做不到了,毫无疑问,RecyclerView是可以做到的。

fruit_item.xml列表项布局文件代码:

<?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/iv_name"
        android:layout_width="40dp"
        android:layout_height="40dp"
        android:layout_gravity="center_horizontal" />

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

</LinearLayout>

可以看到,我们将LinearLayout改成垂直方向排列,并且把宽度设置为100dp。这里将宽度指定为固定值是因为每种水果的文字长度不一致,如果用wrap_content,RecyclerView的子项就会有长有短,非常不美观;而如果使用match_parent,就会导致宽度过长,一个子项占满了整个屏幕。

然后将ImageView和TextView都设置成了在布局中水平居中,并且使用layout_marginTop属性让文字和图片之间保持一些距离。

MainActivity.java代码:

package com.example.administrator.activitydemo;

import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;

import java.util.ArrayList;
import java.util.List;

public class MainActivity extends AppCompatActivity {
    private RecyclerView recyclerView;
    private String[] data = {"Apple", "Banana", "Orange", "Watermelon",
            "Pear", "Grape", "Pineapple", "Strawberry", "Cherry", "Mango",
            "Apple", "Banana", "Orange", "Watermelon",
            "Pear", "Grape", "Pineapple", "Strawberry", "Cherry", "Mango"
    };
    private int[] res = {R.drawable.apple, R.drawable.banana, R.drawable.orange, R.drawable.watermelon,
            R.drawable.pear, R.drawable.grape, R.drawable.pineapple, R.drawable.strawberry, R.drawable.cherry, R.drawable.mango,
            R.drawable.apple, R.drawable.banana, R.drawable.orange, R.drawable.watermelon,
            R.drawable.pear, R.drawable.grape, R.drawable.pineapple, R.drawable.strawberry, R.drawable.cherry, R.drawable.mango};
    private List<Fruit> fruitList;


    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        initView();//初始化UI控件
        initData();//初始化数据
    }


    private void initView() {
        recyclerView = (RecyclerView) findViewById(R.id.recyclerView);
    }

    private void initData() {
        initFruits();//初始化水果数据
        LinearLayoutManager layoutManager = new LinearLayoutManager(MainActivity.this);//实例化布局管理器
        layoutManager.setOrientation(LinearLayoutManager.HORIZONTAL);//为布局管理器设定线性方向
        recyclerView.setLayoutManager(layoutManager);//为RecyclerView控件设置布局管理器
        FruitAdapter adapter = new FruitAdapter(fruitList);//实例化适配器
        recyclerView.setAdapter(adapter);//为RecyclerView控件设置适配器
    }

    private void initFruits() {
        fruitList = new ArrayList<>();
        for (int i = 0; i < data.length; i++) {
            Fruit fruit = new Fruit(data[i], res[i]);
            fruitList.add(fruit);
        }
    }

}


效果图:

MainActivity.java中加入了一行代码,调用LinearLayoutManager的setOrientation()方法来设置布局的排列方向,默认是纵向排列的,我们传入的LinearLayoutManager.HORIZONTAL表示让布局横行排列,这样RecyclerView就可以横向滚动了。

为什么ListView很难或者根本无法实现的效果在RecyclerView上这么轻松就能实现了呢?

这主要得益于RecyclerView出色的设计。ListView的布局排列是由自身去管理的,而RecyclerView则将这个工作交给了LayoutManager,LayoutManager中制定了一套可拓展的布局排列接口,子类只要按照接口的规范来实现,就能定制出各种不同排列方式的布局了。

除了LinearLayoutManager之外,RecyclerView还给我们提供了GridLayoutManager和StaggeredGridLayoutManager这两种内置的布局排列方式。GridLayoutManager可以用于实现网络布局,StaggeredGridLayoutManager可以用于实现瀑布流布局。这里我们来实现一下效果更加酷炫的瀑布流布局。

fruit_item.xml代码:

<?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/iv_name"
        android:layout_width="40dp"
        android:layout_height="40dp"
        android:layout_gravity="center_horizontal" />

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

</LinearLayout>

这里做了几处小的调整,首先将LinearLayout的宽度由100dp改成了match_parent,因为瀑布流布局的宽度应该是根据布局的列数来自动适配的,而不是一个固定值。另外我们使用了layout_margin属性来让子项之间互留一点间距,这样就不至于所有子项都紧贴在一起。还有就是将TextView的对其属性改成了居左对齐,因为待会我们会将文字的长度变长,如果还是居中就会很怪。

MainActivity.java代码:

package com.example.administrator.activitydemo;

import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v7.app.AppCompatActivity;
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 RecyclerView recyclerView;
    private String[] data = {"Apple", "Banana", "Orange", "Watermelon",
            "Pear", "Grape", "Pineapple", "Strawberry", "Cherry", "Mango",
            "Apple", "Banana", "Orange", "Watermelon",
            "Pear", "Grape", "Pineapple", "Strawberry", "Cherry", "Mango"
    };
    private int[] res = {R.drawable.apple, R.drawable.banana, R.drawable.orange, R.drawable.watermelon,
            R.drawable.pear, R.drawable.grape, R.drawable.pineapple, R.drawable.strawberry, R.drawable.cherry, R.drawable.mango,
            R.drawable.apple, R.drawable.banana, R.drawable.orange, R.drawable.watermelon,
            R.drawable.pear, R.drawable.grape, R.drawable.pineapple, R.drawable.strawberry, R.drawable.cherry, R.drawable.mango};
    private List<Fruit> fruitList;


    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        initView();//初始化UI控件
        initData();//初始化数据
    }


    private void initView() {
        recyclerView = (RecyclerView) findViewById(R.id.recyclerView);
    }

    private void initData() {
        initFruits();//初始化水果数据
        StaggeredGridLayoutManager layoutManager = new StaggeredGridLayoutManager(3, StaggeredGridLayoutManager.VERTICAL);//3列,垂直
        recyclerView.setLayoutManager(layoutManager);//为RecyclerView控件设置布局管理器
        FruitAdapter adapter = new FruitAdapter(fruitList);//实例化适配器
        recyclerView.setAdapter(adapter);//为RecyclerView控件设置适配器
    }

    private void initFruits() {
        fruitList = new ArrayList<>();
        for (int i = 0; i < data.length; i++) {
            Fruit fruit = new Fruit(getRandomLengthName(data[i]), res[i]);
            fruitList.add(fruit);
        }
    }

    private String getRandomLengthName(String name) {
        Random random = new Random();
        int length = random.nextInt(20) + 1;//取0~20之间的一个随机数
        StringBuilder builder = new StringBuilder();
        for (int i = 0; i < length; i++) {
            builder.append(name);//字符串拼接
        }
        return builder.toString();
    }

}


效果图:

这里的水果的名字长短都是随机生成的,所以当我们再次运行一次程序,效果图又会不一样的。

 

RecyclerView的点击事件:

和ListView一样,RecyclerView也必须要能响应点击事件才可以,不然的话就没什么实际用途了。不过不同于ListView的是,RecyclerView并没有提供类似于setOnClickListener()这样的注册监听方法,而是需要我们自己给子项具体的View去注册点击事件,相比于ListView来说,实现起来要复杂一些。

那么你可能就有疑问了,为什么RecyclerView在各个方面的设计都要优于ListView,偏偏在点击事件上却没有处理得非常好呢?其实不是这样的,ListView在点击事件上的处理并不人性化,setOnClickListener()方法注册的是子项的点击事件,但如果我想点击的是子项里具体的某一个按钮呢?虽然ListView也是能够做到的,但是实现起来就比较麻烦了。为此,RecyclerView干脆直接摒弃了子项点击事件的监听器,所有的点击事件都由具体的View去注册,就再没有这个困扰了。

下面我们来具体学习一下如何在RecyclerView中注册点击事件,修改FruitAdapter中的代码,如下所示:

package com.example.administrator.activitydemo;

import android.content.Context;
import android.support.annotation.NonNull;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.ImageView;
import android.widget.TextView;
import android.widget.Toast;

import java.util.List;

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

    private List<Fruit> fruitList;

    public FruitAdapter(List<Fruit> fruitList) {
        this.fruitList = fruitList;
    }

    @NonNull
    @Override
    public ViewHolder onCreateViewHolder(@NonNull ViewGroup viewGroup, int i) {
        View view = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.fruit_item, viewGroup, false);
        final ViewHolder viewHolder = new ViewHolder(view);
        viewHolder.iv_name.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                int position = viewHolder.getAdapterPosition();
                Fruit fruit = fruitList.get(position);
                Toast.makeText(v.getContext(), "you clicked ImageView: " + fruit.getName(), Toast.LENGTH_SHORT).show();
            }
        });
        viewHolder.tv_name.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                int position = viewHolder.getAdapterPosition();
                Fruit fruit = fruitList.get(position);
                Toast.makeText(v.getContext(), "you clicked TextView: " + fruit.getName(), Toast.LENGTH_SHORT).show();
            }
        });

        return viewHolder;
    }

    @Override
    public void onBindViewHolder(@NonNull ViewHolder viewHolder, int i) {
        Fruit fruit = fruitList.get(i);
        viewHolder.iv_name.setImageResource(fruit.getImageId());
        viewHolder.tv_name.setText("" + fruit.getName());
    }

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

    static class ViewHolder extends RecyclerView.ViewHolder {
        View fruitView;
        ImageView iv_name;
        TextView tv_name;

        public ViewHolder(@NonNull View itemView) {
            super(itemView);
            fruitView = itemView;
            iv_name = (ImageView) itemView.findViewById(R.id.iv_name);
            tv_name = (TextView) itemView.findViewById(R.id.tv_name);
        }
    }


}

我们先是修改了内部类ViewHolder,在这个内部类中声名了一个View类的变量fruitView,并且在其构造函数里面为fruitView初始化了,其次我们在onCreateView()函数中通过获得的View实例来获得我们点击屏幕适配器当前的位置,然后根据适配器的位置,在List列表中找到对应的Fruit实例,最后通过Fruit实例获得水果的名称。最终,分别为ImageView和TextView设置点击监听,Toast提示出水果的名称,这样也可以根据Toast提示,很清晰地分辨我们点击的是文字还是图片。

点击图片后的效果图:

点击文字后的效果图:

 

 

 

猜你喜欢

转载自blog.csdn.net/android_studying/article/details/86065329
今日推荐