recyclerView底部添加透明渐变

最近实现一个recyclerView透明渐变的效果,遇到了一些坑,尝试了一些方法,这里记录一下。

效果图

image.png

image.png

图片在上面显示2列,文字在下面显示1列;底部要有个透明渐变的效果,直到完全看不到。

gridLayoutManager动态设置列数

大概是分两类,一类以图片为item 一行2个,一类以文字为item 一行一个。
这个第一反应是用viewType去区分图片类型,但是由于起初不知道gridLayout可以动态列数。就在上面两列,下面一列上为难起来了。
如果统一用一列吧,那就把两个image当成一个item,但是这样要自己去计算position,而且还要考虑奇数情况下最后一行只有一个图片的情况,这样要多写一些代码和逻辑。
后面发现可以通过改变spanSize来根据viewType来改变spanCount

     //gridLayoutManager这里设置的spanCount=2
     RecyclerView.LayoutManager manager = recyclerView.getLayoutManager();
      if (manager instanceof GridLayoutManager) {
        final GridLayoutManager gridManager = ((GridLayoutManager) manager);
        gridManager.setSpanSizeLookup(new GridLayoutManager.SpanSizeLookup() {
          @Override
          public int getSpanSize(int position) {
            int type = getItemViewType(position);
            switch (type) {
              case TYPE_IMAGE:
                //占用1/2 表示2列
                Log.d("tag", "列数2");
                return 1;
              case TYPE_TEXT:
                //占用2/2 表示1列
                Log.d("tag", "列数1");
                return 2;
              default:
                Log.d("tag", "default列数2");
                return 2;
            }
          }
        });
      }

这样item的layout就很干净了,省去了一些麻烦。

recyclerView添加透明渐变

本来看着快搞完了,只差一个底部透明渐变了,结果就这一个效果高出了好多事情。

尝试了下面三种方法

  • recycleView上面盖一层渐变的layout
  • 原生的fadingEdeg,尝试通过反射去掉顶部阴影
  • 图层合并
  • 第一反应是再在recycleView上面盖一层渐变的layout。

<shape xmlns:android="http://schemas.android.com/apk/res/android"
    android:shape="rectangle">
  <gradient
    android:angle="90"
    android:startColor="@color/color_FFFFFFFF"
    android:endColor="@color/color_00FFFFFF">
  </gradient>
</shape>

效果如下:


image.png

看起来“蒙”了一点哈
但是这样有个问题,就是layout使用这个shape的时候,是从开始到结束的渐变,没有grandient的平铺的效果,也就是只有最后一点是不透明的,不能实现最后是全不透明的。而且这个00FFFFFF其实不是真正意义上的不透明,而是白色的。

效果图底下有一截是全透明的,这个和效果图是有一定差距的,单纯用shape实现不了。尝试再在渐变下面添加一截全透明,就会惨不忍睹。


image.png
  • recyclerView的fadingEdge

组里的老司机提了可以用这个recyclerView原生自带的属性。

android:requiresFadingEdge="vertical"
android:fadingEdgeLength="40dp"
image.png

运行起来看起来虽然和ui有点差距,但是好像可以接受了。
但是滑动的时候就出现问题了,这个自带fadingEdge不仅底部有,顶部也有,而且更坑的是,recyclerView没有提供取消顶部或者底部的接口。要么都不用,要么都要。


image.png

参考网上的一种方法,通过反射去设置顶部的edge宽高,从而使得顶部阴影不显示。
如何屏蔽RecyclerView单边滑动到头阴影(fadingEdge)
本来以为这样就ok了,谁知道这样设置竟然没有效果。

参考给的代码是这样设置的:


image.png

但是进api27的RecyclerView里面看,这个mTopGlow是EdgeEffect类型


image.png
mRecycleView.setOnScrollListener(new RecyclerView.OnScrollListener() {
      @Override public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
        super.onScrolled(recyclerView, dx, dy);
        EdgeEffect mTopGlow = null;
        try {
          Field topGlow = mRecycleView.getClass().getDeclaredField("mTopGlow");
          if (topGlow != null) {
            topGlow.setAccessible(true);
            mTopGlow = (EdgeEffect) topGlow.get(mRecycleView);
          }
        } catch (Exception e) {
          e.printStackTrace();
        }

        if (mTopGlow != null) {
         //设置TOP顶部的矩形阴影大小(看注释和代码大概是这个作用)
          mTopGlow.setSize(0, 0);
          mTopGlow.finish();
        }
      }
    });

这样大部分情况topGlow也是空的。查看代码,发现貌似只有这个方法才会给mTopGlow初始化


image.png

强行在获取topGlow对象前插入ensureTopGlow,使得它不为空:


image.png


  public void makeTopGlowNoneNull() {
    final Class<?> clazz = RecyclerView.class;
    try {
      Method method = clazz.getDeclaredMethod("ensureTopGlow");
      method.setAccessible(true);
      method.invoke(mRecycleView);
    } catch (NoSuchMethodException e) {
      e.printStackTrace();
    } catch (IllegalAccessException e) {
      e.printStackTrace();
    } catch (InvocationTargetException e) {
      e.printStackTrace();
    }
  }

好了,经过上面的操作,mtopFlow取出来并且设置size(0,0)了,讲道理顶部阴影只有一个长0,宽0的矩形,就相当于没有了。
然而。。。
顶部阴影还是存在,而且去调试系统创建上下阴影的场景,也发现没有调用setSize就把阴影画出来了。之后又折腾了一阵子也没看出来这个阴影在recyclerView里面怎么是怎么画出来的,有知道的大兄弟麻烦告知一下

  • 通过图层合并添加透明效果

参考Android实现直播聊天区域顶部渐变效果

 /**
     * 利用itemDecoration的onDraw和onDrawOver回调时机,进行两个图层的合并
     *
     */
    mRecycleView.addItemDecoration(new RecyclerView.ItemDecoration() {
      @SuppressLint("NewApi") @Override public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) {
        super.onDraw(c, parent, state);
        layerId = c.saveLayer(0.0f, 0.0f, (float) parent.getWidth(), (float) parent.getHeight(), mPaint);
      }

      @Override public void onDrawOver(Canvas c, RecyclerView parent, RecyclerView.State state) {
        super.onDrawOver(c, parent, state);
        if (linearGradient == null) {
          linearGradient = new LinearGradient(0f,parent.getHeight()-dpToPixel(54), 0f,
              parent.getHeight()-dpToPixel(15), new int[] {startColor,endColor },new float[]{0.2f,0.8f},
              Shader.TileMode.CLAMP);
        }
        mPaint.setXfermode(xfermode);
        mPaint.setShader(linearGradient);
        c.drawRect(0f,parent.getHeight()-dpToPixel(54),parent.getWidth(),parent.getHeight(),mPaint);
        mPaint.setXfermode(null);
        c.restoreToCount(layerId);
        /**
         * 第一次进去的时候发现画出来的东西很奇怪 暂未找到原因
         * 需要滑动之后才会正常显示,所以第一次进去的时候,手动调用一次刷新
         */

        if (recycleViewfirstComeInFlag == false) {
          recycleViewfirstComeInFlag = true;
              mRecycleView.postInvalidate();
        }
      }

      @Override public void getItemOffsets(Rect outRect, View view, RecyclerView parent,
          RecyclerView.State state) {
        super.getItemOffsets(outRect, view, parent, state);
      }
    });
  }

原理参考文章里面也说了,借助ItemDecoration的draw和drawOver方法将两个图层进行合并,用DES_IN模式,使得SRC图层底部有了DES图层的透明渐变。
看起来抄就完事儿了的东西,我却搞了好一阵子。
参考demo就是一个recyclerView,我的是dialog里面装一个recyclerView。dialog显示在中间,因为对canvas.drawRec()参数坐标以及linearGradient的使用不太熟悉,搞出来一些事情。

关于LinearGrandient的参数

image.png

前面的xy用来标明方向的。如果是Y垂直方向的渐变,只需要(0,startY)到(0,endY),X就没啥作用,保证在Y上是垂直就行。但是这个startY和endY之前我就理解成绝对坐标了,以为手机左上角是坐标原点(0,0);
后面那个postion其实是对应各种color的位置吧,配合CLAMP模式(超出起点和终点外的默认是起点和终点的颜色向外延伸)还是挺好用的。
比起第一种直接用XML shape LienarGradient的startColor endColor要灵活很多。
然而view里面没有像motionEvent那样直接getRowX的方法,就通过百分比计算来找位置,又增加了复杂度。
实际情况比这些影响因素(同时还在调fadingEdge)要多,有点自找麻烦的感觉,一时陷入僵局。

倒着回去想,既然Google没有提供获取绝对左边的方法,而且linearGradient也是设给paint给canvas用,那里面的坐标应该是和cavas是一致的。而canvas在很多地方如view的draw里面也能拿到,那明显设置成相当于canvas的坐标要方便一些。
重新清醒一下头脑,把暂时该注释掉的注释掉,通过最小粒度的改变来调试效果(不然一下子改变很多,出来的效果会让人懵逼),最后总算是搞出来了。

然而事情并没有结束。。。。。
估计是我在dialog里面显示recyclerView的关系,通过图层合并后,会形成很奇怪的效果,必须要滑动一下才可以正常显示。如果recyclerView不是装在dialog里面就不会这样,只是单存的使用就不会这样。

image.png

很奇怪,不过这样可以看到SRC和DES图层 ==
暂时处理是第一次进来在drawOver完成后再postInvalidate一次,有知道原因的老哥帮忙提点一下!

   if (recycleViewfirstComeInFlag == false) {
          recycleViewfirstComeInFlag = true;
              mRecycleView.postInvalidate();
        }

最终效果


image.png

遗留的问题

  • recyclerView fadingEdge的实现,不是flow.setSize,在调试代码时没看出来(Fixed)。
  • dialog上添加recyclerView的图层合并,最开始显示异常的原因。

更新2019/1/10(多谢评论里面的老哥指点):
接上文其实之前一直在recyclerView里面找,没找到屏蔽一端阴影的接口;但是在view里面是提供了的:

{@link View#getTopFadingEdgeStrength() {@link #getTopFadingEdgeStrength()}
 * {@link #getLeftFadingEdgeStrength() {@link #getRightFadingEdgeStrength()}}}

view提供了上下左右四端的接口,由于是顶部不需要渐变,
重写getTopFadingEdgeStrength(),返回0;

recyclerView的draw还是在view里面:


image.png
B0AA13040C7D343D6213EF63D521DA06.png
C22060FF778DEA4E9F4B76D046E36CA4.png

view里面简单看了下,改topFadeStrength的地方只有那里,然后后面会根据这个值去setScale,y传0就不会显示了.

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,772评论 6 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,458评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,610评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,640评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,657评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,590评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,962评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,631评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,870评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,611评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,704评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,386评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,969评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,944评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,179评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,742评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,440评论 2 342

推荐阅读更多精彩内容