zoukankan      html  css  js  c++  java
  • RecyclerView实现瀑布流效果(图文详解+源码奉送)

    最近有时间研究了一下RecyclerView,果然功能强大啊,能实现的效果还是比较多的,那么今天给大家介绍一个用RecyclerView实现的瀑布流效果。

    先来一张效果图:


    这里写图片描述


    看看怎么实现吧:
    整体工程目录结构:
    这里写图片描述

    这里要特别强调一点,有人可能不知道去哪里找android-support-v7-recyclerview.jar这个文件,其实它就在你下载的sdk目录下,我的是在D:Program FilesAndroidandroid-sdkextrasandroidsupportv7 ecyclerviewlibs,直接把这个文件拷贝到自己工程libs目录下即可。不建议在网上找这个jar包,可能会由于版本问题不能使用。

    要使用RecyclerView,和ListView一样,先在布局文件中引用:


    activity_main.xml

    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:tools="http://schemas.android.com/tools"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:background="#000000"
        android:orientation="vertical"
        tools:context="com.example.recyclerview_waterfall.MainActivity" >
    
        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="48dp"
            android:gravity="right">
    
            <ImageView
                android:id="@+id/add"
                android:layout_width="24dp"
                android:layout_margin="12dp"
                android:layout_height="24dp"
                android:src="@drawable/add" />
        </LinearLayout>
    
        <android.support.v7.widget.RecyclerView
            android:id="@+id/lv"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content" />
    
    </LinearLayout>

    上面是主布局文件,再看看Item的布局文件:


    <?xml version="1.0" encoding="utf-8"?>
    <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:padding="1dp"
        android:layout_height="wrap_content" >
    
        <ImageView
            android:id="@+id/iv"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:background="#B0E0E6"
            android:src="@drawable/ic_launcher" />
    
        <TextView
            android:id="@+id/tv"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:background="@null"
            android:textSize="18sp" />
    
    </FrameLayout>

    item布局文件中就两样东西,一个TextView,一张图片。使用FrameLayout让两个空间重叠在一起。
    好了,布局说完了就该说MainActivity了,毫无疑问,先要初始化RecyclerView:

    rv = (RecyclerView) findViewById(R.id.lv);
            rv.setHasFixedSize(true);
            StaggeredGridLayoutManager layoutManager = new StaggeredGridLayoutManager(
                    3, StaggeredGridLayoutManager.VERTICAL);
            rv.setLayoutManager(layoutManager);

    使用StaggeredGridLayoutManager来实现瀑布流效果,它的构造方法中有两个参数,第一个是每行显示几个item,第二个参数是滚动的方向。
    RecyclerView初始化完成之后我们就该初始化模拟数据了:

    /**
         * 初始化模拟数据
         */
        private void initData() {
            list = new ArrayList<String>();
            ivs = new ArrayList<Integer>();
            heights = new ArrayList<Integer>();
            for (int i = 'A'; i <= 'Z'; i++) {
                list.add("" + (char) i);
                heights.add((int) (100 + Math.random() * 300));
            }
            ivs.add(R.drawable.p1);
            ivs.add(R.drawable.p2);
            ivs.add(R.drawable.p3);
            ivs.add(R.drawable.p4);
            ivs.add(R.drawable.p5);
            ivs.add(R.drawable.p6);
            ivs.add(R.drawable.p7);
            ivs.add(R.drawable.p8);
            ivs.add(R.drawable.p9);
            ivs.add(R.drawable.p10);
            ivs.add(R.drawable.p11);
            ivs.add(R.drawable.p12);
            ivs.add(R.drawable.p13);
            ivs.add(R.drawable.p14);
            ivs.add(R.drawable.p15);
            ivs.add(R.drawable.p16);
            ivs.add(R.drawable.p17);
            ivs.add(R.drawable.p18);
            ivs.add(R.drawable.p19);
            ivs.add(R.drawable.p20);
            ivs.add(R.drawable.p21);
            ivs.add(R.drawable.p22);
            ivs.add(R.drawable.p23);
            ivs.add(R.drawable.p24);
            ivs.add(R.drawable.p25);
            ivs.add(R.drawable.p26);
            DisplayMetrics dm = new DisplayMetrics();
            getWindowManager().getDefaultDisplay().getMetrics(dm);
            mScreenWidth = dm.widthPixels;
        }

    这里主要有四个数据要初始化:
    list表示TextView上显示的数据
    ivs表示ImageView上显示图片的id
    heights表示每个item的高,因为瀑布流中item高度不一,所以我们通过随机数来设定不同的高度。
    最后一个mScreenWidth表示屏幕的宽度,我们要根据每行显示几个item来动态设定每个item的宽度。

    做完这些就可以new一个Adapter了:

    public class WaterFallAdapter extends Adapter {
    
        private Context context;
        private List<String> list;
        private LayoutInflater layoutInflater;
        private List<Integer> heights;
        private List<Integer> ivs;
        private int mScreenWidth;
        public OnItemClickListener listener;
    
        public interface OnItemClickListener {
            void onItemClick(View v, int position);
    
            void onItemLongClick(View v, int position);
        }
    
        public void setOnItemClickListener(OnItemClickListener onItemClickListener) {
            this.listener = onItemClickListener;
        }
    
        public WaterFallAdapter(Context context, List<String> list,
                List<Integer> heights, List<Integer> ivs, int mScreenWidth) {
            this.context = context;
            this.list = list;
            this.heights = heights;
            this.ivs = ivs;
            this.mScreenWidth = mScreenWidth;
            layoutInflater = LayoutInflater.from(context);
    
        }
    
        @Override
        public int getItemCount() {
            return ivs.size();
        }
    
        @Override
        public void onBindViewHolder(ViewHolder viewHolder, final int position) {
            final MyViewHolder vHolder = (MyViewHolder) viewHolder;
            LayoutParams tvLp = vHolder.tv.getLayoutParams();
            tvLp.height = heights.get(position);
            tvLp.width = mScreenWidth / 3;
            vHolder.tv.setLayoutParams(tvLp);
            vHolder.tv.setText(list.get(position));
            vHolder.iv.setLayoutParams(tvLp);
            vHolder.iv.setImageResource(ivs.get(position));
            vHolder.iv.setOnClickListener(new OnClickListener() {
    
                @Override
                public void onClick(View v) {
                    int position2 = vHolder.getPosition();
                    listener.onItemClick(v, position2);
                }
            });
            vHolder.iv.setOnLongClickListener(new OnLongClickListener() {
    
                @Override
                public boolean onLongClick(View v) {
                    int position2 = vHolder.getPosition();
                    listener.onItemLongClick(v, position2);
                    return true;
                }
            });
        }
    
        @Override
        public ViewHolder onCreateViewHolder(ViewGroup viewGroup, int pos) {
            View v = layoutInflater.inflate(R.layout.item, null);
            ViewHolder vh = new MyViewHolder(v);
            return vh;
        }
    
        public static class MyViewHolder extends RecyclerView.ViewHolder {
    
            public TextView tv;
            public ImageView iv;
    
            public MyViewHolder(View itemView) {
                super(itemView);
                tv = (TextView) itemView.findViewById(R.id.tv);
                iv = (ImageView) itemView.findViewById(R.id.iv);
            }
    
        }
    }

    RecyclerView中我们使用ViewHolder时要继承RecyclerView.ViewHolder,在这里初始化item,至于回收防止多次实例化等等都由RecyclerView来完成。onBindViewHolder是我们赋值的方法,在这里我们把数据匹配给item去显示。这里我们根据屏幕的宽度动态设置每个item的宽度。

    RecyclerView最大的一个缺陷是没有提供点击事件,所以如果有需要的话要我们自己定义一个接口来完成这个动作。代码如上,我们在很多自定义View中都可以看到类似的方式。
    定义好事件点击接口后,我们就可以在MainActivity中调用了:

                myAdapter.setOnItemClickListener(new OnItemClickListener() {
    
                @Override
                public void onItemLongClick(View v, int position) {
                    Toast.makeText(MainActivity.this, position + "--long click",
                            Toast.LENGTH_SHORT).show();
                    list.remove(position);
                    ivs.remove(position);
                    myAdapter.notifyItemRemoved(position);
                }
    
                @Override
                public void onItemClick(View v, int position) {
                    Toast.makeText(MainActivity.this, position + "--click",
                            Toast.LENGTH_SHORT).show();
                }
            });

    点击直接弹出一个Toast,长按删除该项,删除的时候系统有自带动画,当然我们也可以自定义删除动画。删除之后我们使用myAdapter.notifyItemRemoved(position);而不是使用notifyDataSetChanged()因为前者是局部刷新,只会刷新item变化的部分,而后者会整体刷新,这样会影响动画效果的显示。

    最后再来看看右上角点击事件:

    add = (ImageView) findViewById(R.id.add);
            add.setOnClickListener(new OnClickListener() {
    
                @Override
                public void onClick(View v) {
                    list.add(1, "大家好,我是新来的,请多多关照");
                    ivs.add(1, ivs.get((int) (Math.random() * 26)));
                    myAdapter.notifyItemInserted(1);
                }
            });

    注意刷新方式是一样的。数据添加的位置也是数据显示的位置。

    好了,就简单介绍这么多,有问题欢迎留言讨论。

    本工程源码下载

  • 相关阅读:
    [NOI2012]美食节——费用流(带权二分图匹配)+动态加边
    mysqlsla slow-query常用用法
    [POI2000]病毒
    mysqlsla安装
    mysqldumpslow
    [学习笔记]约数
    查看MySQL数据的连接
    [学习笔记]质数
    关于ulimit -a中需要修改的两个值
    Miller-Rabin与二次探测
  • 原文地址:https://www.cnblogs.com/qitian1/p/6461759.html
Copyright © 2011-2022 走看看