路要走,生活还得继续。

Hardworking Now

Android开发之RecyclerView的使用(二)

今天是准备竞赛的第二天,和两个朋友在一起,很开心。天气不是很好,但是心情却比昨天好了不少,今天继续昨天的内容,继续讲讲RecyclerView这个强大的控件。

3.2 RecyclerView增加分隔线

前面我们说到可以通过RecyclerView.addItemDecoration(ItemDecoration decoration)这个方法进行设置,其中它需要的参数就是我们自己定义的继承自ItemDecoration的一个对象。我们可以创建一个继承RecyclerView.ItemDecoration类来绘制分隔线,通过ItemDecoration可以让我们每一个Item从视觉上面相互分开来,例如ListView的divider非常相似的效果。当然像我们上面的例子ItemDecoration我们没有设置也没有报错,那说明ItemDecoration我们并不是强制需要使用,作为我们开发者可以设置或者不设置Decoration的。实现一个ItemDecoration,系统提供的ItemDecoration是一个抽象类,内部除去已经废弃的方法以外,我们主要实现以下三个方法:

public static abstract class ItemDecoration {   
        public void onDraw(Canvas c,RecyclerView parent,State state) {   
          onDraw(c,parent);   
      }   
      public void onDrawOver(Canvas c,RecyclerView parent,State state) {   
          onDrawOver(c,parent);   
      }   
      public void getItemOffsets(RectoutRect, View view,RecyclerView parent,State state) {   
          getItemOffsets(outRect,((LayoutParams)view.getLayoutParams()).getViewLayoutPosition(),parent);   
      }   
  }

又因为当我们RecyclerView在进行绘制的时候会进行绘制Decoration,那么会去调用onDrawonDrawOver方法,那么这边我们其实只要去重写onDrawgetItemOffsets这两个方法就可以实现啦。然后LayoutManager会进行Item布局的时候,会去调用getItemOffset方法来计算每个Item的Decoration合适的尺寸,下面我们来具体实现一个Decoration,DividerItemDecoration.Java

package com.example.reclerviewpractice;  
  
import android.content.Context;  
import android.content.res.TypedArray;  
import android.graphics.Canvas;  
import android.graphics.Rect;  
import android.graphics.drawable.Drawable;  
import android.support.v7.widget.LinearLayoutManager ;  
import android.support.v7.widget.RecyclerView;  
import android.view.View;  
  
public class DividerItemDecoration 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 DividerItemDecoration(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);  
            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(0, 0, 0, mDivider.getIntrinsicHeight());  
        }else{  
            outRect.set(0, 0, mDivider.getIntrinsicWidth(), 0);  
        }  
    }  
}

在这里我们采用系统主题(android.R.attr.listDivider)来设置成分隔线的,然后来获取尺寸,位置进行setBound(),绘制,接着通过outRect.set()来设置绘制整个区域范围,当然了它是有两种情况的一种LinearLayoutManager.HORIZONTAL另外一种LinearLayoutManager.VERTICAL需要分别对其进行处理最后不要忘记往RecyclerView中设置该自定义的分割线,然后在MainActivity中加上一句recyclerView .addItemDecoration(new DividerItemDecoration(MainActivity.this,LinearLayoutManager.VERTICAL))即给RecyclerView增加分隔线。然后运行,效果如下:

可以看到已经有了分隔线,跟ListView的效果基本一致了。当然了,既然谷歌给我们提供了这个专门添加分隔线的方法,那它肯定会允许我们自定义分隔线的样式,不然把这个方法抽取出来也没有任何意义。

  3.3 改变分隔线样式

     那么怎么更改分隔线的样式呢?在上面的DividerItemDecoration这个类中可以看到这个分隔线是跟ListView一样的,即系统的默认的样式,因此我们可以在styles的xml文件中进行更改,更改如下:
<!-- Application theme. -->  
    <style name ="AppTheme" parent="AppBaseTheme">  
        <!-- All customizations that are NOT specific to a particular API-level can go here. -->  
        <item name= "android:listDivider">@drawable/divider </item >   
    </style >

divider的内容如下:

<?xml version="1.0" encoding= "utf-8"?>  
<shape xmlns:android="http://schemas.android.com/apk/res/android"  
    android:shape="rectangle" >  
     
        <!-- 填充的颜色 -->  
       <solid android:color ="@color/color_red"/>  
       
      <!--  线条大小 -->  
      <size android:height ="1dp" android:width ="1dp"/>  
</shape>

修改之后运行效果如下:

可以看到分隔线的颜色变了,当然了这只是一个小例子,我们可以按照业务需求去更改,这样就基本实现了ListView的效果,看到这肯定会有人说,这尼玛,好麻烦,还不如ListView简单呢,从上面的代码量看来确实是使用起来很复杂,但是如果此时你想将这个列表以GridView的形式展示出来,用RecylerView仅仅是换一行代码的事情,
在上面的代码中我们使用了
LinearLayoutManager layoutManager = new LinearLayoutManager(this);  
 //设置布局管理器  
recyclerView.setLayoutManager(layoutManager);

RecyclerView.LayoutManager是一个抽象类,系统为我们提供了三个实现类
①LinearLayoutManager即线性布局,这个是在上面的例子中我们用到的布局
GridLayoutManager即表格布局
StaggeredGridLayoutManager即流式布局,如瀑布流效果
假如将上述例子换成GridView的效果,那么相应的代码应该这样改

recyclerView .setLayoutManager(new GridLayoutManager( this,4));

除此之外上述的分隔线也要做相应的更改,因为在上述DividerItemDecoration这个方法中从

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

这两行我们可以看出来,它是绘制了一条线这条线就是从RecyclerView去掉左右边距后,剩余的部分,因为当显示成ListView时每一行就一个Item所以整体效果看上去就跟ListView差不多,而当展示成GridView那样的效果时,每一行就不止一个条目了,而有可能是多个,所以这个类就不再适用了,我们需要重新写一个,这里我就直接用鸿洋大神写的了,它的博客地址:http://blog.csdn.net/lmj623565791/article/details/45059587

package com.example.reclerviewpractice;  
  
import android.content.Context;  
import android.content.res.TypedArray;  
import android.graphics.Canvas;  
import android.graphics.Rect;  
import android.graphics.drawable.Drawable;  
import android.support.v7.widget.GridLayoutManager;  
import android.support.v7.widget.RecyclerView;  
import android.support.v7.widget.RecyclerView.LayoutManager;  
import android.support.v7.widget.RecyclerView.State;  
import android.support.v7.widget.StaggeredGridLayoutManager;  
import android.view.View;  
  
/** 
 * 
 * @author zhy 
 * 
 */  
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, State state)  
     {  
  
           drawHorizontal(c, parent);  
           drawVertical(c, parent);  
  
     }  
  
     private int getSpanCount(RecyclerView parent)  
     {  
            // 列数  
            int spanCount = -1;  
           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)  
     {  
           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)  
     {  
           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(0, 0, mDivider.getIntrinsicWidth(), 0);  
           } else if (isLastColum(parent, itemPosition, spanCount, childCount))// 如果是最后一列,则不需要绘制右边  
           {  
                outRect.set(0, 0, 0, mDivider.getIntrinsicHeight());  
           } else  
           {  
                outRect.set(0, 0, mDivider.getIntrinsicWidth(),  
                            mDivider.getIntrinsicHeight());  
           }  
     }  
}

别忘了更改分隔线recyclerView .addItemDecoration(new DividerGridItemDecoration(this ));之后运行发现效果如下

可以看到如果你准备好了分隔线的这个类,从ListView效果到GridView效果,只需要几行代码,是不是瞬间感觉高大上了?还有更让人瞠目结舌的效果,将上述代码做如下更改

[java] view plain copy 在CODE上查看代码片派生到我的代码片
StaggeredGridLayoutManager layoutManager = new StaggeredGridLayoutManager(4,StaggeredGridLayoutManager.HORIZONTAL);  
//设置布局管理器  
recyclerView.setLayoutManager(layoutManager);

 

这里需要注意的是StaggeredGridLayoutManager构造的第二个参数传一个orientation,如果传入的是StaggeredGridLayoutManager.VERTICAL那么前面那个参数就代表有多少列;如果传是StaggeredGridLayoutManager.HORIZONTAL那么前面那个参数就代表有多少行

运行效果如下

这效果是不是有点逆天?可以看到,固定为4行,变成了左右滑动。有一点需要注意,如果是横向的时候,item的宽度需要注意去设置,毕竟横向的宽度没有约束了,因为控件可以横向滚动了,另外它还可以实现瀑布流的效果,关于瀑布流我准备后面专门写一篇博客。

 

今天就到这里,明天准备写一写RecyclerView增加和删除的动画,今天有点累,GN~

点赞

发表评论

电子邮件地址不会被公开。 必填项已用*标注