Android手把手教你实现卡片式瀑布流效果(RecyclerView+CardView,附源码)

Android手把手教你实现卡片式瀑布流效果

卡片式的瀑布流效果是一种非常美观的UI设计,自从Android 5.0的 RecyclerView出现以后,实现瀑布流变得十分简单,本文将一步步带领读者去实现一个卡片式的瀑布流布局。

一、先上效果图

源码上传在git上上,下载直接用Android Studio打开即可https://github.com/WangLaoSeng/waterfall
Android手把手教你实现卡片式瀑布流效果(RecyclerView+CardView,附源码)_第1张图片

二、代码实现卡片式瀑布流

1. 涉及到要使用的类和控件

类或控件 介绍 作用
RecyclerView 5.0出现的类似于ListView的控件 用它的布局管理器实现两列“ListView”效果
StaggeredGridLayoutManager 交错布局管理器 实现两列“ListView”效果
RecyclerView.Adapter 数据适配器 与ListView的Adapter类似,写法上不同。
CardView 同样是5.0新出的控件 实现圆角效果,省去了自己写Drawble,这个可要可不要。
Fresco 图片开源框架 为了简化从网上下载图片的过程就使用了它做快速演示,如想了解Fresco的详情,请看本人另一篇博客:Fresco图片框架简介及使用(可做圆角图片)

2. 在Gradle中添加RecyclerView、Fresco和CardView需要的依赖

compile 'com.android.support:recyclerview-v7:24.2.0'
compile 'com.facebook.fresco:fresco:0.14.1'
compile 'com.android.support:cardview-v7:25.0.0'

3. 在Activity的布局文件中使用RecyclerView


<RelativeLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/activity_main"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="#F0F0F0"
    tools:context="com.testwaterfall.MainActivity"
    >

    <android.support.v7.widget.RecyclerView
        android:id="@+id/recyclerview"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:scrollbars="none"
        >android.support.v7.widget.RecyclerView>
RelativeLayout>

4. 构建RecyclerView的子Item View,命名为recyclerview_item.xml,这个布局里面主要是一张明显的图片和明星的名字。(这里的嵌套层数有点多,不是个好案例,为了省事就这样写了。)


<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:fresco="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:layout_margin="10dp"
    android:background="#F0F0F0"
    android:orientation="vertical"
    android:padding="5dp"
    >

    <android.support.v7.widget.CardView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:background="#FFFFFF"
        app:cardCornerRadius="5dp"
        >
        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:paddingBottom="5dp"
            android:paddingLeft="6dp"
            android:paddingRight="6dp"
            android:paddingTop="10dp"
            android:orientation="vertical"
            >
            <com.facebook.drawee.view.SimpleDraweeView
                android:id="@+id/user_avatar"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                fresco:actualImageScaleType="centerCrop"
                />

            <TextView
                android:id="@+id/user_name"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:layout_gravity="center"
                android:gravity="center"
                />
        LinearLayout>
    android.support.v7.widget.CardView>
LinearLayout>

5. 编写RecyclerView的数据适配器WaterFallAdapter

public class WaterFallAdapter extends RecyclerView.Adapter {

    private Context mContext;
    private List mData; //定义数据源

    //定义构造方法,默认传入上下文和数据源
    public WaterFallAdapter(Context context, List data) {
        mContext = context;
        mData = data;
    }

    @Override  //将ItemView渲染进来,创建ViewHolder
    public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        View view = LayoutInflater.from(mContext).inflate(R.layout.recyclerview_item, null);
        return new MyViewHolder(view);
    }

    @Override  //将数据源的数据绑定到相应控件上
    public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
        MyViewHolder holder2 = (MyViewHolder) holder;
        PersonCard personCard = mData.get(position);
        Uri uri = Uri.parse(personCard.avatarUrl);
        holder2.userAvatar.setImageURI(uri);
        holder2.userAvatar.getLayoutParams().height = personCard.imgHeight; //从数据源中获取图片高度,动态设置到控件上
        holder2.userName.setText(personCard.name);
    }

    @Override
    public int getItemCount() {
        if (mData != null) {
            return mData.size();
        }
        return 0;
    }

    //定义自己的ViewHolder,将View的控件引用在成员变量上
    public class MyViewHolder extends RecyclerView.ViewHolder {
        public SimpleDraweeView userAvatar;
        public TextView userName;

        public MyViewHolder(View itemView) {
            super(itemView);
            userAvatar = (SimpleDraweeView) itemView.findViewById(R.id.user_avatar);
            userName = (TextView) itemView.findViewById(R.id.user_name);
        }
    }
}

6. 构建一个数据Model,明星,以便于传递数据。

public class PersonCard implements Serializable{
    public String avatarUrl; //明显头像的Url
    public String name;  //明显的名字
    public int imgHeight;  //头像图片的高度
}

7. 在Activity里面初始化界面和生成、绑定数据

public class MainActivity extends Activity {

    private RecyclerView mRecyclerView;
    private RecyclerView.LayoutManager mLayoutManager;
    private WaterFallAdapter mAdapter;

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

    private void init() {
        mRecyclerView = (RecyclerView) findViewById(R.id.recyclerview);
        //设置布局管理器为2列,纵向
        mLayoutManager = new StaggeredGridLayoutManager(2, StaggeredGridLayoutManager.VERTICAL);
        mAdapter = new WaterFallAdapter(this, buildData());

        mRecyclerView.setLayoutManager(mLayoutManager);
        mRecyclerView.setAdapter(mAdapter);
    }

    //生成6个明星数据,这些Url地址都来源于网络
    private List buildData() {

        String[] names = {"邓紫棋","范冰冰","杨幂","Angelababy","唐嫣","柳岩"};
        String[] imgUrs = {"https://ss0.bdstatic.com/94oJfD_bAAcT8t7mm9GUKT-xh_/timg?image&quality=100&size=b4000_4000&sec=1477122728&di=21924aeef8f7847a651fc8bf00a28f49&src=http://www.tengtv.com/file/upload/201609/18/232836341.jpg",
                "https://ss0.bdstatic.com/94oJfD_bAAcT8t7mm9GUKT-xh_/timg?image&quality=100&size=b4000_4000&sec=1477122795&di=f740bd484870f9bcb0cafe454a6465a2&src=http://tpic.home.news.cn/xhCloudNewsPic/xhpic1501/M08/28/06/wKhTlVfs1h2EBoQfAAAAAF479OI749.jpg",
                "https://ss1.bdstatic.com/70cFvXSh_Q1YnxGkpoWK1HF6hhy/it/u=673651839,1464649612&fm=111&gp=0.jpg",
                "https://ss0.baidu.com/94o3dSag_xI4khGko9WTAnF6hhy/image/h%3D200/sign=fd90a83e900a304e4d22a7fae1c9a7c3/d01373f082025aafa480a2f1fcedab64034f1a5d.jpg",
                "https://ss1.bdstatic.com/70cFvXSh_Q1YnxGkpoWK1HF6hhy/it/u=1849074283,1272897972&fm=111&gp=0.jpg",
                "https://ss0.baidu.com/-Po3dSag_xI4khGko9WTAnF6hhy/image/h%3D200/sign=005560fc8b5494ee982208191df4e0e1/c2fdfc039245d68827b453e7a3c27d1ed21b243b.jpg",
        };

        List list = new ArrayList<>();
        for(int i=0;i<6;i++) {
            PersonCard p = new PersonCard();
            p.avatarUrl = imgUrs[i];
            p.name = names[i];
            p.imgHeight = (i % 2)*100 + 400; //偶数和奇数的图片设置不同的高度,以到达错开的目的
            list.add(p);
        }

        return list;
    }

}

三、总结

瀑布流实现的关键有3点:

  • 使用RecyclerView和它的StaggeredGridLayoutManager布局管理器,

  • 其次在于列表中的Item View的高度并不一样,这样才能达到交错开来的效果

  • 子Item的布局,让两列之间,上下之间能有一定的“空隙”,这才能达成视觉上的效果。

你可能感兴趣的:(Android)