标签:前言 sni 源码 middle 过程 xtend pos ppc err
前言
RecyclerView是Google在support-v7里面添加的控件,是5.0 Material Design模式下的一员,在众多的App中使用非常频繁,之前是ListView现在是RecyclerView,想比之下RecyclerView更加的灵活,高内聚低耦合,将ListView功能进行了拆分,各个类各司其职构成了现在的RecyclerView。
效果~
Part 1、LinearLayoutAppCompat源码分析
在使用RecyclerView的分割线之前,不得不介绍一下LinearLayoutAppCompat,此控件是support-v7提供的,你可以通过在xml里面配置实现每个子孩子下面都会有个分割线
效果~
- app:divider="@drawable/divider_vertical_holo_light"
- app:showDividers="middle"
tips:
1、app:divider="" 设置分割线的drawable
2、app:showDivider="" 设置显示分割线(none、begining、end、middle)
源码分析
LinearLayoutAppCompat继承于ViewGroup,都会调用onMeasure、onLayout、onDraw方法,通过查看可知绘制分割线的代码在onDraw方法里面
- @Override
- protected void onDraw(Canvas canvas) {
- if (mDivider == null) {
- return;
- }
-
- if (mOrientation == VERTICAL) {
- drawDividersVertical(canvas);
- } else {
- drawDividersHorizontal(canvas);
- }
- }
这里我们只需要看drawDividersVertical的方法即可,drawDividersHorizontal绘制差不多
- void drawDividersVertical(Canvas canvas) {
- final int count = getVirtualChildCount();
- for (int i = 0; i < count; i++) {
- final View child = getVirtualChildAt(i);
-
- if (child != null && child.getVisibility() != GONE) {
- if (hasDividerBeforeChildAt(i)) {
- final LayoutParams lp = (LayoutParams) child.getLayoutParams();
- final int top = child.getTop() - lp.topMargin - mDividerHeight;
- drawHorizontalDivider(canvas, top);
- }
- }
- }
-
- if (hasDividerBeforeChildAt(count)) {
- final View child = getVirtualChildAt(count - 1);
- int bottom = 0;
- if (child == null) {
- bottom = getHeight() - getPaddingBottom() - mDividerHeight;
- } else {
- final LayoutParams lp = (LayoutParams) child.getLayoutParams();
- bottom = child.getBottom() + lp.bottomMargin;
- }
- drawHorizontalDivider(canvas, bottom);
- }
- }
tips:
1、绘制过程:第一个Child的顶部分割线->最后一个Child顶部分割线->最后一个Child底部分割线
2、通过判断hasDividerBeforeChildAt()方法来判断(none、begining、end、middle)
Part 2、实现RecyclerView线性方向间隔线
因为RecyclerView没有提供分割线,所以我们需要自己来进行添加
- public class MyItemDecoration extends RecyclerView.ItemDecoration {
- @Override
- public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
- }
-
- @Override
- public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) {
- super.onDraw(c, parent, state);
- }
- @Override
- public void onDrawOver(Canvas c, RecyclerView parent, RecyclerView.State state) {
- super.onDrawOver(c, parent, state);
- }
- }
tips:
1、回调先后顺序:getItemOffsets()->onDraw()->onDrawOver()
2、getItemOffsets() 获得每个Item的偏移量 onDraw() : 绘制分割线 onDrawOver():绘制在Item的上层(也可以将绘制分割线放在此方法里面)
偏移量具体表示:
所以在getItemOffsets方法应该这样写
- @Override
- public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
-
- if(orientation == LinearLayoutManager.VERTICAL){
- outRect.set(0,0,0,divide.getIntrinsicHeight());
- }else{
- outRect.set(0,0,divide.getIntrinsicWidth(),0);
- }
- }
然后就是在onDraw里面进行绘制分割线
-
-
-
-
-
-
- private void drawableVertical(Canvas c, RecyclerView parent) {
- int count = parent.getChildCount();
- int left = parent.getPaddingLeft();
- int right = parent.getWidth() - parent.getPaddingRight();
- for (int i = 0; i < count; i++) {
- View child = parent.getChildAt(i);
- RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child.getLayoutParams();
- int top = child.getBottom() + params.bottomMargin+(int)ViewCompat.getTranslationY(child);
- int bottom = top + divide.getIntrinsicHeight();
- divide.setBounds(left, top, right, bottom);
- divide.draw(c);
- }
- }
tips:
1、ViewCompat.getTranslationY(child) : 考虑动画变换的影响应该加上这个
Part 3、RecyclerView的网格分割线
思路:定义两个方法,一个画横线一个画竖线
so,getItemOffsets()方法
- @Override
- @Deprecated
- public void getItemOffsets(Rect outRect, int itemPosition,
- RecyclerView parent) {
-
- int right = divide.getIntrinsicWidth();
- int bottom = divide.getIntrinsicHeight();
- outRect.set(0, 0, right, bottom);
-
- }
onDraw方法
- private void drawHorizontal(Canvas c, RecyclerView parent) {
- int count = parent.getChildCount();
- for (int i = 0; i < count; i++) {
- View childView = parent.getChildAt(i);
- RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) childView.getLayoutParams();
- int left = childView.getLeft() - params.leftMargin;
- int right = childView.getRight() + params.rightMargin;
- int top = childView.getBottom() + params.bottomMargin;
- int bottom = top + divide.getIntrinsicHeight();
- divide.setBounds(left, top, right, bottom);
- divide.draw(c);
- }
- }
-
- private void drawVertical(Canvas c, RecyclerView parent) {
- int count = parent.getChildCount();
- for (int i = 0; i < count; i++) {
- View childView = parent.getChildAt(i);
- RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) childView.getLayoutParams();
- int left = childView.getRight() + params.rightMargin;
- int right = left + divide.getIntrinsicWidth();
- int top = childView.getTop() - params.topMargin;
- int bottom = childView.getBottom() + params.bottomMargin;
- divide.setBounds(left, top, right, bottom);
- divide.draw(c);
- }
- }
具体意思很容易理解
效果~
在效果图中可以发现,顶部和左侧没有线,下部和右侧有线。那怎么样才能去掉下部和右侧的线呢?这里有个好方法便是在分配Item便宜量的时候对position进行判断,符合则下部和右侧条件则将bottom和right置为0即可,代码如下
- @Override
- @Deprecated
- public void getItemOffsets(Rect outRect, int itemPosition,
- RecyclerView parent) {
-
- int right = divide.getIntrinsicWidth();
- int bottom = divide.getIntrinsicHeight();
- if (isLastColum(itemPosition, parent)) {
- right = 0;
- }
- if (isLastRow(itemPosition, parent)) {
- bottom = 0;
- }
- outRect.set(0, 0, right, bottom);
- }
判断是否为最后一列
-
-
-
-
-
-
-
- private boolean isLastColum(int itemPosition, RecyclerView parent) {
- RecyclerView.LayoutManager layoutManager = parent.getLayoutManager();
-
- if (layoutManager instanceof GridLayoutManager) {
- int spanCount = getSpanCount(parent);
- if ((itemPosition + 1) % spanCount == 0) {
- return true;
- }
- }
- return false;
- }
判断是否为最后一行
-
-
-
-
-
-
-
- private boolean isLastRow(int itemPosition, RecyclerView parent) {
- int spanCount = getSpanCount(parent);
- RecyclerView.LayoutManager layoutManager = parent.getLayoutManager();
-
- if (layoutManager instanceof GridLayoutManager) {
- int childCount = parent.getAdapter().getItemCount();
- int lastRowCount = childCount % spanCount;
-
- if (lastRowCount == 0 || lastRowCount < spanCount) {
- return true;
- }
- }
- return false;
- }
- private int getSpanCount(RecyclerView parent) {
- RecyclerView.LayoutManager layoutManager = parent.getLayoutManager();
- if (layoutManager instanceof GridLayoutManager) {
- GridLayoutManager lm = (GridLayoutManager) layoutManager;
- int spanCount = lm.getSpanCount();
- return spanCount;
- }
- return 0;
- }
至此bug解决了
艺术控件RecyclerView的分隔线&bug解决
标签:前言 sni 源码 middle 过程 xtend pos ppc err
原文地址:http://blog.csdn.net/weiwozhiyi/article/details/54463202