android:gradientRadiusの問題。。
Androidのグラデーションはxmlで記述できて便利です。
typeを指定して線形(linear)、放射線状(radial)、走査(sweep)などのグラデーションを書くことができます。
しかし放射線状のグラデーションで指定できるgradientRadiusは、pxでしか指定できないようです。。
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.
ドキュメントには%指定等もできる、みたいに書いてありますが、実際に指定しても期待通りに動きません。。
<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').
こんなエラーでビルドが通りません。
%指定
%指定はコンパイルも通りますが、
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>
標準解像度(だいぶ昔)
<resources ...>
...
<item name="radius" format="float" type="dimen">326</item>
....
</resources>
昔の高解像度(QHD?)
<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で代入するのがいいと思います。
// 背景に指定してるDrawableを取得して編集。。
((GradientDrawable) someView.getBackground())
.setGradientRadius(getResources().getDimension(
R.dimen.yellowbadge_largeradius));
viewから背景を取得すれば、普通にdimensionを代入できます。
汎用的にたくさん使うなら普通にカスタムビューを作ればいいですね。。
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
- Radial Gradient for different dpi
- Issue 71065: GradientDrawable: Radial gradient gradientRadius not working as documented
ロリポップだと治っているのかもしれませんが、旧機種対応は必要そうですね。。