How to merge two Drawables dynamically in Android?

akshayt23 picture akshayt23 · Jul 17, 2017 · Viewed 9.5k times · Source

so I have two different Drawables which I need to merge and get a single Drawable at runtime. I want the first Drawable to be at the top and the other one at the bottom. I came across LayerDrawable and it looks like it's exactly what I need, but I'm having trouble trying to arrange the Drawables.

So I have an ImageButton which is 48x48 dp and this is where the final Drawable goes. The first Drawable is a plus button (20x20 dp) and the second one is a small dot (4x4 dp) below the plus button.

The plus button Drawable is loaded using font glyphs. I'm creating the dot button Drawable using this xml snippet:

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android" 
       android:shape="oval">
    <solid
        android:color="@color/white_40"/>
    <size
        android:width="4dp"
        android:height="4dp"/>
</shape>

My first approach was to just add both Drawables to the LayerDrawable, but when I do that, the width/height attributes of the dot specified in the xml are ignored, and it stretches to cover the plus icon.

LayerDrawable finalDrawable = new LayerDrawable(new Drawable[] {plusIcon, dotIcon});

The above results in this:



The second approach I tried was by using setLayerInset to try and position the two Drawables.

    LayerDrawable finalDrawable = new LayerDrawable(new Drawable[] {plusIcon, dotIcon});
    finalDrawable.setLayerInset(0, 0, 0, 0, 0);
    finalDrawable.setLayerInset(1, dp(22), dp(44), dp(22), 0);

The above code snippet ended up placing the dot in the correct position, but it also started affecting the position and size of the plus button and it ended up looking like this:
enter image description here

But what I really want is to have the plus button in the centre of the ImageButton and the plus icon just below it. Does anyone have an idea where I'm going wrong and how can I position the two drawables correctly?

PS: My app supports API 15+, so I can't use a bunch of methods from LayerDrawable's API like setLayerGravity, `setPaddingMode, etc.

Answer

Ben P. picture Ben P. · Jul 17, 2017

Edit

This code will work on API levels below 23:

ImageButton button = (ImageButton) findViewById(R.id.button);

Drawable plusIcon = ContextCompat.getDrawable(this, R.drawable.plus);
Drawable dotIcon = ContextCompat.getDrawable(this, R.drawable.oval);

int horizontalInset = (plusIcon.getIntrinsicWidth() - dotIcon.getIntrinsicWidth()) / 2;

LayerDrawable finalDrawable = new LayerDrawable(new Drawable[] {plusIcon, dotIcon});
finalDrawable.setLayerInset(0, 0, 0, 0, dotIcon.getIntrinsicHeight());
finalDrawable.setLayerInset(1, horizontalInset, plusIcon.getIntrinsicHeight(), horizontalInset, 0);

button.setImageDrawable(finalDrawable);

Original

The following code works for me:

ImageButton button = (ImageButton) findViewById(R.id.button);

Drawable plusIcon = ContextCompat.getDrawable(this, R.drawable.plus);
Drawable dotIcon = ContextCompat.getDrawable(this, R.drawable.oval);

LayerDrawable finalDrawable = new LayerDrawable(new Drawable[] {plusIcon, dotIcon});
finalDrawable.setLayerInsetBottom(0, dotIcon.getIntrinsicHeight());
finalDrawable.setLayerGravity(1, Gravity.BOTTOM | Gravity.CENTER_HORIZONTAL);

button.setImageDrawable(finalDrawable);

This produces the following ui:

enter image description here