SwipeRefreshLayout+CardView+RecyclerView 精美瀑布流效果

博主声明:

转载请在开头附加本文链接及作者信息,并标记为转载。本文由博主 威威喵 原创,请多支持与指教。

本文首发于此   博主威威喵  |  博客主页https://blog.csdn.net/smile_running

 

感受 Material Design UI 魅力,你不能错过的 Material Design 全系列文章:

Material Design 之一 BottomNavigationView + ViewPager + Fragment 仿微信底部导航效果

Material Design 之二 Toolbar + DrawerLayout + NavigationView 实现QQ侧拉抽屉效果

Material Design 之三 TabLayout + ViewPager + Fragment 今日头条标题切换效果

Material Design 之四 SwipeRefreshLayout + CardView + RecyclerView 精美瀑布流效果

Material Design 之五 CoordinatorLayout + AppBarLayout 提供最佳浏览体验

Material Design 之六 CollapsingToolbarLayout + FloatingActionButton 打造精美详情界面

Material Design 之七 TextInputLayout+TextInputEditText 打造精美的登录界面

    RecyclerView 是我们 Android 开发中必备的控件之一,可以说基本每一个复杂一点的界面都需要 RecyclerView 来帮助处理,因为 RecyclerView 的可定制性非常高,我们可以玩出各种花样来。

    不仅如此,RecyclerView 自带的三种布局形式,比如线性、网格、瀑布流都非常容易就能用上的,我们可以根据需要,自行的设置页面样式,接下来,我们使用 RecyclerView 来使用一下这三种布局,并且配合 design 库下的 CardView 控件,制作一个精美的动物信息浏览的界面。

先看看效果图吧:

    在此之前呢,我们把准备工作给做好,第一个是我的代码是沿用上篇文章:TabLayout+ViewPager 快速实现标题切换效果

第二个是由于本次图片的量比较大,因为我们在 RecyclerView 中要引入大量的图片来浏览,所以不得不使用第三方开源的图片加载库,这也是为了方便。

首先,在 app 项目中引入 Glide 图片加载库,sync 一下会自动下载:

    implementation 'com.github.bumptech.glide:glide:4.9.0'

然后它的用法非常简单,比如这样:

    Glide.with(context).load(data.get(i).get("pic")).into(holder.img);

RecyclerView 线性布局

好了,基于这个代码上, 我们添加一个线性布局的 RecyclerView 

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout 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/line_recy_view"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />
</RelativeLayout>

然后,它的 Item 布局文件如下:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:padding="8dp"
    android:layout_height="140dp">

    <ImageView
        android:id="@+id/img_recy_item_1_pic"
        android:layout_width="140dp"
        android:layout_height="match_parent"
        android:scaleType="centerCrop" />

    <TextView
        android:id="@+id/tv_recy_item_1_name"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginLeft="16dp"
        android:layout_marginTop="8dp"
        android:layout_toRightOf="@id/img_recy_item_1_pic"
        android:textSize="23sp" />

    <TextView
        android:id="@+id/tv_recy_item_1_desc"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentBottom="true"
        android:layout_marginLeft="16dp"
        android:layout_marginBottom="8dp"
        android:layout_toRightOf="@id/img_recy_item_1_pic"
        android:textSize="18sp" />
</RelativeLayout>

接下来是它的适配器,在适配器中设置 item 布局和加载每一个 item 的数据:

package nd.no.xww.bottomnavigationlayout;

import android.content.Context;
import android.media.Image;
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.ImageView;
import android.widget.TextView;

import com.bumptech.glide.Glide;

import java.util.List;
import java.util.Map;

/**
 * @author xww
 * @desciption :
 * @date 2019/7/24
 * @time 12:06
 */
public class RecyclerLineAdapter extends RecyclerView.Adapter<RecyclerLineAdapter.ViewHolder> {

    Context context;
    List<Map<String, Object>> data;

    public RecyclerLineAdapter(Context context, List<Map<String, Object>> data) {
        this.data = data;
        this.context = context;
    }

    @NonNull
    @Override
    public ViewHolder onCreateViewHolder(@NonNull ViewGroup viewGroup, int i) {
        View view = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.recycler_one_view_1_item, viewGroup, false);
        return new ViewHolder(view);
    }

    @Override
    public void onBindViewHolder(@NonNull ViewHolder holder, int i) {
        Glide.with(context).load(data.get(i).get("pic")).into(holder.img);
        holder.name.setText(data.get(i).get("name").toString());
        holder.desc.setText(data.get(i).get("desc").toString());
    }

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

    public class ViewHolder extends RecyclerView.ViewHolder {

        ImageView img;
        TextView name;
        TextView desc;

        public ViewHolder(@NonNull View itemView) {
            super(itemView);

            img = itemView.findViewById(R.id.img_recy_item_1_pic);
            name = itemView.findViewById(R.id.tv_recy_item_1_name);
            desc = itemView.findViewById(R.id.tv_recy_item_1_desc);

        }
    }
}

 添加数据源和设置适配器

    int[] pics = {
            R.drawable.a1,
            R.drawable.a2,
            R.drawable.a3,
            R.drawable.a4,
            R.drawable.a5,
            R.drawable.a6,
            R.drawable.a7,
            R.drawable.a8,
            R.drawable.a9,
            R.drawable.a10,
            R.drawable.a11,
            R.drawable.a12,
    };

    String[] names = {
            "北极熊",
            "犀牛",
            "花豹",
            "白马",
            "小鹦鹉",
            "袋鼠",
            "狐狸",
            "小猫咪",
            "哈巴狗",
            "蜥蜴",
            "大熊猫",
            "蚂蚁",
    };

    List<Map<String, Object>> lineData = new ArrayList<>();

    private void addData() {
        Map<String, Object> map = null;
        Random random = new Random();
        for (int i = 0; i < 30; i++) {
            int n = random.nextInt(pics.length);
            map = new HashMap<>();
            map.put("pic", pics[n]);
            map.put("name", names[n]);
            map.put("desc", "我是一只" + names[n]);
            lineData.add(map);
        }
    }

        // 设置为线性布局
        lineRecycler.setLayoutManager(new LinearLayoutManager(getContext()));

        // 设置适配器
        lineRecycler.setAdapter(new RecyclerLineAdapter(getContext(), lineData));

然后,我们运行一下是这样的效果:

 不过这样的界面并不是特别好看,于是,我们就给它的 item 添加一个 CardView,使得我们的每一个 Item 都像一张张卡片那样呈现出来,修改 recyclerview item 布局如下:

<?xml version="1.0" encoding="utf-8"?>
<android.support.v7.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="140dp"
    android:layout_margin="8dp"
    app:cardCornerRadius="8dp"
    app:cardElevation="4dp">

    <RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:padding="8dp">

        <ImageView
            android:id="@+id/img_recy_item_1_pic"
            android:layout_width="140dp"
            android:layout_height="match_parent"
            android:scaleType="centerCrop" />

        <TextView
            android:id="@+id/tv_recy_item_1_name"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginLeft="16dp"
            android:layout_marginTop="8dp"
            android:layout_toRightOf="@id/img_recy_item_1_pic"
            android:textSize="23sp" />

        <TextView
            android:id="@+id/tv_recy_item_1_desc"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_alignParentBottom="true"
            android:layout_marginLeft="16dp"
            android:layout_marginBottom="8dp"
            android:layout_toRightOf="@id/img_recy_item_1_pic"
            android:textSize="18sp" />
    </RelativeLayout>
</android.support.v7.widget.CardView>

    我们在原先基础的最外层包裹了一个 CardView,并且设置了两个 CardView 经常使用的属性。一个是 cardElevation 指的是 CardView 的一个高度,可以这样想象,RecyclerView 是一个平面,而 CardView 就在它的上面,我们可以通过设置 cardElevation 来控制这个高度,也就是 CardView 在这样的高度时的一个阴影效果,高度越高,阴影越明显。

    还有一个属性是 cardCornerRadius 指的是 CardView 四周的弧度,值越大,圆弧就越明显。那么,我们修改完之后的效果如下:

 嗯,这样看起来就有点舒服了哦,我是比较喜欢这样的效果,看上去像一张一张小卡片一样,浏览起来也舒服。

RecyclerView 网格布局

    上面我们介绍的就是 RecyclerView 线性布局的使用,效果还是不错的,一般可以用于浏览个人信息列表操作。接着,我们来看第二种布局形式:网格布局。

    网格布局也是非常常用的,一般用于图片相册的浏览,接下来的工作就是复制粘贴一下原先的代码就可以了,因为适配器里面的代码几乎一个样,我们可以考虑把它封装起来用,不过现在还是怎么方便怎么来。

    对于线性布局来说,网格布局是需要做一定量的修改的,也就是布局文件的修改,我们把旁边的 “我是一只...” 文本给去掉,保留了图片和动物名称,所以布局文件代码修改为:

<?xml version="1.0" encoding="utf-8"?>
<android.support.v7.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="180dp"
    android:layout_margin="8dp"
    app:cardCornerRadius="8dp"
    app:cardElevation="4dp">

    <RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:padding="8dp">

        <ImageView
            android:id="@+id/img_recy_item_2_pic"
            android:layout_width="match_parent"
            android:layout_height="120dp"
            android:scaleType="centerCrop" />

        <TextView
            android:id="@+id/tv_recy_item_2_name"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_below="@id/img_recy_item_2_pic"
            android:layout_centerInParent="true"
            android:layout_marginTop="8dp"
            android:layout_marginBottom="8dp"
            android:textSize="18sp" />

    </RelativeLayout>
</android.support.v7.widget.CardView>

没了这个,我们适配器中也相应的把 textview 给删除,这个应该很简单,就是把 desc 给删了就好了,还是贴一下代码吧:

package nd.no.xww.bottomnavigationlayout;

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.ImageView;
import android.widget.TextView;

import com.bumptech.glide.Glide;

import java.util.List;
import java.util.Map;

/**
 * @author xww
 * @desciption :
 * @date 2019/7/24
 * @time 12:06
 */
public class RecyclerGridAdapter extends RecyclerView.Adapter<RecyclerGridAdapter.ViewHolder> {

    Context context;
    List<Map<String, Object>> data;

    public RecyclerGridAdapter(Context context, List<Map<String, Object>> data) {
        this.data = data;
        this.context = context;
    }

    @NonNull
    @Override
    public ViewHolder onCreateViewHolder(@NonNull ViewGroup viewGroup, int i) {
        View view = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.recycler_one_view_2_item, viewGroup, false);
        return new ViewHolder(view);
    }

    @Override
    public void onBindViewHolder(@NonNull ViewHolder holder, int i) {
        Glide.with(context).load(data.get(i).get("pic")).into(holder.img);
        holder.name.setText(data.get(i).get("name").toString());
    }

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

    public class ViewHolder extends RecyclerView.ViewHolder {

        ImageView img;
        TextView name;

        public ViewHolder(@NonNull View itemView) {
            super(itemView);

            img = itemView.findViewById(R.id.img_recy_item_2_pic);
            name = itemView.findViewById(R.id.tv_recy_item_2_name);

        }
    }
}

然后,是数据做一下简单的修改,把对应的 desc 数据给删除:

  private void addGridData() {
        Map<String, Object> map = null;
        Random random = new Random();
        for (int i = 0; i < 30; i++) {
            int n = random.nextInt(pics.length);
            map = new HashMap<>();
            map.put("pic", pics[n]);
            map.put("name", names[n]);
            gridData.add(map);
        }
    }

最重要的一步,为 RecyclerView 设置布局管理样式:

        //设置网格布局样式,2为列数
        gridRecycler.setLayoutManager(new GridLayoutManager(getContext(), 2));

        gridRecycler.setAdapter(new RecyclerGridAdapter(getContext(), gridData));

好了,这样我们再运行一下,效果如下:

 这样就完成了网格布局的设置,效果看起来还可以。

RecyclerView 瀑布流布局

    最后一个布局样式是瀑布流的布局,其实和网格布局几乎一样的,网格布局是规规矩矩的,而瀑布流就是有长有短的那种,有错位和落差感,有时候太规矩的不好看,有一点错位显得更加美观。

    瀑布流的 RecyclerView Item 布局文件要注意了,不能写固定的一个高度,否则就没有效果了。比如,我们得这样改:

<?xml version="1.0" encoding="utf-8"?>
<android.support.v7.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layout_margin="8dp"
    app:cardCornerRadius="8dp"
    app:cardElevation="4dp">

    <RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:padding="8dp">

        <ImageView
            android:id="@+id/img_recy_item_3_pic"
            android:layout_width="match_parent"
            android:layout_height="120dp"
            android:scaleType="centerCrop" />

        <TextView
            android:id="@+id/tv_recy_item_3_name"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_below="@id/img_recy_item_3_pic"
            android:layout_centerInParent="true"
            android:layout_marginTop="8dp"
            android:textSize="16sp" />

    </RelativeLayout>
</android.support.v7.widget.CardView>

    注意上面的代码,cardview 的高度不能固定,以及下面的 textview 高度也都不能固定值,都要写为 wrap_content,适配器就不需要修改了,要改的地方就是数据格式还有 RecyclerView 的布局管理样式。

我们添加数据要改为这样,名称有长的有短的,才能形成长短不一的瀑布流的形式。

    private void addStaggeredData() {
        Map<String, Object> map = null;
        Random random = new Random();
        String[] str = {
                "瀑布流\n",
                "瀑布流\n瀑布流\n",
                "瀑布流\n瀑布流\n瀑布流\n",
                "瀑布流\n瀑布流\n瀑布流\n瀑布流\n",
        };

        for (int i = 0; i < 30; i++) {
            int n = random.nextInt(pics.length);
            map = new HashMap<>();
            map.put("pic", pics[n]);
            map.put("name", names[n] + "\n" + str[random.nextInt(str.length)]);
            staggeredData.add(map);
        }
    }

最关键的还是 RecyclerView 的设置:

        // 设置瀑布流形式,2为两列
        staggerRecycler.setLayoutManager(new StaggeredGridLayoutManager(2, StaggeredGridLayoutManager.VERTICAL));

        // 设置适配器
        staggerRecycler.setAdapter(new RecyclerStaggeredAdapter(getContext(), staggeredData));

好了,我们运行一下,就会产生错落的瀑布流效果了:

    那么,到此为止呢,几种  RecyclerView 常用的布局形式都给试了一遍,总体来说都非常不错的,特别是配合 CardView 以后,显示效果更佳。不过 design 库里面还有一个比较漂亮的控件,与 RecyclerView 也是天生绝配的存在,那就是下拉刷新的控件:SwipeRefreshLayout,最后呢,我们继续把 SwipeRefreshLayout 添加到 RecyclerView 上,看看效果如何吧。

下拉刷新 SwipeRefreshLayout

    我们就那网格布局例子为基础, 在它的 RecyclerView 上面加上一个 SwipeRefreshLayout 控件,这样要注意一下,SwipeRefreshLayout 必须是 RecyclerView 的父容器,也就是 SwipeRefreshLayout  包裹 RecyclerView,看下面的代码你就非常清楚了:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <android.support.v4.widget.SwipeRefreshLayout
        android:id="@+id/swipe_refresh"
        android:layout_width="match_parent"
        android:layout_height="match_parent">

        <android.support.v7.widget.RecyclerView
            android:id="@+id/grid_recy_view"
            android:layout_width="match_parent"
            android:layout_height="match_parent" />
        
    </android.support.v4.widget.SwipeRefreshLayout>

</RelativeLayout>

    SwipeRefreshLayout 要刷新的话,需要添加一个监听事件,又因为适配器在刷新后需要把数据也刷新一下,然后再绑定到 RecyclerView 上,代码如下所示。

        swipeRefreshLayout.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {
            @Override
            public void onRefresh() {
                gridData.clear();
                addGridData();
                gridAdapter.notifyDataSetChanged();

                new Handler().postDelayed(new Runnable() {
                    @Override
                    public void run() {
                        if (swipeRefreshLayout.isRefreshing()) {
                            swipeRefreshLayout.setRefreshing(false);
                        }
                    }
                }, 1500);

            }
        });

    首先,要把集合给清空,然后为集合添加新的数据,最后调用适配器通知一下重新绑定数据就搞定了。那么效果如下:

 这里有个与 SwipeRefreshLayout 相关的比较常用的属性,直接看下面的代码就知道了:

        //为下拉刷新,设置一组颜色
        swipeRefreshLayout.setColorSchemeColors(Color.BLUE, Color.RED, Color.GREEN);
        //设置触发刷新的距离
        swipeRefreshLayout.setDistanceToTriggerSync(200);
        //设置滑动的距离
        swipeRefreshLayout.setSlingshotDistance(400);

 刷新时就会不停的变化进度的颜色效果,显得花里胡哨的,当然了,你也可以只添加一种颜色。

还有两个用于设置刷新的监听与取消情况:

        //开始刷新,false 取消刷新
        swipeRefreshLayout.setRefreshing(true);
        //判断是否正在刷新
        swipeRefreshLayout.isRefreshing();

好了,结合上面的几个控件,我们可以随意的写出好看的界面。

  • 13
    点赞
  • 66
    收藏
    觉得还不错? 一键收藏
  • 37
    评论
好的,我很乐意回答您的问题。首先,RecyclerView 是一个强大的视图组件,用于显示大量数据集合。它可以高效地管理和显示列表、网格和瀑布流等多种样式的布局。而 CardView 则是一个 Material Design 风格的容器视图,用于显示单个数据项的卡片式布局。 在 Android 中使用 RecyclerViewCardView 需要先在 build.gradle 文件中添加依赖库: ```groovy dependencies { implementation 'com.android.support:recyclerview-v7:28.0.0' implementation 'com.android.support:cardview-v7:28.0.0' } ``` 接下来,在布局文件中声明 RecyclerViewCardView: ```xml <android.support.v7.widget.RecyclerView android:id="@+id/recyclerview" android:layout_width="match_parent" android:layout_height="match_parent" android:clipToPadding="false" android:padding="@dimen/default_padding" android:scrollbarStyle="outsideOverlay" android:scrollbars="vertical" /> <android.support.v7.widget.CardView android:id="@+id/cardview" android:layout_width="match_parent" android:layout_height="wrap_content" app:cardCornerRadius="@dimen/default_corner_radius" app:cardElevation="@dimen/default_elevation" app:cardUseCompatPadding="true"> <!-- 在 CardView 中添加需要显示的内容 --> </android.support.v7.widget.CardView> ``` 最后,在代码中使用 RecyclerViewCardView: ```java RecyclerView recyclerView = findViewById(R.id.recyclerview); recyclerView.setLayoutManager(new LinearLayoutManager(this)); recyclerView.setAdapter(new MyAdapter()); CardView cardView = findViewById(R.id.cardview); cardView.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { // 处理点击事件 } }); ``` 其中,MyAdapter 是一个自定义的适配器类,用于管理和显示数据集合。在该类中,需要实现 onCreateViewHolder、onBindViewHolder 和 getItemCount 等方法,以便正确地显示数据项。 总之,RecyclerViewCardView 是 Android 中常用的界面组件,它们可以帮助我们高效地显示列表和卡片式布局。
评论 37
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值