Android聊天面板和橫向觀眾列表透明度漸變的實現(xiàn)

問題重述

  • 這次的題目有點長,特意的將兩個類似的東西進行了劃分。也是為了完全重現(xiàn)我在遇到和解決這兩個問題時候的過程洒忧。在一開始运授,是需求那邊要我做一個我們聊天面板的漸變效果烤惊。經(jīng)過多方查證,終于實現(xiàn)了這個功能吁朦。后來在迭代版本的時候又要加一個觀眾列表的右側(cè)透明漸變效果柒室,就想到了還用公屏同樣的代碼,然而中間竟然遇到了坑逗宜,還找不到攻略(好慌-雄右。-),所以記錄一下纺讲,防止后面遇到相同的問題沒有資源參考擂仍。

  • 先上個效果圖,靜態(tài)滴


    兩種效果顯示
  • 你們要的 Git地址熬甚,拿去不謝逢渔!(●′?`●)

聊天面板的實現(xiàn)

在開始我就是要實現(xiàn)一個聊天面板的消息漸變消失效果(如上圖),經(jīng)過查找資料则涯,最后使用了下面的代碼實現(xiàn):

// 實現(xiàn)漸變效果
    Paint mPaint;
    private int layerId;
    private LinearGradient linearGradient;
    public void doTopGradualEffect(){
        mPaint = new Paint();
        // dst_in 模式复局,實現(xiàn)底層透明度隨上層透明度進行同步顯示
        //(即上層為透明時,下層就透明粟判,并不是上層覆蓋下層)
        // 具體關(guān)于PorterDuff.Mode的東西大家可以自行查閱了解
        final Xfermode xfermode = new PorterDuffXfermode(PorterDuff.Mode.DST_IN);
        mPaint.setXfermode(xfermode);
        // 透明位置不變亿昏,位于Recyclerview偏上位置
        // 使用 CLAMP 模式邊緣拉伸,完美契合背景顏色
        linearGradient = new LinearGradient(0.0f, 0.0f, 0.0f, 100.0f, new int[]{0, Color.BLACK}, null, Shader.TileMode.CLAMP);

        addItemDecoration(new RecyclerView.ItemDecoration() {
            @Override
            public void onDrawOver(Canvas canvas, RecyclerView parent, RecyclerView.State state) {
                super.onDrawOver(canvas, parent, state);

                mPaint.setXfermode(xfermode);
                mPaint.setShader(linearGradient);
                canvas.drawRect(0.0f, 0.0f, parent.getRight(), 200.0f, mPaint);
                mPaint.setXfermode(null);
                canvas.restoreToCount(layerId);
            }

            @Override
            public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) {
                super.onDraw(c, parent, state);
                // 此處 Paint的參數(shù)這里傳的null档礁, 在傳入 mPaint 時會出現(xiàn)
                // 第一次打開黑屏閃現(xiàn)的問題
                // 注意 saveLayer 不能省也不能移動到onDrawOver方法里
                layerId = c.saveLayer(0.0f, 0.0f, (float) parent.getWidth(), (float) parent.getHeight(), null, Canvas.ALL_SAVE_FLAG);
            }

            @Override
            public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
                // 該方法作用自行百度
                super.getItemOffsets(outRect, view, parent, state);
            }
        });
    }

注釋還是比較清楚的角钩。主要使用了 RecyclerviewaddItemDecoration 和 PorterDuff.Mode 的 DST_IN 模式,這個模式下繪制的效果會受到源圖像(即代碼中的 drawRect)透明度的影響呻澜,因為我們使用了漸變的 Shader--LinearGradient递礼,所以畫出來的矩形透明度是漸變的,當我們 restore 的時候就會影響到底層的透明度(即公屏消息的透明度)羹幸。

Lucky

觀眾列表右側(cè)透明漸變

這里我要強調(diào) 右側(cè)脊髓,因為我在考慮這個功能時馬上想到了使用上面類似的代碼,然而在左側(cè)是OK的栅受,可怎么也移動不到右側(cè)将硝。開始走了一條不歸路恭朗。因為左側(cè)是OK的,所以我想只需要把透明位置移動到右側(cè)就可以了依疼,就想修改 drawRect 的左右邊界線來實現(xiàn)移動痰腮,但是都說了是不歸路(╥╯^╰╥),期間進行了各種修改調(diào)試律罢,最后甚至嘗試了蓋一張圖片膀值,添加一個毛玻璃蒙層,但效果都不好误辑。在休息了一晚后沧踏,我又回到上次的記錄點(左側(cè)OK),終于讓我發(fā)現(xiàn)了問題所在稀余,原來控制透明度的位置不是通過控制矩形的位置悦冀,而是通過 linearGradient 的位置來實現(xiàn)的。后面又出現(xiàn)一些小問題睛琳,最終出世了以下代碼:

// 實現(xiàn)漸變效果
    Paint mPaint;
    private int layerId;
    private LinearGradient linearGradient;
    private int preWidth = 0;// Recyclerview寬度動態(tài)變化時盒蟆,監(jiān)聽每一次的寬度
    public void doTopGradualEffect(){
        mPaint = new Paint();
        // dst_in 模式,實現(xiàn)底層透明度隨上層透明度進行同步顯示(即上層為透明時师骗,下層就透明历等,并不是上層覆蓋下層)
        final Xfermode xfermode = new PorterDuffXfermode(PorterDuff.Mode.DST_IN);
        mPaint.setXfermode(xfermode);

        addItemDecoration(new RecyclerView.ItemDecoration() {
            @Override
            public void onDrawOver(Canvas canvas, RecyclerView parent, RecyclerView.State state) {
                super.onDrawOver(canvas, parent, state);
                // 當linearGradient為空即第一次繪制 或 Recyclerview寬度發(fā)生改變時,
                // 重新計算透明位置
                if (linearGradient == null || preWidth!=parent.getWidth()){
                    // 透明位置從最后一個 itemView 的一半處到 Recyclerview 的最右邊
                    linearGradient = new LinearGradient(parent.getWidth()-(itemViewWidth/2), 
                              0.0f, parent.getWidth(), 0.0f, new int[]{Color.BLACK, 0}, null, Shader.TileMode.CLAMP);
                    preWidth = parent.getWidth();
                }

                mPaint.setXfermode(xfermode);
                mPaint.setShader(linearGradient);
                canvas.drawRect(0.0f, 0.0f, parent.getRight(), parent.getBottom(), mPaint);
                mPaint.setXfermode(null);
                canvas.restoreToCount(layerId);
            }

            @Override
            public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) {
                super.onDraw(c, parent, state);
                // 此處 Paint的參數(shù)這里傳的null辟癌, 在傳入 mPaint 時會出現(xiàn)第一次打開黑屏閃現(xiàn)的問題
                // 注意 saveLayer 不能省也不能移動到onDrawOver方法里
                layerId = c.saveLayer(0.0f, 0.0f, (float) parent.getWidth(), (float) parent.getHeight(), null, Canvas.ALL_SAVE_FLAG);
            }

            @Override
            public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
                // 該方法作用自行百度
                super.getItemOffsets(outRect, view, parent, state);
            }
        });
    }

苦惱了一天的問題原來就在于一個參數(shù)的修改 (?^?^)?寒屯,終于順利完成了這個功能,后來由于我們的需求是少于3個人右對齊黍少,3個人以上左對齊寡夹,所以添加了動態(tài)計算的代碼。

  • Tip:我示例的代碼都是在我自定義的Recyclerview中厂置,所以是直接調(diào)用的addItemDecoration菩掏,還有記得 saveLayer 時如果傳了 Paint 的話可能出現(xiàn)界面打開的時候閃一個黑屏。

轉(zhuǎn)載請聯(lián)系作者--維權(quán)騎士昵济,盜版必究

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末智绸,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子访忿,更是在濱河造成了極大的恐慌瞧栗,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,470評論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件海铆,死亡現(xiàn)場離奇詭異迹恐,居然都是意外死亡,警方通過查閱死者的電腦和手機卧斟,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,393評論 3 392
  • 文/潘曉璐 我一進店門殴边,熙熙樓的掌柜王于貴愁眉苦臉地迎上來通熄,“玉大人,你說我怎么就攤上這事找都。” “怎么了廊酣?”我有些...
    開封第一講書人閱讀 162,577評論 0 353
  • 文/不壞的土叔 我叫張陵能耻,是天一觀的道長。 經(jīng)常有香客問我亡驰,道長晓猛,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,176評論 1 292
  • 正文 為了忘掉前任凡辱,我火速辦了婚禮戒职,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘透乾。我一直安慰自己洪燥,他們只是感情好,可當我...
    茶點故事閱讀 67,189評論 6 388
  • 文/花漫 我一把揭開白布乳乌。 她就那樣靜靜地躺著捧韵,像睡著了一般。 火紅的嫁衣襯著肌膚如雪汉操。 梳的紋絲不亂的頭發(fā)上再来,一...
    開封第一講書人閱讀 51,155評論 1 299
  • 那天,我揣著相機與錄音磷瘤,去河邊找鬼芒篷。 笑死,一個胖子當著我的面吹牛采缚,可吹牛的內(nèi)容都是我干的针炉。 我是一名探鬼主播,決...
    沈念sama閱讀 40,041評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼仰担,長吁一口氣:“原來是場噩夢啊……” “哼糊识!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起摔蓝,我...
    開封第一講書人閱讀 38,903評論 0 274
  • 序言:老撾萬榮一對情侶失蹤赂苗,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后贮尉,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體拌滋,經(jīng)...
    沈念sama閱讀 45,319評論 1 310
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,539評論 2 332
  • 正文 我和宋清朗相戀三年猜谚,在試婚紗的時候發(fā)現(xiàn)自己被綠了败砂。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片赌渣。...
    茶點故事閱讀 39,703評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖昌犹,靈堂內(nèi)的尸體忽然破棺而出坚芜,到底是詐尸還是另有隱情,我是刑警寧澤斜姥,帶...
    沈念sama閱讀 35,417評論 5 343
  • 正文 年R本政府宣布鸿竖,位于F島的核電站,受9級特大地震影響铸敏,放射性物質(zhì)發(fā)生泄漏缚忧。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,013評論 3 325
  • 文/蒙蒙 一杈笔、第九天 我趴在偏房一處隱蔽的房頂上張望闪水。 院中可真熱鬧,春花似錦蒙具、人聲如沸球榆。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,664評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽芜果。三九已至,卻和暖如春融师,著一層夾襖步出監(jiān)牢的瞬間右钾,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,818評論 1 269
  • 我被黑心中介騙來泰國打工旱爆, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留舀射,地道東北人。 一個月前我還...
    沈念sama閱讀 47,711評論 2 368
  • 正文 我出身青樓怀伦,卻偏偏與公主長得像脆烟,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子房待,可洞房花燭夜當晚...
    茶點故事閱讀 44,601評論 2 353