Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Enlarge the center of an Android gradient drawable

I created an Android gradient drawable where the top and bottom are black and the center is transparent:

<shape xmlns:android="http://schemas.android.com/apk/res/android"
    android:shape="rectangle" >

    <gradient
        android:startColor="@android:color/black"
        android:centerColor="@android:color/transparent"
        android:endColor="@android:color/black"
        android:angle="90"/>
</shape>

The rendered gradient looks like this:

gradient image

As you can see, the black parts spread to most of the screen. I want the black to show only on a small portion of the top and bottom. Is there a way I can make the transparent center larger, or make the top and bottom black stripes smaller?

I tried playing around with some of the other XML attributes mentioned in the linked GradientDrawable documentation, yet none of them seem to make and difference.

like image 428
Tot Zam Avatar asked Mar 16 '17 11:03

Tot Zam


2 Answers

For an XML only solution, you can create a layer-list with two separate gradient objects.

The following code creates two overlapping gradient objects and uses centerY with centerColor to offset the black section. Here, the centerY attributes are set to 0.9 and 0.1, so the black color is restricted to the top and bottom 10% of the view height.

<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
    <item>
        <shape>
            <gradient
                android:angle="90"
                android:centerColor="@android:color/transparent"
                android:centerY="0.9"
                android:endColor="@android:color/black"
                android:startColor="@android:color/transparent" />
        </shape>
    </item>

    <item>
        <shape>
            <gradient
                android:angle="90"
                android:centerColor="@android:color/transparent"
                android:centerY="0.1"
                android:endColor="@android:color/transparent"
                android:startColor="@android:color/black" />
        </shape>
    </item>
</layer-list>

For API level 23 or higher, the following solution will also work, using android:height. This solution can work even if you don't know the total height of your view, as long as you know how large you want the gradient to be.

This code creates two separate gradients, each with a height of 60sp, and then uses android:gravity to float the gradients to the top and bottom of the view.

<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
    <item
        android:height="60sp"
        android:gravity="top">
        <shape android:shape="rectangle">
            <gradient
                android:angle="90"
                android:endColor="@android:color/black"
                android:startColor="@android:color/transparent" />
        </shape>
    </item>
    <item
        android:height="65sp"
        android:gravity="bottom">
        <shape android:shape="rectangle">
            <gradient
                android:angle="90"
                android:endColor="@android:color/transparent"
                android:startColor="@android:color/black" />
        </shape>
    </item>
</layer-list>

Thank you @Luksprog for the code help, and @thenaoh for the start of the idea.

The above solutions work and it is nice that they are pure XML. If your gradient is showing with stripes, you may want to try a programmatic solution, like shown in @lelloman's answer, to create a smoother gradient.

like image 150
Tot Zam Avatar answered Nov 20 '22 13:11

Tot Zam


Here is how it could be done with a custom Drawable. You can tune the LinearGradient as you want, and then set it as the view's background with view.setBackground(new CustomDrawable());.

public class CustomDrawable extends Drawable {

    private Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
    private int[] colors;
    private float[] positions;

    public CustomDrawable() {
        paint.setStyle(Paint.Style.FILL);
        this.colors = new int[]{0xff000000, 0xffaaaaaa, 0xffffffff, 0xffaaaaaa, 0xff000000};
        this.positions = new float[]{.0f, .2f, .5f, .8f, 1.f};
    }

    @Override
    public void setBounds(int left, int top, int right, int bottom) {
        super.setBounds(left, top, right, bottom);    

        LinearGradient linearGradient = new LinearGradient(left, top,left, bottom, colors, positions, Shader.TileMode.CLAMP);
        paint.setShader(linearGradient);
    }

    @Override
    public void draw(@NonNull Canvas canvas) {    
        canvas.drawRect(getBounds(), paint);
    }

    @Override
    public void setAlpha(@IntRange(from = 0, to = 255) int alpha) {
        paint.setAlpha(alpha);
    }

    @Override
    public void setColorFilter(@Nullable ColorFilter colorFilter) {
        paint.setColorFilter(colorFilter);
    }

    @Override
    public int getOpacity() {
        return PixelFormat.TRANSPARENT;
    }
}
like image 36
lelloman Avatar answered Nov 20 '22 11:11

lelloman