用RecyclerView实现纵向滚动、横向滚动和瀑布流布局

    xiaoxiao2022-07-05  180

    我们知道,ListView只能实现数据纵向滚动的效果,RecyclerView可以说是增强版的List View,不仅可以轻松实现和ListView同样的效果,还优化了ListView的不足之处,目前官方是更加推荐RecyclerView,首先来新建一个RecyclerViewTest项目

    一、纵向滚动

    首先我们需要在app/build.gradle文件中添加依赖,

    dependencies { implementation fileTree(dir: 'libs', include: ['*.jar']) implementation 'com.android.support:appcompat-v7:28.0.0' implementation 'com.android.support.constraint:constraint-layout:1.1.3' implementation 'com.android.support:recyclerview-v7:28.0.0' testImplementation 'junit:junit:4.12' androidTestImplementation 'com.android.support.test:runner:1.0.2' androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2' }

    添加完记得Sync Now,接着修改activity_main.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="match_parent" > <android.support.v7.widget.RecyclerView android:id="@+id/recycler_view" android:layout_width="match_parent" android:layout_height="match_parent" /> </LinearLayout>

    在布局中加入RecyclerView控件是非常简单的,先为RecyclerView指定一个id,然后设置高度和宽度为match_parent,这样会占满整个布局的空间,要注意的是由于RecyclerView并不是内置在系统SDK中的,需要把完整的路径写出来

    这里我做的是一个水果的展示,然后建立Fruit.java

    package com.example.recyclerviewtest; 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_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" > <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_gravity="center_vertical" android:layout_marginTop="10dp" android:textColor="#FFC107"/> </LinearLayout>

    接下来我们需要为RecycleView准备一个适配器,新建FruitAdapter.java,让这个适配器继承RecyclerView.Adapter,并将泛型指定为FruitAdapter.ViewHolder,然后在ViewHolder的构造参数中要传入一个View参数,这个参数通常就是RecyclerView子项的最外层布局,通过findViewById()方法来获取布局中的ImageView和TextView

    package com.example.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; public class FruitAdapter extends RecyclerView.Adapter<FruitAdapter.ViewHolder> { private List<Fruit> mFruitList; static class ViewHolder extends RecyclerView.ViewHolder{ View fruitView; ImageView fruitImage; TextView fruitName; public ViewHolder(View view) { super(view); fruitView = view; fruitImage = (ImageView)view.findViewById(R.id.fruit_image); fruitName = (TextView)view.findViewById(R.id.fruit_name); } } // 传入一个构造函数,把数据源传进来,并赋值给全局变量mFruitList public FruitAdapter(List<Fruit> fruitList){ mFruitList = fruitList; } /** *由于FruitAdapter是继承于RecyclerView.Adapter,那么必须重写onCreateViewHolder()、 *onBindViewHolder()、getItemCount()这3个方法,onCreateViewHolder()用来创建ViewHolder *实例,然后把fruit_item布局加载进来,创建一个VIewHolder实例,并把加载进来的布局传入到 *构造函数中,最后将ViewHolder实例返回。onBindViewHolder()方法是用来对RecyclerView子 *项中的数据进行赋值,会在每个子项被滚动到屏幕内的时候执行,这里通过positon参数得到当前项 *的Fruit实例,再将数据设置到ViewHolder的ImageView和TextView当中,getItemCount()用来返回 *数据源的长度 **/ @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; } @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(); } }

    最后修改MainActivity,在这里用了一个同样的initFruits()方法,用于初始化所有的水果数据,接着在onCreate()方法中获取到RecyclerView实例,然后创建一个LinearLayoutManager对象。LayoutManager用于指定RecyclerView的布局方式,LinearLayoutManager是线性布局的意思,接下来创建FruitAdapter的实例,并将水果数据传入到FruitAdapter的构造函数中,最后调用RecyclerView的setAdapter()方法来完成适配器设置。

    package com.example.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 android.view.LayoutInflater; import android.widget.LinearLayout; 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); LinearLayoutManager layoutManager = new LinearLayoutManager(this); 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.ic_launcher); fruitList.add(apple); Fruit banner = new Fruit( ("Banner"),R.mipmap.ic_launcher); fruitList.add(banner); Fruit pear = new Fruit( ("Pear"),R.mipmap.ic_launcher); fruitList.add(pear); Fruit watermalon = new Fruit( ("Watermalon"),R.mipmap.ic_launcher); fruitList.add(watermalon); Fruit cherry = new Fruit( ("Cherry"),R.mipmap.ic_launcher); fruitList.add(cherry); } } }

    运行一下,可以看到和ListView一样的效果

    二、横向滚动

    首先对fruit_item布局进行修改,把元素改为垂直排列

    <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="100dp" android:layout_height="wrap_content" > <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_gravity="center_horizontal" android:layout_marginTop="10dp" android:textColor="#FFC107"/> </LinearLayout>

    这里要设定宽度,因为文字长度不一的话很难看,使用layout_marginTop属性让文字和图片之间保持距离。

    接下来修改MainActivity中的代码,因为LinearLayoutManger.HORIZONTAL是默认纵向排列的,我们要设为横向滚动只需要设置为LinearLayoutManager.HORIZONTAL

    package com.example.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 android.view.LayoutInflater; import android.widget.LinearLayout; 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); LinearLayoutManager layoutManager = new LinearLayoutManager(this); 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.ic_launcher); fruitList.add(apple); Fruit banner = new Fruit( ("Banner"),R.mipmap.ic_launcher); fruitList.add(banner); Fruit pear = new Fruit( ("Pear"),R.mipmap.ic_launcher); fruitList.add(pear); Fruit watermalon = new Fruit( ("Watermalon"),R.mipmap.ic_launcher); fruitList.add(watermalon); Fruit cherry = new Fruit( ("Cherry"),R.mipmap.ic_launcher); fruitList.add(cherry); } } }

    最后运行一下就可以看到横向布局了 当然,GiridLayoutManager可以用于实现网格布局,StaggeredGirdLayoutManager可以实现瀑布流布局

    三、瀑布流布局

    最后来看看瀑布流布局,首先修改一下fruit_item.xml中的代码

    这里我们为了好看把TextView的对齐属性改为居左对齐,因为待会文字会变长

    <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_margin="5dp" android:background="#D8F0ED"> <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_gravity="left" android:layout_marginTop="10dp" android:textColor="#FFC107"/> </LinearLayout>

    接着来修改MainActivity中的代码

    package com.example.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实例,StaggeredGridLayoutManager *构造函数接收两个参数,第一个参数用于指定布局的列数,表示把布局分为3列, *第二个参数表示指定布局的排列方向 **/ 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.ic_launcher); fruitList.add(apple); Fruit banner = new Fruit getRandomLengthName("Banner"),R.mipmap.ic_launcher); fruitList.add(banner); Fruit pear = new Fruit( getRandomLengthName("Pear"),R.mipmap.ic_launcher); fruitList.add(pear); Fruit watermalon = new Fruit( getRandomLengthName("Watermalon"),R.mipmap.ic_launcher); fruitList.add(watermalon); Fruit cherry = new Fruit( getRandomLengthName("Cherry"),R.mipmap.ic_launcher); fruitList.add(cherry); } } 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(); }

    因为瀑布流布局需要各个子项高度不一致才能看出明显效果,这里使用getRandLengthName()方法把名字随机重复几遍,这样保证各水果的名字长短不一样

    最后运行,可以看到效果

    四、设置监听器

    当然,我们还可以加上点击事件,不过要注意的是,RecyclerView并没有提供类似于setOnItemClickListener()这样的注册监听器方法,需要我们自己给子项具体的View去注册点击事件

    修改FruitAdapter.java

    package com.example.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; public class FruitAdapter extends RecyclerView.Adapter<FruitAdapter.ViewHolder> { private List<Fruit> mFruitList; static class ViewHolder extends RecyclerView.ViewHolder{ View fruitView; ImageView fruitImage; TextView fruitName; public ViewHolder(View view) { super(view); fruitView = view; fruitImage = (ImageView)view.findViewById(R.id.fruit_image); fruitName = (TextView)view.findViewById(R.id.fruit_name); } } public FruitAdapter(List<Fruit> fruitList){ mFruitList = fruitList; } @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(),"你点击了文字"+fruit.getName(), Toast.LENGTH_LONG).show(); } }); holder.fruitImage.setOnClickListener(new View.OnClickListener(){ @Override public void onClick(View v){ int position = holder.getAdapterPosition(); Fruit fruit = mFruitList.get(position); Toast.makeText(v.getContext(),"你点击了图片" + fruit.getName(), Toast.LENGTH_LONG).show(); } }); // 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(); } }

    我们在ViewHolder中加入了fruitView变量来保存子项最外层布局的实例,然后在onCreateViewHolder()方法中注册点击事件就可以了

    我们会看到,点击文字和点击图片是弹出不同的内容的

    最新回复(0)