Android RecyclerView详解及实现瀑布流式布局

2016/10/18 15:01
阅读数 2.5K

RecyclerView一个可以代替ListView和GridView的控件,那么RecyclerView到底比他们好在哪里?

RecyclerView架构提供了一种插拔式的体验,所以实现了代码的高度解耦,使用起来也异常的灵活。

 

我们可以通过设置它的LayoutManager控制其显示的方式,通过ItemDecoration控制Item间的间隔,通过ItemAnimator控制Item的增删动画

RecyclerView.LayoutManager提供了三个实现类其中LinearLayoutManager 现行管理器,支持横向、纵向,GridLayoutManager 网格布局管理器,StaggeredGridLayoutManager 瀑布就式布局管理器

 

那么先从LinearLayoutManager看起

先在gradle中引用compile 'com.Android.support:recyclerview-v7:23.4.0'

Activity布局文件如下:

1

2

3

4

5

6

7

8

9

10

11

12

13

<?xml version="1.0" encoding="utf-8"?>  

<RelativeLayout 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"  

    tools:context="com.lg.recyclerviewdemo.LinearActivity">  

   

    <android.support.v7.widget.RecyclerView  

        android:id="@+id/linear_recycler"  

        android:layout_width="match_parent"  

        android:layout_height="match_parent"/>  

   

</RelativeLayout>

item布局文件:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

<?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">  

   

    <LinearLayout  

        android:layout_width="match_parent"  

        android:layout_height="match_parent"  

        android:gravity="center">  

   

        <ImageView  

            android:layout_width="70dp"  

            android:layout_height="70dp"  

            android:src="@drawable/android" />  

   

        <TextView  

            android:id="@+id/recycler_item_tv"  

            android:layout_width="wrap_content"  

            android:layout_height="100dp"  

            android:layout_marginLeft="20dp"  

            android:gravity="center"  

            android:textColor="@color/colorPrimary"  

            android:textSize="20sp"  

            android:textStyle="bold" />  

    </LinearLayout>  

</RelativeLayout>

在Acitvity中初始化数据:

1

2

3

4

mDatas = new ArrayList<String>();  

for (int i = 1; i <= 65; i++) {  

      mDatas.add("item"+i);  

}

核心代码:

1

2

3

4

5

6

7

recyclerAdapter = new RecyclerAdapter();  

//设置布局管理器  

linear_recycler.setLayoutManager(new LinearLayoutManager(this));  

//设置adapter  

linear_recycler.setAdapter(recyclerAdapter);  

//添加分割线  

linear_recycler.addItemDecoration(new DividerLinearItemDecoration(this, DividerLinearItemDecoration.VERTICAL_LIST));

接下来自制adapter:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

public class RecyclerAdapter extends RecyclerView.Adapter<LinearHolder> {  

    private View view;  

   

    @Override  

    public LinearHolder onCreateViewHolder(ViewGroup parent, int viewType) {  

        //利用反射将item的布局加载出来  

        view = LayoutInflater.from(parent.getContext()).inflate(R.layout.recycler_item, null);  

        //new一个我们的ViewHolder,findViewById操作都在LinearHolder的构造方法中进行了  

        return new LinearHolder(view);  

    }  

   

    @Override  

    public void onBindViewHolder(LinearHolder holder, int position) {  

        holder.recycler_item.setText(MainActivity.mDatas.get(position));  

    }  

   

    @Override  

    public int getItemCount() {  

        return MainActivity.mDatas.size();  

    }  

}  

   

class LinearHolder extends RecyclerView.ViewHolder {  

    TextView recycler_item;  

    public LinearHolder(View itemView) {  

        super(itemView);  

        recycler_item = (TextView) itemView.findViewById(R.id.recycler_item_tv);  

    }

再绘画它的分割线:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

public class DividerLinearItemDecoration extends RecyclerView.ItemDecoration {  

   

    private static final int[] ATTRS = new int[]{  

            android.R.attr.listDivider  

    };  

   

    public static final int HORIZONTAL_LIST = LinearLayoutManager.HORIZONTAL;  

   

    public static final int VERTICAL_LIST = LinearLayoutManager.VERTICAL;  

   

    private Drawable mDivider;  

   

    private int mOrientation;  

   

    public DividerLinearItemDecoration(Context context, int orientation) {  

        final TypedArray a = context.obtainStyledAttributes(ATTRS);  

        mDivider = a.getDrawable(0);  

        a.recycle();  

        setOrientation(orientation);  

    }  

   

    public void setOrientation(int orientation) {  

        if (orientation != HORIZONTAL_LIST && orientation != VERTICAL_LIST) {  

            throw new IllegalArgumentException("invalid orientation");  

        }  

        mOrientation = orientation;  

    }  

   

    @Override  

    public void onDraw(Canvas c, RecyclerView parent) {  

        if (mOrientation == VERTICAL_LIST) {  

            drawVertical(c, parent);  

        else {  

            drawHorizontal(c, parent);  

        }  

    }  

   

   

    public void drawVertical(Canvas c, RecyclerView parent) {  

        final int left = parent.getPaddingLeft();  

        final int right = parent.getWidth() - parent.getPaddingRight();  

   

        final int childCount = parent.getChildCount();  

        for (int i = 0; i < childCount; i++) {  

            final View child = parent.getChildAt(i);  

            RecyclerView v = new RecyclerView(parent.getContext());  

            final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child  

                    .getLayoutParams();  

            final int top = child.getBottom() + params.bottomMargin;  

            final int bottom = top + mDivider.getIntrinsicHeight();  

            mDivider.setBounds(left, top, right, bottom);  

            mDivider.draw(c);  

        }  

    }  

   

    public void drawHorizontal(Canvas c, RecyclerView parent) {  

        final int top = parent.getPaddingTop();  

        final int bottom = parent.getHeight() - parent.getPaddingBottom();  

   

        final int childCount = parent.getChildCount();  

        for (int i = 0; i < childCount; i++) {  

            final View child = parent.getChildAt(i);  

            final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child  

                    .getLayoutParams();  

            final int left = child.getRight() + params.rightMargin;  

            final int right = left + mDivider.getIntrinsicHeight();  

            mDivider.setBounds(left, top, right, bottom);  

            mDivider.draw(c);  

        }  

    }  

   

    @Override  

    public void getItemOffsets(Rect outRect, int itemPosition, RecyclerView parent) {  

        if (mOrientation == VERTICAL_LIST) {  

            outRect.set(000, mDivider.getIntrinsicHeight());  

        else {  

            outRect.set(00, mDivider.getIntrinsicWidth(), 0);  

        }  

    }  

}

好了,我们来看看效果:

wKiom1dXbuygmqK3AAe-rLGwO18242.gif

嘛,貌似和ListView没什么区别,还这么麻烦

别急,我们试试GridLayoutManager

很简单,我们只需要改变LayoutManager和ItemDecoration就行了:

1

2

grid_recycler.setLayoutManager(new GridLayoutManager(this,2));  

grid_recycler.addItemDecoration(new DividerGridItemDecoration(this));

DividerGridItemDecoration代码:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

107

108

109

110

111

112

113

114

115

116

117

118

119

120

121

122

123

124

125

126

127

128

129

130

131

132

133

134

135

136

137

138

139

140

141

142

143

144

145

146

147

148

149

150

151

152

153

154

155

156

public class DividerGridItemDecoration extends RecyclerView.ItemDecoration {  

    private static final int[] ATTRS = new int[] { android.R.attr.listDivider };  

    private Drawable mDivider;  

   

    public DividerGridItemDecoration(Context context)  

    {  

        final TypedArray a = context.obtainStyledAttributes(ATTRS);  

        mDivider = a.getDrawable(0);  

        a.recycle();  

    }  

   

    @Override  

    public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state)  

    {  

   

        drawHorizontal(c, parent);  

        drawVertical(c, parent);  

   

    }  

   

    private int getSpanCount(RecyclerView parent)  

    {  

        // 列数  

        int spanCount = -1;  

        RecyclerView.LayoutManager layoutManager = parent.getLayoutManager();  

        if (layoutManager instanceof GridLayoutManager)  

        {  

   

            spanCount = ((GridLayoutManager) layoutManager).getSpanCount();  

        else if (layoutManager instanceof StaggeredGridLayoutManager)  

        {  

            spanCount = ((StaggeredGridLayoutManager) layoutManager)  

                    .getSpanCount();  

        }  

        return spanCount;  

    }  

   

    public void drawHorizontal(Canvas c, RecyclerView parent)  

    {  

        int childCount = parent.getChildCount();  

        for (int i = 0; i < childCount; i++)  

        {  

            final View child = parent.getChildAt(i);  

            final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child  

                    .getLayoutParams();  

            final int left = child.getLeft() - params.leftMargin;  

            final int right = child.getRight() + params.rightMargin  

                    + mDivider.getIntrinsicWidth();  

            final int top = child.getBottom() + params.bottomMargin;  

            final int bottom = top + mDivider.getIntrinsicHeight();  

            mDivider.setBounds(left, top, right, bottom);  

            mDivider.draw(c);  

        }  

    }  

   

    public void drawVertical(Canvas c, RecyclerView parent)  

    {  

        final int childCount = parent.getChildCount();  

        for (int i = 0; i < childCount; i++)  

        {  

            final View child = parent.getChildAt(i);  

   

            final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child  

                    .getLayoutParams();  

            final int top = child.getTop() - params.topMargin;  

            final int bottom = child.getBottom() + params.bottomMargin;  

            final int left = child.getRight() + params.rightMargin;  

            final int right = left + mDivider.getIntrinsicWidth();  

   

            mDivider.setBounds(left, top, right, bottom);  

            mDivider.draw(c);  

        }  

    }  

   

    private boolean isLastColum(RecyclerView parent, int pos, int spanCount,  

                                int childCount)  

    {  

        RecyclerView.LayoutManager layoutManager = parent.getLayoutManager();  

        if (layoutManager instanceof GridLayoutManager)  

        {  

            if ((pos + 1) % spanCount == 0)// 如果是最后一列,则不需要绘制右边  

            {  

                return true;  

            }  

        else if (layoutManager instanceof StaggeredGridLayoutManager)  

        {  

            int orientation = ((StaggeredGridLayoutManager) layoutManager)  

                    .getOrientation();  

            if (orientation == StaggeredGridLayoutManager.VERTICAL)  

            {  

                if ((pos + 1) % spanCount == 0)// 如果是最后一列,则不需要绘制右边  

                {  

                    return true;  

                }  

            else  

            {  

                childCount = childCount - childCount % spanCount;  

                if (pos >= childCount)// 如果是最后一列,则不需要绘制右边  

                    return true;  

            }  

        }  

        return false;  

    }  

   

    private boolean isLastRaw(RecyclerView parent, int pos, int spanCount,  

                              int childCount)  

    {  

        RecyclerView.LayoutManager layoutManager = parent.getLayoutManager();  

        if (layoutManager instanceof GridLayoutManager)  

        {  

            childCount = childCount - childCount % spanCount;  

            if (pos >= childCount)// 如果是最后一行,则不需要绘制底部  

                return true;  

        else if (layoutManager instanceof StaggeredGridLayoutManager)  

        {  

            int orientation = ((StaggeredGridLayoutManager) layoutManager)  

                    .getOrientation();  

            // StaggeredGridLayoutManager 且纵向滚动  

            if (orientation == StaggeredGridLayoutManager.VERTICAL)  

            {  

                childCount = childCount - childCount % spanCount;  

                // 如果是最后一行,则不需要绘制底部  

                if (pos >= childCount)  

                    return true;  

            else  

            // StaggeredGridLayoutManager 且横向滚动  

            {  

                // 如果是最后一行,则不需要绘制底部  

                if ((pos + 1) % spanCount == 0)  

                {  

                    return true;  

                }  

            }  

        }  

        return false;  

    }  

   

    @Override  

    public void getItemOffsets(Rect outRect, int itemPosition,  

                               RecyclerView parent)  

    {  

        int spanCount = getSpanCount(parent);  

        int childCount = parent.getAdapter().getItemCount();  

        if (isLastRaw(parent, itemPosition, spanCount, childCount))// 如果是最后一行,则不需要绘制底部  

        {  

            outRect.set(00, mDivider.getIntrinsicWidth(), 0);  

        else if (isLastColum(parent, itemPosition, spanCount, childCount))// 如果是最后一列,则不需要绘制右边  

        {  

            outRect.set(000, mDivider.getIntrinsicHeight());  

        else  

        {  

            outRect.set(00, mDivider.getIntrinsicWidth(),  

                    mDivider.getIntrinsicHeight());  

        }  

    }  

}

看下效果吧:

wKiom1dXb8qhuw4AAA1eFtv66qw243.gif

渍,有点意思,不过也没那么神乎其神啊

别忘了,我们还有个StaggeredGridLayoutManager没用

展示了那么多纵向的,我们来个横向的,同样改变LayoutManager

1

stag_grid_recycler.setLayoutManager(new StaggeredGridLayoutManager(3,StaggeredGridLayoutManager.HORIZONTAL));

看下效果:

wKiom1dXcMyDrPtmAAlGwpCV-YM923.gif

一个RecyclerView就能实现这么多功能,确实强大啊

不过,你以为这样就完了?下来要放大招了

免费学习更多精品课程,登录乐搏学院官网http://h.learnbo.cn/

 

看标题,瀑布流有木有,你用ListView不会那么简单就实现吧,但是用RecyclerView分分钟

稍微改变item布局,让图片放在字的上面

我们在onBindViewHolder给item设置随机高度:

1

2

LayoutParams layoutParams = holder.sg_item.getLayoutParams();  

layoutParams.height = heights.get(position);

看下大招效果:

wKioL1dXcpHR4gJkAAuJ6EU5yuk291.gif

我就问你6不6,6的话还不快关注我(嘎嘎)

好吧,可能吓到你了,什么?点击事件?

好吧,很不幸告诉你,要自己写,对,就是要自己写。

前面已经说过了,RecyclerView实现了高度解耦,非常的灵活(你要干什么,自己去写)。那就写吧!

先写个接口:

1

2

3

4

5

6

public interface OnItemClickLitener {  

    /*点击事件*/  

    void onItemClick(View view, int position);  

    /*长按事件*/  

    void onItemLongClick(View view, int position);  

}

在adapter中加入代码:

1

2

3

4

5

private OnItemClickLitener mOnItemClickLitener;  

   

public void setOnItemClickLitener(OnItemClickLitener mOnItemClickLitener) {  

      this.mOnItemClickLitener = mOnItemClickLitener;  

}

onBindViewHolder方法中加入:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

holder.sg_item.setOnClickListener(new View.OnClickListener() {  

       @Override  

       public void onClick(View v) {  

              int pos = holder.getLayoutPosition();  

              mOnItemClickLitener.onItemClick(holder.itemView, pos);  

          }  

       });  

holder.sg_item.setOnLongClickListener(new View.OnLongClickListener() {  

      @Override  

      public boolean onLongClick(View v) {  

             int pos = holder.getLayoutPosition();  

             mOnItemClickLitener.onItemLongClick(holder.itemView, pos);  

             return false;  

      }  

});

然后在Activity中调用:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

staggeredGridAdapter.setOnItemClickLitener(new OnItemClickLitener() {  

            @Override  

            public void onItemClick(View view, int position) {  

                staggeredGridAdapter.notifyItemRemoved(position);  

            }  

   

            @Override  

            public void onItemLongClick(View view, final int position) {  

                android.support.v7.app.AlertDialog.Builder builder = new AlertDialog.Builder(StaggeredGridVActivity.this);  

                builder.setTitle("Delete?")  

                        .setNegativeButton("no"null)  

                        .setPositiveButton("yes"new DialogInterface.OnClickListener() {  

                            @Override  

                            public void onClick(DialogInterface dialog, int which) {  

                                staggeredGridAdapter.notifyItemRemoved(position);  

                                Toast.makeText(StaggeredGridVActivity.this,MainActivity.mDatas.get(position),Toast.LENGTH_SHORT).show();  

                            }  

                        }).show();  

            }  

        });

效果图:

wKiom1dXcmXhm0gbAAWp7e1t6Vw175.gif

怎么样?厉害吧。不过你以为这样就完了?

如果我想要将item托拉拽再加上侧滑删除呢?

首先,如果要实现托拉拽功能,那item长按事件还是不要写代码的,避免事件冲突

然后在Activity中加入代码:

1

2

ItemTouchHelper itemTouchHelper = new ItemTouchHelper(callback);  

itemTouchHelper.attachToRecyclerView(stag_v_recycler);

callback代码:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

ItemTouchHelper.Callback callback = new ItemTouchHelper.Callback() {  

   

       //这个方法是用来设置我们拖动的方向以及侧滑的方向的

       @Override  

       public int getMovementFlags(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) {  

           //设置拖拽方向为上下左右  

           final int dragFlags = ItemTouchHelper.UP | ItemTouchHelper.DOWN |  

                   ItemTouchHelper.LEFT | ItemTouchHelper.RIGHT;  

           //设置侧滑方向为从左到右和从右到左都可以  

           final int swipeFlags = ItemTouchHelper.START | ItemTouchHelper.END;  

           //将方向参数设置进去  

           return makeMovementFlags(dragFlags, swipeFlags);  

       }  

   

       /** 

        * @param recyclerView 

        * @param viewHolder 拖动的ViewHolder 

        * @param target 目标位置的ViewHolder 

        * @return 

        */  

       @Override  

       public boolean onMove(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, RecyclerView.ViewHolder target) {  

           int fromPosition = viewHolder.getAdapterPosition();//得到拖动ViewHolder的position  

           int toPosition = target.getAdapterPosition();//得到目标ViewHolder的position  

           if (fromPosition < toPosition) {  

               //分别把中间所有的item的位置重新交换  

               for (int i = fromPosition; i < toPosition; i++) {  

                   Collections.swap(MainActivity.mDatas, i, i + 1);  

               }  

           else {  

               for (int i = fromPosition; i > toPosition; i--) {  

                   Collections.swap(MainActivity.mDatas, i, i - 1);  

               }  

           }  

           staggeredGridAdapter.notifyItemMoved(fromPosition, toPosition);  

           //返回true表示执行拖动  

           return true;  

       }  

   

       @Override  

       public void onSwiped(RecyclerView.ViewHolder viewHolder, int direction) {  

           int position = viewHolder.getAdapterPosition();  

           staggeredGridAdapter.notifyItemRemoved(position);  

       }  

   

       @Override  

       public void onChildDraw(Canvas c, RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, float dX, float dY, int actionState, boolean isCurrentlyActive) {  

           super.onChildDraw(c, recyclerView, viewHolder, dX, dY, actionState, isCurrentlyActive);  

           if (actionState == ItemTouchHelper.ACTION_STATE_SWIPE) {  

               //滑动时改变Item的透明度  

               final float alpha = 1 - Math.abs(dX) / (float) viewHolder.itemView.getWidth();  

               viewHolder.itemView.setAlpha(alpha);  

               viewHolder.itemView.setTranslationX(dX);  

           }  

       }  

   

   };

OK,我们来看看效果:

wKioL1dXdKHy7VZpAAomf3UaDH4928.gif

如果你喜欢我的文章,那就关注我的博客吧,我会不定期的发些技术贴

源码地址:http://down.51cto.com/data/2222200

本文出自 “Android开发专栏” 博客,请务必保留此出处http://liuyvhao.blog.51cto.com/11690759/1787183

展开阅读全文
打赏
0
0 收藏
分享
加载中
更多评论
打赏
0 评论
0 收藏
0
分享
返回顶部
顶部