Mohamad Shaker
Mohamad Shaker

Reputation: 1486

How to add shadow to vector drawable?

I have a vector drawable (category_bg) and I'm using it as a background to a FrameLayout

<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
    android:width="200dp"
    android:height="100dp"
    android:viewportWidth="600"
    android:viewportHeight="450">

    <path
        android:fillColor="#FFFFFF"
        android:fillType="evenOdd"
        android:pathData="M24.8,21.4c0,0,73.8-8.1,161.3-8.1c77.7,0,153.9,2,231.9,2c88.5,0,156.3,6,156.3,6c5.6,0,10.1,4.5,10.1,10.1
c0,0,2,41.2,2,134.1c0,68.5,4,77.9,4,139.1c0,65-6,114.9-6,114.9c0,5.6-4.5,10.1-10.1,10.1c0,0-32.1,7.1-80.6,7.1
c-47.5,0-99.6-7.1-164.7-7.1c-77.6,0-160.9,6-219.4,6c-52.8,0-84.7-6-84.7-6c-5.6,0-10.1-4.5-10.1-10.1c0,0-4-46.6-4-111.9
c0-85.8-1-102.9-1-183.5c0-55,5-92.7,5-92.7C14.7,25.9,19.2,21.4,24.8,21.4z" />
</vector>

I would like to add shadow to the vector drawable

my xml file

<FrameLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:background="@drawable/category_bg"
        android:padding="3dp">


        <com.shaker.materialComponents.view.MaskableFrameLayout
            android:id="@+id/category"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            app:mask="@drawable/pic_mask"
            app:porterduffxfermode="DST_IN">

            <com.shaker.materialComponents.view.AspectRatioImageView
                android:id="@+id/image"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:adjustViewBounds="true"
                android:scaleType="centerCrop"
                android:src="@drawable/meal"
                app:aspectRatio="0.75"
                app:aspectRatioEnabled="true"
                app:dominantMeasurement="width"/>


            <LinearLayout
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:layout_gravity="bottom"
                android:background="@drawable/category_gradient_bg"
                android:gravity="center"
                android:orientation="horizontal"
                android:paddingBottom="16dp"
                android:paddingLeft="16dp"
                android:paddingRight="16dp"
                android:paddingTop="20dp">


                <com.shaker.materialComponents.view.MaterialTextView
                    android:id="@+id/name"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:layout_weight="1"
                    android:ellipsize="end"
                    android:lines="1"
                    android:text="Salads"
                    android:textColor="#fff"
                    android:textSize="18dp"
                    android:textStyle="bold"
                    app:customFont="GothamRounded-Bold.ttf"/>

                <ImageView
                    android:id="@+id/expand_btn"
                    android:layout_width="20dp"
                    android:layout_height="20dp"
                    android:src="@drawable/expand_button"/>

            </LinearLayout>

        </com.shaker.materialComponents.view.MaskableFrameLayout>

current result

enter image description here

after adding shadow it should be like this

enter image description here

Upvotes: 15

Views: 16131

Answers (3)

Raymond Arteaga
Raymond Arteaga

Reputation: 4673

If you are already using compose (or willing to integrate it to your project by using a ComposeView together with the traditional views approach) you can do it like this:

Box{
        Icon(
            imageVector = Icons.Default.ArrowForward,
            contentDescription = null,
            Modifier
                .offset(x = (-1).dp, y = (1).dp)
                .blur(4.dp),
            tint = Color.Black
            )
            Icon(
                imageVector = Icons.Default.ArrowForward,
                contentDescription = null,
                tint = Color.White
            )
}

It looks like this: enter image description here

Which can be tuned using the blur modifier and tint parameter. Icons and values are hard coded in this example for clarity.

Upvotes: 0

Ultimo_m
Ultimo_m

Reputation: 4897

Using the solution above I did this for a circle drawable:

val source: Drawable = ContextCompat.getDrawable(context,R.drawable.my_drawable)!!
val colors = intArrayOf(
                Color.GRAY, Color.TRANSPARENT
        )
val shadow: Drawable = GradientDrawable(GradientDrawable.Orientation.BOTTOM_TOP, colors)
val s: Shape = DrawableShadow(context, source, source, shadow, 10f)
imageView.background = ShapeDrawable(s)
imageView.setImageResource(R.drawable.my_drawable)

and the DrawableShadow I changed:

class DrawableShadow(
    private val context: Context,
    private val source: Drawable,
    private val mask: Drawable,
    private val shadow: Drawable,
    private val shadowRadius: Float
) : Shape() {
...
override fun draw(canvas: Canvas, paint: Paint?) {
    canvas.drawColor(Color.TRANSPARENT)
...

Credits to @pskink for his solution

Upvotes: 2

Dominikus K.
Dominikus K.

Reputation: 283

All kudos for this goes to pskink, who linked this code in his comment to the question. Unfortunately this still seems to be the only solution out there

// test code
    View v = new View(this);
    setContentView(v);

    Drawable source = getResources().getDrawable(R.drawable.chrome);
    Drawable mask = getResources().getDrawable(R.drawable.chrome_mask);;
    int[] colors = {
            Color.RED, Color.BLACK
    };
    Drawable shadow = new GradientDrawable(GradientDrawable.Orientation.LEFT_RIGHT, colors);
    Shape s = new S(this, source, mask, shadow, 8);
    v.setBackground(new ShapeDrawable(s));

// end of test code

~

class S extends Shape {
    Context ctx;
    Drawable source;
    Drawable mask;
    Drawable shadow;
    float shadowRadius;
    Matrix matrix = new Matrix();
    Bitmap bitmap;

    public S(Context ctx, Drawable source, Drawable mask, Drawable shadow, float shadowRadius) {
        this.ctx = ctx;
        this.source = source;
        this.mask = mask;
        this.shadow = shadow;
        this.shadowRadius = shadowRadius;
    }

    @Override
    protected void onResize(float width, float height) {
        int intrinsicWidth = source.getIntrinsicWidth();
        int intrinsicHeight = source.getIntrinsicHeight();
        source.setBounds(0, 0, intrinsicWidth, intrinsicHeight);
        mask.setBounds(0, 0, intrinsicWidth, intrinsicHeight);
        shadow.setBounds(0, 0, intrinsicWidth, intrinsicHeight);

        RectF src = new RectF(0, 0, intrinsicWidth, intrinsicHeight);
        RectF dst = new RectF(0, 0, width, height);
        matrix.setRectToRect(src, dst, Matrix.ScaleToFit.CENTER);

        Paint p = new Paint();
        p.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_IN));
        bitmap = Bitmap.createBitmap(intrinsicWidth, intrinsicHeight, Bitmap.Config.ARGB_8888);
        Canvas c = new Canvas(bitmap);

        shadow.draw(c);
        c.saveLayer(null, p, Canvas.ALL_SAVE_FLAG);
        mask.draw(c);
        c.restore();
        bitmap = blurRenderScript(bitmap);

        c = new Canvas(bitmap);
        int count = c.saveLayer(null, null, Canvas.ALL_SAVE_FLAG);
        source.draw(c);
        c.saveLayer(null, p, Canvas.ALL_SAVE_FLAG);
        mask.draw(c);
        c.restoreToCount(count);
    }

    @Override
    public void draw(Canvas canvas, Paint paint) {
        canvas.drawColor(0xffdddddd);
        canvas.drawBitmap(bitmap, matrix, null);
    }

    Bitmap blurRenderScript(Bitmap input) {
        Bitmap output = Bitmap.createBitmap(input.getWidth(), input.getHeight(), input.getConfig());
        RenderScript rs = RenderScript.create(ctx);
        ScriptIntrinsicBlur script = ScriptIntrinsicBlur.create(rs, Element.U8_4(rs));
        Allocation inAlloc = Allocation.createFromBitmap(rs, input, Allocation.MipmapControl.MIPMAP_NONE, Allocation.USAGE_GRAPHICS_TEXTURE);
        Allocation outAlloc = Allocation.createFromBitmap(rs, output);
        script.setRadius(shadowRadius);
        script.setInput(inAlloc);
        script.forEach(outAlloc);
        outAlloc.copyTo(output);
        rs.destroy();
        return output;
    }
}

Upvotes: 5

Related Questions