I'm trying to create a shape drawable with radial gradient background, with radius that will adjust to the screen size (take a look at the relevant documentation).
This is my code:
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle" >
<gradient
android:endColor="#000"
android:gradientRadius="50%p"
android:startColor="#5d2456"
android:type="radial" />
</shape>
But it doens't seem to work. if I remove the "%p", it works, but then the radius will be static, thus not adjusting to the screen size...Any idea what's wrong?
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.
Using %p
has a similar result. Obviously this is something I hope will be changed in the future, because it does not have much use as it is. Usually XML ShapeDrawable resources adapt their size to some external container, in this case gradientRadius
is setting the size regardless of the container.
I ended up creating a custom View with following overriden onDraw method:
@Override
protected void onDraw(Canvas canvas) {
Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
int maxSize = Math.max(getHeight(), getWidth());
RadialGradient gradient = new RadialGradient(
getWidth()/2,
getHeight()/2,
maxSize,
new int[] {Color.RED, Color.BLACK},
new float[] {0, 1},
android.graphics.Shader.TileMode.CLAMP);
paint.setDither(true);
paint.setShader(gradient);
canvas.drawRect(0, 0, getWidth(), getHeight(), paint);
}
In your layout just add the View:
<yourpackage.GradientView
android:layout_width="match_parent"
android:layout_height="match_parent" />
Of course it would be possible to create attributes for the View, eg. color, percentage to allow customization via XML.
Note that gradientRadius percentages do work in Lollipop. But if you have to support pre-Lollipop I expanded upon @marnaish's answer adding XML attributes. My gradientRadius is defined as a percentage of the parent view's width:
public class RadialGradientView extends View {
private final int endColor;
private final int startColor;
private final float gradientRadiusWidthPercent;
private final float centerY;
private final float centerX;
private Paint paint;
public RadialGradientView(Context context, AttributeSet attrs) {
super(context, attrs);
TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.RadialGradientView, 0, 0);
startColor = a.getColor(R.styleable.RadialGradientView_startColor, Color.RED);
endColor = a.getColor(R.styleable.RadialGradientView_endColor, Color.BLACK);
gradientRadiusWidthPercent = a.getFloat(R.styleable.RadialGradientView_gradientRadiusWidthPercent, 1);
centerX = a.getFloat(R.styleable.RadialGradientView_centerX, .5f);
centerY = a.getFloat(R.styleable.RadialGradientView_centerY, .5f);
a.recycle();
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int parentWidth = MeasureSpec.getSize(widthMeasureSpec);
int parentHeight = MeasureSpec.getSize(heightMeasureSpec);
paint = new Paint(Paint.ANTI_ALIAS_FLAG);
RadialGradient gradient = new RadialGradient(
parentWidth*centerX,
parentHeight*centerY,
parentWidth*gradientRadiusWidthPercent,
new int[] {startColor, endColor},
null,
android.graphics.Shader.TileMode.CLAMP);
paint.setDither(true);
paint.setShader(gradient);
}
@Override
protected void onDraw(Canvas canvas) {
canvas.drawRect(0, 0, getWidth(), getHeight(), paint);
}
}
In attrs.xml:
<declare-styleable name="RadialGradientView">
<attr name="startColor" format="color|reference"/>
<attr name="endColor" format="color|reference"/>
<attr name="gradientRadiusWidthPercent" format="float"/>
<attr name="centerX" format="float"/>
<attr name="centerY" format="float"/>
</declare-styleable>
Unfortunately you can't create an XML drawable from a custom class, so you can't set it as a View's android:background. The workaround is to use a FrameLayout to layer it as the background.
<FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="100dp">
<com.RadialGradientView
android:layout_width="match_parent"
android:layout_height="match_parent"
app:centerX=".3"
app:centerY=".5"
app:endColor="#0f0"
app:startColor="#f00"
app:gradientRadiusWidthPercent=".5"
/>
<TextView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:text="What's up world?"/>
</FrameLayout>
You could create a shape drawable at runtime similiar to this post https://stackoverflow.com/a/4943888/604504
This way you could calculate the percentage after retrieving the screen size.