美文网首页
Radial GradientDrawable兼容问题

Radial GradientDrawable兼容问题

作者: wyonxue | 来源:发表于2018-01-09 21:08 被阅读0次

    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)

    相关文章

      网友评论

          本文标题:Radial GradientDrawable兼容问题

          本文链接:https://www.haomeiwen.com/subject/vddgnxtx.html