Radial GradientDrawable兼容问题

Radial GradientDrawable兼容问题

在android中可以使用<shape/>标签方便实现图形Drawable,<shape/>标签对应的class类是GradientDrawable。如果要实现一个圆形的阴影效果,可以使用下面的xml代码:

<!-- radial shadow drawable -->
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
       android:shape="oval">
    <gradient
        android:centerX="0.5"
        android:centerY="0.5"
        android:endColor="@android:color/transparent"
        android:gradientRadius="50"
        android:startColor="#2b2b2b"
        android:type="radial"/>
</shape>

<gradient/>标签实现了渐变效果,android:type="radial"表示圆形类型,另外还有linear,sweeplinear表示线性渐变,sweep则是类似雷达扫描的渐变。这里只探讨radial类型的情况。

android:gradientRadius是渐变半径值,必须设置该值才能实现圆形渐变效果。而不同SDK API版本下从xml解析该属性值的实现不同,也导致了本文所说的兼容性问题

Drawable Xml资源文件解析过程一文中,xml的解析最后会在drawable.inflate()中完成。下面是GradientDrawable.inflate()方法的实现,这里只关注对android:gradientRadius的解析。

// SDK 19 KITKAT 4.4
// xml解析
TypedValue tv = a.peekValue(com.android.internal.R.styleable.GradientDrawableGradient_gradientRadius);
if (tv != null) {
  boolean radiusRel = tv.type == TypedValue.TYPE_FRACTION;
  st.mGradientRadius = radiusRel ? 
          tv.getFraction(1.0f, 1.0f) : tv.getFloat();
} else if (gradientType == RADIAL_GRADIENT) {
  throw new XmlPullParserException(a.getPositionDescription() 
      + "<gradient> tag requires 'gradientRadius' " 
      + "attribute with radial type");
}
// 绘制
mFillPaint.setShader(new RadialGradient(x0, y0,
                            level * st.mGradientRadius, colors, null,
                            Shader.TileMode.CLAMP));
/*
可以看到,如果type设置为radial,但没有设置gradientRadius会抛出异常;
而gradientRadius可以使用Fraction、Float格式,并且直接以px单位使用了该值。
虽然可以使用Fraction,但并不支持*%,*%p格式。
*/
// SDK 21 LOLLIPOP 5.0
// xml解析
final TypedValue tv = a.peekValue(R.styleable.GradientDrawableGradient_gradientRadius);
if (tv != null) {
    final float radius;
    final int radiusType;
    if (tv.type == TypedValue.TYPE_FRACTION) {
        radius = tv.getFraction(1.0f, 1.0f);
        final int unit = (tv.data >> TypedValue.COMPLEX_UNIT_SHIFT)
                            & TypedValue.COMPLEX_UNIT_MASK;
        if (unit == TypedValue.COMPLEX_UNIT_FRACTION_PARENT) {
            radiusType = RADIUS_TYPE_FRACTION_PARENT;
        } else {
            radiusType = RADIUS_TYPE_FRACTION;
        }
    } else {
        radius = tv.getDimension(r.getDisplayMetrics());
        radiusType = RADIUS_TYPE_PIXELS;
    }

    st.mGradientRadius = radius;
    st.mGradientRadiusType = radiusType;
} else if (st.mGradient == RADIAL_GRADIENT) {
        throw new XmlPullParserException(
                        a.getPositionDescription()
                        + "<gradient> tag requires 'gradientRadius' "
                        + "attribute with radial type");
}
// 绘制
float radius = st.mGradientRadius;
if (st.mGradientRadiusType == RADIUS_TYPE_FRACTION) {
        radius *= Math.min(st.mWidth, st.mHeight);
} else if (st.mGradientRadiusType == RADIUS_TYPE_FRACTION_PARENT) {
        radius *= Math.min(r.width(), r.height());
}
if (st.mUseLevel) {
        radius *= getLevel() / 10000.0f;
}
mGradientRadius = radius;
/*
5.0中gradientRadius可以使用Fraction、Dimension格式,并且真正支持了Fraction百分比格式(*%, *%p)。
但却不能使用Float格式,Float格式将被解析为Dimension,导致效果不符合预期,5.1中解决了这个问题。
*/
// SDK 22 LOLLIPOP_MR1 5.1
// xml解析
final TypedValue tv = a.peekValue(R.styleable.GradientDrawableGradient_gradientRadius);
if (tv != null) {
        final float radius;
        final int radiusType;
        if (tv.type == TypedValue.TYPE_FRACTION) {
                radius = tv.getFraction(1.0f, 1.0f);
                final int unit = (tv.data >> TypedValue.COMPLEX_UNIT_SHIFT)
                            & TypedValue.COMPLEX_UNIT_MASK;
                if (unit == TypedValue.COMPLEX_UNIT_FRACTION_PARENT) {
                        radiusType = RADIUS_TYPE_FRACTION_PARENT;
                } else {
                        radiusType = RADIUS_TYPE_FRACTION;
                }
        } else if (tv.type == TypedValue.TYPE_DIMENSION) {
                radius = tv.getDimension(r.getDisplayMetrics());
                radiusType = RADIUS_TYPE_PIXELS;
        } else {
                radius = tv.getFloat();
                radiusType = RADIUS_TYPE_PIXELS;
        }
        st.mGradientRadius = radius;
        st.mGradientRadiusType = radiusType;
} else if (st.mGradient == RADIAL_GRADIENT) {
        throw new XmlPullParserException(
                        a.getPositionDescription()
                        + "<gradient> tag requires 'gradientRadius' "
                        + "attribute with radial type");
}
// 绘制
float radius = st.mGradientRadius;
if (st.mGradientRadiusType == RADIUS_TYPE_FRACTION) {
        // Fall back to parent width or height if intrinsic
        // size is not specified.
        final float width = st.mWidth >= 0 ? st.mWidth : r.width();
        final float height = st.mHeight >= 0 ? st.mHeight : r.height();
        radius *= Math.min(width, height);
} else if (st.mGradientRadiusType == RADIUS_TYPE_FRACTION_PARENT) {
        radius *= Math.min(r.width(), r.height());
}

/*
5.1解决了5.0的问题,可以支持Float格式数据,并且优化了‘*%’格式下自身size未指定时的情况。
*/

总结:
设置android:gradientRadius属性值时:
Api 21以下:只能使用Float格式数据。%、%p、dimension格式没有预期效果;
Api 21时:只能使用Fraction(%,%p)、Dimension格式数据。不能使用Float数据,否则Float会被解析为Dimension,显示错误。drawable自身size未指定时,使用%格式不会显示;
Api 22及以上:可以正常使用Float、Dimension、%、%p格式。

(float值使用时单位为px)

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

推荐阅读更多精彩内容