Oval Gradient in Android
Asked Answered
M

5

16

I know how to setup and display an oval shape. I know how to apply a gradient to this shape. What I cant figure out is how I can get an oval gradient to match the shape.

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
       android:shape="oval" >
    <gradient
        android:startColor="#66FFFFFF"
        android:endColor="#00FFFFFF"
        android:gradientRadius="100"
        android:type="radial" />
</shape>

If you can imagine, this gradient has a semi transparent white glow in the middle, then fades to alpha zero at the edges. I need to get it to go out in an oval shape, not just a circular gradient. How can I achieve this?

Mephistopheles answered 20/8, 2010 at 1:25 Comment(1)
I've solved a similar issue by calling Canvas#scale before drawing GradientDrawable on it.Lille
T
2

This is difficult since drawables defined in this fashion draw themselves at runtime, adapting to the space you put them in. Your best solution, if you must do this in code, would be to take the shape drawable you have defined in XML and draw it onto a Canvas or into a Bitmap as a perfect circle. At this point, the gradient pattern will follow the shape outline. Once the shape has been drawn into a static context you can add the shape to a view (as a background, let's say), which will distort it into an oval when it tries to fit the view bounds. Since you have an image, the whole thing will distort proportionately.

Hopefully, it won't pixel too bad with this method.

Tafilelt answered 22/8, 2010 at 23:28 Comment(2)
Marking correct for "if you must do this in code". I found it is much easier and much higher quality as an image, and this reminded me of this.Mephistopheles
This is my implementation of your idea: https://mcmap.net/q/751421/-memory-heavy-oval-gradientVerney
O
3

I would suggest more 'direct' drawing approach. If you can draw gradient pixel-by-pixel, then you need just to remember that for

  • circle gradient color is proportional to r
  • ellipse (oval) gradient color is proportional to r1+r2

Here:

r - distance to circle center

r1,r2 - distances to two foci of ellipse

EDIT: Consider this Pixel Shader code:

uniform sampler2D tex;

void main()
{
    vec2 center = vec2(0.5,0.5);
    float len = 1.3*(distance(gl_TexCoord[0].xy,center));
    vec2 foc1 = vec2(len,0.);
    vec2 foc2 = vec2(-len,0.);
    float r = distance(center+foc1,gl_TexCoord[0].xy) +
             distance(center+foc2,gl_TexCoord[0].xy);
    float k = pow(r*0.9,1.3);
    vec4 color = vec4(k,k,k,1.);
    gl_FragColor = color;
}

You will get oval something like that: alt text

good luck

Oakum answered 29/8, 2010 at 8:30 Comment(2)
This perhaps could be ok, but it seems expensive.Mephistopheles
It depends how per-pixel calculation would be performed. If it is performed on GPU side (as gradient calculation Pixel Shader) - then it is as fast as lightning :)Oakum
S
3
<?xml version="1.0" encoding="utf-8"?>

<stroke android:width="1dp" android:color="#ffffffff" />

<size
    android:width="40dp"
    android:height="40dp"/>

<gradient
    android:type="radial"
    android:startColor="#ffff0000"
    android:endColor="#330000ff"
    android:gradientRadius="40dp"
    android:angle="270"
    android:centerX=".5"
    android:centerY=".5"/>

Swinney answered 9/2, 2015 at 4:24 Comment(1)
Good answer but how to do this programmatically with kotlin?Marrin
T
2

This is difficult since drawables defined in this fashion draw themselves at runtime, adapting to the space you put them in. Your best solution, if you must do this in code, would be to take the shape drawable you have defined in XML and draw it onto a Canvas or into a Bitmap as a perfect circle. At this point, the gradient pattern will follow the shape outline. Once the shape has been drawn into a static context you can add the shape to a view (as a background, let's say), which will distort it into an oval when it tries to fit the view bounds. Since you have an image, the whole thing will distort proportionately.

Hopefully, it won't pixel too bad with this method.

Tafilelt answered 22/8, 2010 at 23:28 Comment(2)
Marking correct for "if you must do this in code". I found it is much easier and much higher quality as an image, and this reminded me of this.Mephistopheles
This is my implementation of your idea: https://mcmap.net/q/751421/-memory-heavy-oval-gradientVerney
C
1

Another workaround that I found is to use scaleX="1.x" or scaleY="1.x". However, this dose not match the shape.

Caffrey answered 12/1, 2021 at 13:53 Comment(0)
F
0

A "dumb but working" way to do this would be to draw your ellipse contour, then a multitude of nested ellipses with the same "center", whose sizes and colors change as the ellipses grow smaller, all the way to a one-pixel ellipse. If you custom code your gradient, interpolate the colors in HSV, not in RVB !

Filose answered 20/8, 2010 at 6:43 Comment(1)
This suggestion would work OK, but be warned that each layer you add is another layer your GPU has to draw to the screen. I tried this once and the whole app ground to a halt.Moccasin

© 2022 - 2024 — McMap. All rights reserved.