Android中recyclerView底部添加透明渐变效果

前言

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

效果图

图片在上面显示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>

效果如下:

看起来“蒙”了一点哈

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

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

recyclerView的fadingEdge

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

android:requiresFadingEdge="vertical"
android:fadingEdgeLength="40dp"

运行起来看起来虽然和ui有点差距,但是好像可以接受了。

但是滑动的时候就出现问题了,这个自带fadingEdge不仅底部有,顶部也有,而且更坑的是,recyclerView没有提供取消顶部或者底部的接口。要么都不用,要么都要。

参考网上的一种方法,通过反射去设置顶部的edge宽高,从而使得顶部阴影不显示。

如何屏蔽RecyclerView单边滑动到头阴影(fadingEdge)

本来以为这样就ok了,谁知道这样设置竟然没有效果。

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

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

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初始化

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

 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的参数

前面的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里面就不会这样,只是单存的使用就不会这样。

很奇怪,不过这样可以看到SRC和DES图层 ==

暂时处理是第一次进来在drawOver完成后再postInvalidate一次,有知道原因的老哥帮忙提点一下!

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

最终效果

遗留的问题

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

总结

以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,如果有疑问大家可以留言交流,谢谢大家对我们的支持。

您可能感兴趣的文章:

  • Android中使用RecyclerView实现下拉刷新和上拉加载
  • Android中RecyclerView布局代替GridView实现类似支付宝的界面
  • Android中RecyclerView实现横向滑动代码
  • Android RecyclerView实现下拉刷新和上拉加载
  • Android项目实战之仿网易新闻的页面(RecyclerView )
  • Android RecyclerView 上拉加载更多及下拉刷新功能的实现方法
  • Android中RecyclerView嵌套滑动冲突解决的代码片段
  • Android使用RecyclerView实现水平滚动控件
(0)

相关推荐

  • Android使用RecyclerView实现水平滚动控件

    前言 相信大家都知道Android滚动控件的实现方式有很多, 使用RecyclerView也比较简单. 做了一个简单的年龄滚动控件, 让我们来看看RecyclerView的使用方式, 主要有以下几点: (1) 对齐控件中心位置. (2) 计算滚动距离. (3) 高亮中心视图. (4) 实时显示中心数据. (5) 停止时自动对齐. (6) 滚动时, 设置按钮状态开关. 效果 1. 框架 主要关注RecyclerView部分逻辑. /** * 初始化年龄滑动条 */ private void ini

  • Android项目实战之仿网易新闻的页面(RecyclerView )

    本文实例实现一个仿网易新闻的页面,上面是轮播的图片,下面是 RecyclerView 显示新闻列表,具体内容如下 错误方法 <?xml version="1.0" encoding="utf-8"?> <LinearLayout ...> <ViewPager ... /> <android.support.v7.widget.RecyclerView .../> </LinearLayout> 这样布局

  • Android RecyclerView 上拉加载更多及下拉刷新功能的实现方法

    RecyclerView 已经出来很久了,但是在项目中之前都使用的是ListView,最近新的项目上了都大量的使用了RecycleView.尤其是瀑布流的下拉刷新,网上吧啦吧啦没有合适的自己总结了一哈. 先贴图上来看看: 使用RecyclerView实现上拉加载更多和下拉刷新的功能我自己有两种方式: 1.使用系统自带的Android.support.v4.widget.SwipeRefreshLayout这个控价来实现. 2.自定义的里面带有RecyleView的控件. 使用RecycleVie

  • Android RecyclerView实现下拉刷新和上拉加载

    RecyclerView已经出来很久了,许许多多的项目都开始从ListView转战RecyclerView,那么,上拉加载和下拉刷新是一件很有必要的事情. 在ListView上,我们可以通过自己添加addHeadView和addFootView去添加头布局和底部局实现自定义的上拉和下拉,或者使用一些第三方库来简单的集成,例如Android-pulltorefresh或者android-Ultra-Pull-to-Refresh,后者的自定义更强,但需要自己实现上拉加载. 而在下面我们将用两种方式

  • Android中RecyclerView实现横向滑动代码

    RecyclerView 是Android L版本中新添加的一个用来取代ListView的SDK,它的灵活性与可替代性比listview更好.本文给大家介绍Android中RecyclerView实现横向滑动代码,一起看看吧. android.support.v7.widget.RecyclerView 功能:RecyclerView横向滑动 控件:<android.support.v7.widget.RecyclerView /> Java类:RecyclerView.GalleryAdap

  • Android中使用RecyclerView实现下拉刷新和上拉加载

    推荐阅读:使用RecyclerView添加Header和Footer的方法                       RecyclerView的使用之HelloWorld RecyclerView 是Android L版本中新添加的一个用来取代ListView的SDK,它的灵活性与可替代性比listview更好.本文给大家介绍如何为RecyclerView添加下拉刷新和上拉加载,过去在ListView当中添加下拉刷新和上拉加载是非常方便的利用addHeaderView和addFooterVie

  • Android中RecyclerView布局代替GridView实现类似支付宝的界面

    单纯使用GridView 通用的两种给GridView 添加分割线的方法:http://stackoverflow.com/questions/7132030/android-gridview-draw-dividers 给Gridview 添加分割线,也就是实现网格布局,不清楚谷歌为什么没有给Gridview 添加一个类似 ListView 的Divider 属性,因此就需要我们自己去添加分割线, 目前两种方法,第一种是 利用GridView 的  android:horizontalSpac

  • Android中RecyclerView嵌套滑动冲突解决的代码片段

    在纵向RecyclerView嵌套横向RecyclerView时,如果纵向RecyclerView有下拉刷新功能,那么内部的横向RecyclerView的横向滑动体验会很差.(只有纯横向滑动时,才能滑动内部的横向RecyclerView,否则滑动事件就会影响到下拉刷新),添加拦截判断. public class MySwipeRefreshLayout extends SwipeRefreshLayout { private boolean mIsVpDragger; private final

  • Android中recyclerView底部添加透明渐变效果

    前言 最近实现一个recyclerView透明渐变的效果,遇到了一些坑,尝试了一些方法,这里记录一下. 效果图 图片在上面显示2列,文字在下面显示1列:底部要有个透明渐变的效果,直到完全看不到. gridLayoutManager动态设置列数 大概是分两类,一类以图片为item 一行2个,一类以文字为item 一行一个. 这个第一反应是用viewType去区分图片类型,但是由于起初不知道gridLayout可以动态列数.就在上面两列,下面一列上为难起来了. 如果统一用一列吧,那就把两个image

  • Android中RecyclerView实现Item添加和删除的代码示例

    本文介绍了Android中RecyclerView实现Item添加和删除的代码示例,分享给大家,具体如下: 先上效果图: RecyclerView简介: RecyclerView用以下两种方式简化了数据的展示和处理: 1. 使用LayoutManager来确定每一个item的排列方式. 2. 为增加和删除项目提供默认的动画效果,也可以自定义. RecyclerView项目结构如下: Adapter:使用RecyclerView之前,你需要一个继承自RecyclerView.Adapter的适配器

  • Android 中RecyclerView顶部刷新实现详解

    Android 中RecyclerView顶部刷新实现详解 1. RecyclerView顶部刷新的原理 RecyclerView顶部刷新的实现通常都是在RecyclerView外部再包裹一层布局.在这个外层布局中,还包含一个自定义的View,作为顶部刷新时的指示View.也就是说,外层布局中包含两个child,一个顶部刷新View,一个RecyclerView,顶部刷新View默认是隐藏不可见的.在外层布局中对滑动事件进行处理,当RecyclerView滑动到顶部并继续下滑的时候,根据滑动的距

  • Android中RecyclerView上拉下拉,分割线,多条目的实例代码

    //activity的xml <?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:id="@+id/activity

  • Android 中RecyclerView通用适配器的实现

    Android 中RecyclerView通用适配器的实现 前言: SDK的5.0版本出来已经N久了,可以说是已经经过许多人的检验了,里面的新控件不能说是非常完美,但也是非常好用了,其中最让我喜爱的就是RecyclerView了,可以完美替代ListView和GridView(除了添加headerview和footview了,网上有许多解决方式.这个下面会以一种简单的方式顺带解决,肯定为大家省心),而且可以代码动态切换这两种布局方式以及瀑布流布局.相关切换方式网上有很多,大家自行搜索,我就不贴连

  • Android中RecyclerView拖拽、侧删功能的实现代码

    废话不多说,下面展示一下效果. 这是GridView主文件实现. public class GridViewActivity extends AppCompatActivity { RecyclerView mRecyclerView; List<String> mStringList; RecyclerAdapter mRecyAdapter; @Override protected void onCreate(@Nullable Bundle savedInstanceState) { s

  • 如何给Android中的按钮添加图片功能

    在layout中建一个my_login.xml文件 代码如下 <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:Android="http://schemas.android.com/apk/res/android" android:layout_width="fill_parent" android:layout_height="

  • Android 中RecyclerView多种item布局的写法(头布局+脚布局)

    RecyclerView多个item布局的写法(头布局+脚布局) 上图 github 下载源码 Initial commit第一次提交的代码,为本文内容 以下的为主要代码,看注释即可,比较简单 MainActivity 含上拉加载更多 package com.anew.recyclerviewall; import android.os.Bundle; import android.os.Handler; import android.support.v7.app.AppCompatActivi

  • Android中RecyclerView点击Item设置事件

    在上一篇Android RecylerView入门教程中提到,RecyclerView不再负责Item视图的布局及显示,所以RecyclerView也没有为Item开放OnItemClick等点击事件,这就需要开发者自己实现.博客最下面有Demo程序运行动画. 奉上Demo的Github链接. 在调研过程中,发现有同学修改RecyclerView源码来实现Item的点击监听,但认为这不是一个优雅的解决方案,最终决定在RecyclerView.ViewHolder上做文章. 思路是:因为ViewH

随机推荐