Help us understand the problem. What is going on with this article?

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

More than 5 years have passed since last update.

android:gradientRadiusの問題。。

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

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

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.

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

<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

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

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

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>

標準解像度(だいぶ昔)

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

昔の高解像度(QHD?)

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で代入するのがいいと思います。

// 背景に指定してる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);
}

参考

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

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away