Android聊天面板和横向观众列表透明度渐变的实现

问题重述

  • 这次的题目有点长,特意的将两个类似的东西进行了划分。也是为了完全重现我在遇到和解决这两个问题时候的过程。在一开始,是需求那边要我做一个我们聊天面板的渐变效果。经过多方查证,终于实现了这个功能。后来在迭代版本的时候又要加一个观众列表的右侧透明渐变效果,就想到了还用公屏同样的代码,然而中间竟然遇到了坑,还找不到攻略(好慌-。-),所以记录一下,防止后面遇到相同的问题没有资源参考。

  • 先上个效果图,静态滴


    两种效果显示
  • 你们要的 Git地址,拿去不谢!(●´∀`●)

聊天面板的实现

在开始我就是要实现一个聊天面板的消息渐变消失效果(如上图),经过查找资料,最后使用了下面的代码实现:

// 实现渐变效果
    Paint mPaint;
    private int layerId;
    private LinearGradient linearGradient;
    public void doTopGradualEffect(){
        mPaint = new Paint();
        // dst_in 模式,实现底层透明度随上层透明度进行同步显示
        //(即上层为透明时,下层就透明,并不是上层覆盖下层)
        // 具体关于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的参数这里传的null, 在传入 mPaint 时会出现
                // 第一次打开黑屏闪现的问题
                // 注意 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

观众列表右侧透明渐变

这里我要强调 右侧,因为我在考虑这个功能时马上想到了使用上面类似的代码,然而在左侧是OK的,可怎么也移动不到右侧。开始走了一条不归路。因为左侧是OK的,所以我想只需要把透明位置移动到右侧就可以了,就想修改 drawRect 的左右边界线来实现移动,但是都说了是不归路(╥╯^╰╥),期间进行了各种修改调试,最后甚至尝试了盖一张图片,添加一个毛玻璃蒙层,但效果都不好。在休息了一晚后,我又回到上次的记录点(左侧OK),终于让我发现了问题所在,原来控制透明度的位置不是通过控制矩形的位置,而是通过 linearGradient 的位置来实现的。后面又出现一些小问题,最终出世了以下代码:

// 实现渐变效果
    Paint mPaint;
    private int layerId;
    private LinearGradient linearGradient;
    private int preWidth = 0;// Recyclerview宽度动态变化时,监听每一次的宽度
    public void doTopGradualEffect(){
        mPaint = new Paint();
        // dst_in 模式,实现底层透明度随上层透明度进行同步显示(即上层为透明时,下层就透明,并不是上层覆盖下层)
        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宽度发生改变时,
                // 重新计算透明位置
                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的参数这里传的null, 在传入 mPaint 时会出现第一次打开黑屏闪现的问题
                // 注意 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);
            }
        });
    }

苦恼了一天的问题原来就在于一个参数的修改 (〜^㉨^)〜,终于顺利完成了这个功能,后来由于我们的需求是少于3个人右对齐,3个人以上左对齐,所以添加了动态计算的代码。

  • Tip:我示例的代码都是在我自定义的Recyclerview中,所以是直接调用的addItemDecoration,还有记得 saveLayer 时如果传了 Paint 的话可能出现界面打开的时候闪一个黑屏。

转载请联系作者--维权骑士,盗版必究

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