RippleDrawable 触摸反馈 ---- java 代码编写

RippleDrawable 触摸反馈 ---- java 代码编写

默认效果(有界 & 无界)

  1. 有界: ?Android:attr/selectableItemBackground
  2. 无界: ?android:attr/selectableItemBackgroundBorderless

用 xml 自己编写一个无界波纹

<?xml version="1.0" encoding="utf-8"?>
<ripple xmlns:android="http://schemas.android.com/apk/res/android"
    android:color="@color/md_gray_500">
</ripple>

用 xml 自己编写一个有界波纹

<?xml version="1.0" encoding="utf-8"?>
<ripple xmlns:android="http://schemas.android.com/apk/res/android"
        android:color="@color/gray_cc">
    <item android:drawable="@color/white"/>
</ripple>

代码编写 RippleDrawable

1. 首先看 RippleDrawable 类的构造函数

//xml 中编写的话会执行此无参构造函数
RippleDrawable() {
        this(new RippleState(null, null, null), null);
    }
//代码编写使用有参构造函数
public RippleDrawable(@NonNull ColorStateList color, @Nullable Drawable content,
            @Nullable Drawable mask) {
        this(new RippleState(null, null, null), null);
        //color: 波纹的颜色,必须要有,没有直接抛异常,程序崩溃
        if (color == null) {
            throw new IllegalArgumentException("RippleDrawable requires a non-null color");
        }
        //content: 背景,当背景为null时,波纹为无界.不为null时为有界.
        if (content != null) {
            addLayer(content, null, 0, 0, 0, 0, 0);
        }
        //mask: 按照说明,mask是不会被draw的,但是它会限制波纹的边界,如果为null,默认为content的边界,同上,当content为null就没有边界了.
        if (mask != null) {
            addLayer(mask, null, android.R.id.mask, 0, 0, 0, 0);
        }
        //省略无关代码...
    }

2. 按照构造参数,提供需要的参数

  • ColorStateList color
  • Drawable content
  • Drawable mask

我写了个简单的:

RippleDrawable drawable=new RippleDrawable(
                ColorStateList.valueOf(Color.GRAY),//灰色波纹
                getResources().getDrawable(R.drawable.shape_cricle_solid_whote_stoke_gray),//一个圆角5dp,白色填充,灰色边框的shape图形,在xml中定义的
                getShape()//编写了一个方法来获取mask
        );

用代码编写shape在下文会提到.
上面用到的shape xml:

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
       android:shape="rectangle">
    <corners android:radius="5dp"/>
    <solid android:color="@color/white"/>
    <stroke
        android:width="1px"
        android:color="@color/gray_cc"/>
</shape>

这里我们的重点放在 mask 的形状上,因为上面说过,mask 会限制波纹扩散的边界,一般情况我们是不需要额外定义的,我们都是拿一个View的背景来作为波纹边界----就是RippleDrawable第二个参数 Drawable content.我们试着去定义,看看实际的限制是如何的.

自定义波纹边界

我的getshape()方法写了一个见到的shape:

    private Drawable getShape(){
        ShapeDrawable mask =new ShapeDrawable(new RectShape(){
            @Override
            public void draw(Canvas canvas, Paint paint) {
                final float width = this.getWidth();
                final float height = this.getHeight();
                //我想在这个矩形的区域(这里是800px*800px正方形)里画一个 六芒星 ~{=.=}
                //六芒星是上下两个三角形叠加的,所以我画两个三角形就行了.

                //if width == height (为了显示好看,我们设置长宽一样,这里我就认为长宽一样了)
                final float r = width /2 ;
                PointF center = new PointF(width/2,height/2);
                float offsetHeight = (float) (Math.sin(Math.toRadians(30)) * r);
                float offsetWidth = (float) (Math.cos(Math.toRadians(30)) * r);

                //画正三角形
                Path up = new Path();
                up.moveTo(center.x - offsetWidth,center.y + offsetHeight);
                up.lineTo(center.x - offsetWidth,center.y + offsetHeight);
                up.lineTo(center.x,0);
                up.lineTo(center.x + offsetWidth,center.y + offsetHeight);
                up.close();
                canvas.drawPath(up,paint);

                //画倒三角形
                Path down =new Path();
                down.moveTo(center.x - offsetWidth,center.y - offsetHeight);
                down.lineTo(center.x - offsetWidth,center.y - offsetHeight);
                down.lineTo(center.x,center.y * 2);
                down.lineTo(center.x + offsetWidth ,center.y - offsetHeight);
                down.close();
                canvas.drawPath(down,paint);
            }
        });
        return mask;
    }

方法比较简单,就是连线,然后画出来.
最后的效果是这样的:


RippleDrawable mask

当然你有想法可以自己去实现,但是基本的就是这样了.嘿嘿

代码编写 Shape 图形

首先说一个误区,可能有人认为 Shape 图形对应的是 ShapeDrawable ,实际上不是的.这里搬一段源码比较好说明.

DrawableInflater 类有一个方法 inflateFromTag(String name):

    private Drawable inflateFromTag(@NonNull String name) {
        switch (name) {
            case "selector":
                return new StateListDrawable();
            case "animated-selector":
                return new AnimatedStateListDrawable();
            case "level-list":
                return new LevelListDrawable();
            case "layer-list":
                return new LayerDrawable();
            case "transition":
                return new TransitionDrawable();
            case "ripple":
                return new RippleDrawable();
            case "color":
                return new ColorDrawable();
            case "shape"://xml 中<shape>标签对应的是 GradientDrawable
                return new GradientDrawable();
            case "vector":
                return new VectorDrawable();
            case "animated-vector":
                return new AnimatedVectorDrawable();
            case "scale":
                return new ScaleDrawable();
            case "clip":
                return new ClipDrawable();
            case "rotate":
                return new RotateDrawable();
            case "animated-rotate":
                return new AnimatedRotateDrawable();
            case "animation-list":
                return new AnimationDrawable();
            case "inset":
                return new InsetDrawable();
            case "bitmap":
                return new BitmapDrawable();
            case "nine-patch":
                return new NinePatchDrawable();
            default:
                return null;
        }
    }

所以对代码编写Shape图形:

GradientDrawable gradientDrawable=new GradientDrawable();
        int dp50 = DisplayUtil.dp2px(this, 50);
        gradientDrawable.setColor(Color.RED);
        gradientDrawable.setCornerRadius(dp50);
        gradientDrawable.setStroke(dp50/5,Color.BLACK);

这样就定义了一个填充色是红色,圆角是50dp,边线是10dp 黑色的Shape图形了.

关于 shape 和 RippleDrawable 再说两句

关于波纹的两层颜色:底层浅一些,上面一层暗一些,源码里是这样写的:

RippleDrawabledraw()方法里有用来花 background 与 ripple的方法 drawBackgroundAndRipples(canvas);

    private void drawBackgroundAndRipples(Canvas canvas) {
        final RippleForeground active = mRipple;
        final RippleBackground background = mBackground;
        
        //省略无关代码...
        //获取设置的颜色,默认是黑色
        final int color = mState.mColor.getColorForState(getState(), Color.BLACK);
        //获取设置颜色的一半透明度
        final int halfAlpha = (Color.alpha(color) / 2) << 24;
        
        //
        final Paint p = getRipplePaint();
        if (mMaskColorFilter != null) {
            //波动时间取决于颜料的alpha值,因此我们需要将alpha通道推入颜色,并让滤镜处理全alpha颜色。
            final int fullAlphaColor = color | (0xFF << 24);
            mMaskColorFilter.setColor(fullAlphaColor);

            p.setColor(halfAlpha);
            p.setColorFilter(mMaskColorFilter);
            p.setShader(mMaskShader);
        } else {
            final int halfAlphaColor = (color & 0xFFFFFF) | halfAlpha;
            p.setColor(halfAlphaColor);
            p.setColorFilter(null);
            p.setShader(null);
        }
        //省略无关代码...
    }

总结: 也就是这两层,实际上是 Ripple Color 和一半透明度的 Ripple Color;

第二点:
当你拿 Shape 图形作为 RippleDrawable的mask 时,注意,mask 的有效区域 是要被 Draw的,虽然最终是不会画在你所看到的View上,但是你定义的 Shape 被draw 的部分才能作为 波纹 的边界条件,否则限制无效

我就拿上面编写的shape图形说明:

GradientDrawable gradientDrawable=new GradientDrawable();
        int dp50 = DisplayUtil.dp2px(this, 50);
        //gradientDrawable.setColor(Color.RED);
        gradientDrawable.setCornerRadius(dp50);
        gradientDrawable.setStroke(dp50/5,Color.BLACK);

我注释掉设置填充色的这句,然后看看效果:

只有边框的情况

当我取消注释:

有边框和填充色的情况

有什么不对的欢迎指正,喜欢点个赞呗~~~~

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

推荐阅读更多精彩内容