ShapeDrawableの放射線状のグラデーションの半径指定について


## android:gradientRadiusの問題。。

Androidのグラデーションはxmlで記述できて便利です。
typeを指定して線形(linear)、放射線状(radial)、走査(sweep)などのグラデーションを書くことができます。

しかし放射線状のグラデーションで指定できるgradientRadiusは、pxでしか指定できないようです。。

[android:gradientRadius](http://developer.android.com/reference/android/graphics/drawable/GradientDrawable.html#attr_android:gradientRadius)
> Radius of the gradient, used only with radial gradient.
> 
> May be a floating point value, such as "1.2".
> 
> May be a fractional value, which is a floating point number appended with either % or %p, such as "14.5%". The % suffix always means a percentage of the base size; the optional %p suffix provides a size relative to some parent container.
> 
> This may also be a reference to a resource (in the form "@[package:]type:name") or theme attribute (in the form "?[package:][type:]name") containing a value of this type.
> 
> This corresponds to the global attribute resource symbol gradientRadius. 

ドキュメントには%指定等もできる、みたいに書いてありますが、実際に指定しても期待通りに動きません。。


```xml
<shape xmlns:android="http://schemas.android.com/apk/res/android">
    <gradient
        android:centerX="0.4"
        android:centerY="0.6"
        android:endColor="#090e1c"
        android:gradientRadius="800"
        android:startColor="#314036"
        android:type="radial"
        />
        <!--
        解像度や使い回しのためにこんな風に書きたい！
        android:gradientRadius="800dp"
        android:gradientRadius="80%"
        -->
</shape>
```


### dp指定
dp指定はAPI21のPreviewだと動いたように見えますが

```
Error:(9, 45) Dimension types not allowed (at 'gradientRadius' with value '5dp').
```
こんなエラーでビルドが通りません。


### %指定

%指定はコンパイルも通りますが、

* [Gradient Radius as percentage of screen size](http://stackoverflow.com/questions/11176590/gradient-radius-as-percentage-of-screen-size/17861728#17861728)

> From what I´ve tested, the % does work, but not as you expected.
First of all
> 
>```
android:gradientRadius="50"
```
> 
seems to take the value as pixels 50px
>
> ```
android:gradientRadius="50%"
```
> 
is converted as if 50% = 0.5 px, try
>
> ```
android:gradientRadius="5000%"
```
> 
and you will see a 50px radius.

のようにただの「100で割る」(?)というような意味しか示してないようです。。

こんな感じでdp指定も%指定も期待通りには動きません。

## 解決策

ロリポップだと治っているという情報もありそうなんですが、古い端末の解決策はまだなさそうです。

こちらで一応対策案が書かれていたのでメモしておきます。
* [Radial Gradient for different dpi](http://stackoverflow.com/questions/8501599/radial-gradient-for-different-dpi)

### 解像度ごとにxmlで定義する

解像度ごとに決めうちなら、px指定のリソースを出し分けますw

```xml:res/drawable/foo.xml
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
                                android:shape="rectangle">
 <gradient
      android:startColor="#7faacaFF"
      android:endColor="#cfe1edFF"
      android:gradientRadius="@dimen/radius"
      android:type="radial"/>
 </shape>
```

標準解像度(だいぶ昔)

```xml:res/values-mdpi/dimens.xml
<resources ...>
...
<item name="radius" format="float" type="dimen">326</item>
....
</resources>
```

昔の高解像度(QHD？)

```xml:res/values-hdpi/dimens.xml
<resources ...>
...
<item name="radius" format="float" type="dimen">200.34</item>
....
</resources>
```

HDならxhdpi、FHDならxxhdpi、2Kだとxxxhdpiも指定しておく必要がありそうですね。
dimenタグだとformatがreferenceになってしまったりしてビルドエラーが起きるので、typeとformatを指定してるみたいです。
ちょっと面倒ですね。。


### Javaで出し分ける

xmlを無理に使う必要がなかったり、比率を指定したかったり使い回す場合はjavaで代入するのがいいと思います。

```java
// 背景に指定してるDrawableを取得して編集。。
((GradientDrawable) someView.getBackground())
        .setGradientRadius(getResources().getDimension(
            R.dimen.yellowbadge_largeradius));
```
viewから背景を取得すれば、普通にdimensionを代入できます。

汎用的にたくさん使うなら普通にカスタムビューを作ればいいですね。。

```java
Paint mPaint;
RadialGradient mGradient;

private void init(){
    // PaintとかはonDrawで確保しないほうがいいですね。。Gradientも？
    mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
}

@Override
protected void onDraw(Canvas canvas) {
    int maxSize = Math.max(getHeight(), getWidth());
    
    // RadialGradientを作って書く。
    // コンストラクタでattrを見るようにすればxmlで色々できますね。
    mGradient = new RadialGradient(
            getWidth()/2,
            getHeight()/2,
            maxSize, 
            new int[] {Color.RED, Color.BLACK},
            new float[] {0, 1}, 
            android.graphics.Shader.TileMode.CLAMP);
    
    // PaintにGradientをセット
    mPaint.setDither(true);
    mPaint.setShader(mGradient);

    // View全体に描画, paddingも考慮すべきですね。。。
    canvas.drawRect(0, 0, getWidth(), getHeight(), mPaint);
}
```


## 参考

* [Gradient Radius as percentage of screen size](http://stackoverflow.com/questions/11176590/gradient-radius-as-percentage-of-screen-size)
* [Radial Gradient for different dpi](http://stackoverflow.com/questions/8501599/radial-gradient-for-different-dpi)
* [Issue 71065: 	GradientDrawable: Radial gradient gradientRadius not working as documented](https://code.google.com/p/android/issues/detail?id=71065)

ロリポップだと治っているのかもしれませんが、旧機種対応は必要そうですね。。

* [Issue 77862: 	Radial gradient is broken in Android 5 Lollipop api21](https://code.google.com/p/android/issues/detail?id=77862)