2

I would like to achieve a layout that looks like the "desired" image as part of the initial app state that informs the user of some basic actions. Right now I'm getting the "actual" image. enter image description here

I need to have a background image of any drawable/image that has an overlay that is slanted from lower-left to upper-right and of any color. This also has to be scalable in terms of the same shape over any number of devices, phones or tablets and support back to SDK 16.

So far I've been going off the idea of using a vector image to create the slant drawable and then overlay that on top of the background image using a FrameLayout for the layering effect. I'm not sure if this is the best way to accomplish this but I'd like the overlay drawable to be a vector drawable or a nine-patch so it doesn't pixelate on wider layouts.

layout/view_layout.xml

<FrameLayout
    android:layout_width="match_parent"
    android:layout_height="wrap_content">

    <ImageView
        android:id="@+id/background_image"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:src="@drawable/bg_image"/>

    <View
        android:layout_width="match_parent"
        android:layout_height="52dp"
        android:layout_gravity="bottom"
        android:background="@drawable/overlay"/>

</FrameLayout>

drawable/overlay.xml

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

    <path
        android:fillColor="#fff"
        android:pathData="M48,0 L48,36 L0,36 L0,32 Z"/>

</vector>

If anyone knows how to add a drop shadow to a vector drawable (I haven't been able to find a way) or if there is a better approach to this, any help or suggestion would be much appreciated. Thanks!

wchristiansen
  • 436
  • 7
  • 20
  • i think you need to create a layer like this but a round shape (circle) -- http://stackoverflow.com/questions/26356060/how-to-add-shadow-effect-on-drawable-in-android – Tasos Feb 10 '17 at 18:52
  • @Tasos That isn't quite what I'm looking for. It appears that the selected answer is only using a rectangle shape which won't work because of the way the slant works the shadow needs to follow the entire slant without being separated. I also don't think creating an addition three views is what I'd prefer to do. Thanks for the suggestion though! – wchristiansen Feb 13 '17 at 15:26

1 Answers1

3

What I ended up doing to solve this was extending the ImageView class and adding custom draw logic to overlay the Image. It's not using a Vector Drawable like I had hoped but it does present the exact effect that I was hoping for. Here's a basic outline of my class:

public class CoveredImageView extends ImageView {

    static float DENSITY = 1f;
    static final float SHADOW_DISTANCE = 10f;
    static final int SHADOW_COLOR = 0xAA000000;

    Path path;
    Paint paint;

    Point p1, p2, p3;

    public CoveredImageView(Context context) {
        this(context, null);
    }

    public CoveredImageView(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public CoveredImageView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init(context);
    }

    void init(@NonNull Context context) {
        DENSITY = context.getResources().getDisplayMetrics().density;

        path = new Path();
        paint = new Paint();
        p1 = new Point();
        p2 = new Point();
        p3 = new Point();

        // Required to make the ShadowLayer work properly
        setLayerType(LAYER_TYPE_SOFTWARE, null);
    }

    void updateDrawVariables() {
        int shadowSize = (int)(SHADOW_DISTANCE * DENSITY);

        paint.setColor(Color.WHITE);
        paint.setStyle(Paint.Style.FILL);
        paint.setAntiAlias(true);
        paint.setShadowLayer(shadowSize, 0, -1, SHADOW_COLOR);

        // Offset the actual position by the shadow size so
        int left = 0 - shadowSize;
        int right = getMeasuredWidth() + shadowSize;
        int bottom = getMeasuredHeight();

        p1.set(left, bottom);
        p2.set(right, bottom - (int)(52 * DENSITY));
        p3.set(right, bottom);

        path.setFillType(Path.FillType.EVEN_ODD);
        path.moveTo(p1.x, p1.y);
        path.lineTo(p2.x, p2.y);
        path.lineTo(p3.x, p3.y);
        // Move the path shape down so that the shadow doesn't "fade" at the left and right edges
        path.lineTo(p3.x, p3.y + shadowSize);
        path.lineTo(p1.x, p1.y + shadowSize);
        path.close();
    }

    @Override
    public void onLayout(boolean changed, int l, int t, int r, int b) {
        super.onLayout(changed, l, t, r, b);
        // Update all the drawing variables if the layout values have changed
        if(changed) {
            updateDrawVariables();
        }
    }

    @Override
    public void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        // Paint the current path values that were set after onLayout()
        canvas.drawPath(path, paint);
    }
}
wchristiansen
  • 436
  • 7
  • 20
  • Have you tried (android:strokeColor) (android:strokeWidth) in the path of the vector drawable -- for instance (android:strokeColor="#50FFFFFF" android:strokeWidth="5") will give you 50% white transparency with a thickness of 5 giving you a box shadow around the vector https://developer.android.com/reference/android/graphics/drawable/VectorDrawable.html – Tasos Feb 14 '17 at 09:50
  • @Tasos Yeah, I actually had tried that initially but the problem is that the stroke color can't handle a true gradient, it can only handle a solid color unless you are using SDK 24+. My app needs to support SDK 16+ so that won't work. Thanks though – wchristiansen Feb 14 '17 at 15:28