recyclerView底部添加透明漸變

最近實現(xiàn)一個recyclerView透明漸變的效果,遇到了一些坑,嘗試了一些方法,這里記錄一下。

效果圖

image.png

image.png

圖片在上面顯示2列,文字在下面顯示1列;底部要有個透明漸變的效果,直到完全看不到。

gridLayoutManager動態(tài)設(shè)置列數(shù)

大概是分兩類,一類以圖片為item 一行2個,一類以文字為item 一行一個。
這個第一反應(yīng)是用viewType去區(qū)分圖片類型,但是由于起初不知道gridLayout可以動態(tài)列數(shù)。就在上面兩列,下面一列上為難起來了。
如果統(tǒng)一用一列吧,那就把兩個image當成一個item,但是這樣要自己去計算position,而且還要考慮奇數(shù)情況下最后一行只有一個圖片的情況,這樣要多寫一些代碼和邏輯。
后面發(fā)現(xiàn)可以通過改變spanSize來根據(jù)viewType來改變spanCount

     //gridLayoutManager這里設(shè)置的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", "列數(shù)2");
                return 1;
              case TYPE_TEXT:
                //占用2/2 表示1列
                Log.d("tag", "列數(shù)1");
                return 2;
              default:
                Log.d("tag", "default列數(shù)2");
                return 2;
            }
          }
        });
      }

這樣item的layout就很干凈了,省去了一些麻煩。

recyclerView添加透明漸變

本來看著快搞完了,只差一個底部透明漸變了,結(jié)果就這一個效果高出了好多事情。

嘗試了下面三種方法

  • recycleView上面蓋一層漸變的layout
  • 原生的fadingEdeg,嘗試通過反射去掉頂部陰影
  • 圖層合并
  • 第一反應(yīng)是再在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的時候,是從開始到結(jié)束的漸變,沒有g(shù)randient的平鋪的效果,也就是只有最后一點是不透明的,不能實現(xiàn)最后是全不透明的。而且這個00FFFFFF其實不是真正意義上的不透明,而是白色的。

效果圖底下有一截是全透明的,這個和效果圖是有一定差距的,單純用shape實現(xiàn)不了。嘗試再在漸變下面添加一截全透明,就會慘不忍睹。


image.png
  • recyclerView的fadingEdge

組里的老司機提了可以用這個recyclerView原生自帶的屬性。

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

運行起來看起來雖然和ui有點差距,但是好像可以接受了。
但是滑動的時候就出現(xiàn)問題了,這個自帶fadingEdge不僅底部有,頂部也有,而且更坑的是,recyclerView沒有提供取消頂部或者底部的接口。要么都不用,要么都要。


image.png

參考網(wǎng)上的一種方法,通過反射去設(shè)置頂部的edge寬高,從而使得頂部陰影不顯示。
如何屏蔽RecyclerView單邊滑動到頭陰影(fadingEdge)
本來以為這樣就ok了,誰知道這樣設(shè)置竟然沒有效果。

參考給的代碼是這樣設(shè)置的:


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) {
         //設(shè)置TOP頂部的矩形陰影大?。醋⑨尯痛a大概是這個作用)
          mTopGlow.setSize(0, 0);
          mTopGlow.finish();
        }
      }
    });

這樣大部分情況topGlow也是空的。查看代碼,發(fā)現(xiàn)貌似只有這個方法才會給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();
    }
  }

好了,經(jīng)過上面的操作,mtopFlow取出來并且設(shè)置size(0,0)了,講道理頂部陰影只有一個長0,寬0的矩形,就相當于沒有了。
然而。。。
頂部陰影還是存在,而且去調(diào)試系統(tǒng)創(chuàng)建上下陰影的場景,也發(fā)現(xiàn)沒有調(diào)用setSize就把陰影畫出來了。之后又折騰了一陣子也沒看出來這個陰影在recyclerView里面怎么是怎么畫出來的,有知道的大兄弟麻煩告知一下。

  • 通過圖層合并添加透明效果

參考Android實現(xiàn)直播聊天區(qū)域頂部漸變效果

 /**
     * 利用itemDecoration的onDraw和onDrawOver回調(diào)時機,進行兩個圖層的合并
     *
     */
    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);
        /**
         * 第一次進去的時候發(fā)現(xiàn)畫出來的東西很奇怪 暫未找到原因
         * 需要滑動之后才會正常顯示,所以第一次進去的時候,手動調(diào)用一次刷新
         */

        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()參數(shù)坐標以及l(fā)inearGradient的使用不太熟悉,搞出來一些事情。

關(guān)于LinearGrandient的參數(shù)

image.png

前面的xy用來標明方向的。如果是Y垂直方向的漸變,只需要(0,startY)到(0,endY),X就沒啥作用,保證在Y上是垂直就行。但是這個startY和endY之前我就理解成絕對坐標了,以為手機左上角是坐標原點(0,0);
后面那個postion其實是對應(yīng)各種color的位置吧,配合CLAMP模式(超出起點和終點外的默認是起點和終點的顏色向外延伸)還是挺好用的。
比起第一種直接用XML shape LienarGradient的startColor endColor要靈活很多。
然而view里面沒有像motionEvent那樣直接getRowX的方法,就通過百分比計算來找位置,又增加了復(fù)雜度。
實際情況比這些影響因素(同時還在調(diào)fadingEdge)要多,有點自找麻煩的感覺,一時陷入僵局。

倒著回去想,既然Google沒有提供獲取絕對左邊的方法,而且linearGradient也是設(shè)給paint給canvas用,那里面的坐標應(yīng)該是和cavas是一致的。而canvas在很多地方如view的draw里面也能拿到,那明顯設(shè)置成相當于canvas的坐標要方便一些。
重新清醒一下頭腦,把暫時該注釋掉的注釋掉,通過最小粒度的改變來調(diào)試效果(不然一下子改變很多,出來的效果會讓人懵逼),最后總算是搞出來了。

然而事情并沒有結(jié)束。。。。。
估計是我在dialog里面顯示recyclerView的關(guān)系,通過圖層合并后,會形成很奇怪的效果,必須要滑動一下才可以正常顯示。如果recyclerView不是裝在dialog里面就不會這樣,只是單存的使用就不會這樣。

image.png

很奇怪,不過這樣可以看到SRC和DES圖層 ==
暫時處理是第一次進來在drawOver完成后再postInvalidate一次,有知道原因的老哥幫忙提點一下!

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

最終效果


image.png

遺留的問題

  • recyclerView fadingEdge的實現(xiàn),不是flow.setSize,在調(diào)試代碼時沒看出來(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的地方只有那里,然后后面會根據(jù)這個值去setScale,y傳0就不會顯示了.

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 內(nèi)容 抽屜菜單 ListView WebView SwitchButton 按鈕 點贊按鈕 進度條 TabLayo...
    小狼W閱讀 1,666評論 0 10
  • 1 CALayer IOS SDK詳解之CALayer(一) http://doc.okbase.net/Hell...
    Kevin_Junbaozi閱讀 5,331評論 3 23
  • 最近做了一個Android UI相關(guān)開源項目庫匯總,里面集合了OpenDigg 上的優(yōu)質(zhì)的Android開源項目庫...
    OpenDigg閱讀 17,589評論 6 222
  • 一天,我在家里單獨一人看電視,不知不覺,我進入了夢鄉(xiāng)。 我眼前出現(xiàn)了一臺時光機,我來到了一個奇妙...
    你下午有了閱讀 535評論 0 0
  • 昨天老婆說,陪我說會兒話吧,這不經(jīng)意的一句話勾起了我的回憶,轉(zhuǎn)眼間結(jié)婚已經(jīng)一年,畢業(yè)也已經(jīng)五年. ...
    幽狼圈圈閱讀 386評論 1 3

友情鏈接更多精彩內(nèi)容