Java – Draw complex drawables in APIs lower than 23

Draw complex drawables in APIs lower than 23… here is a solution to the problem.

Draw complex drawables in APIs lower than 23

I’m trying to draw a drawable object consisting of circles and rectangles. When the API is set to 23, it works fine. I noticed that the following properties of “project” have been added in the latest api: height, gravity, weight. Can older versions of SDKs be implemented?

Drawable:

<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
    <item
        android:height="5dp"
        android:gravity="center_vertical"
        android:left="5dp">
        <shape android:shape="rectangle">
            <size
                android:width="50dp"
                android:height="5dp" />
            <solid android:color="#125572" />
        </shape>
    </item>
    <item
        android:width="20dp"
        android:height="20dp"
        android:gravity="center_vertical">
        <shape android:shape="oval">
            <solid android:color="#125572" />
            <size
                android:width="20dp"
                android:height="20dp" />
        </shape>
    </item>
</layer-list>

Images using API 23:

enter image description here

Images using API 22:

enter image description here

Solution

I

know this issue is old, but I solved it and maybe it will be useful for people now.

For the solution, I created two classes.

A class called MyLayerDrawable extends LayerDrawable. An important thing to keep in mind about this class is that you can get the top and left illustrations for each layer.

public class MyLayerDrawable extends LayerDrawable{

private List<Layer> layers;

some code here

public static class Layer{
    private Drawable drawable;
    private int topInset;
    private int leftInset;
    public Layer(Drawable drawable,int topInset,int leftInset){
        this.leftInset=leftInset;
        this.drawable=drawable;
        this.topInset=topInset;
    }
    public Drawable getDrawable(){
        return drawable;
    }
    public int getTopInset(){
        return topInset;
    }
    public int getLeftInset(){return leftInset; }
}

The second class I created is called MyImageViewPatch, and it has an ImageView.
The only public method of this class is setImageMyLayerDrawable(MyLayerDrawable layerDrawable). This method delegates the drawable image to the ImageView: it owns

public void setImageMyLayerDrawable(MyLayerDrawable layerDrawable){
    imageView.setImageDrawable(layerDrawable);

}

But before calling the setImageDrawable method, the observer object is added to the ImageView in the MyImageViewPatch constructor. Call this observer before drawing the Drawable in the ImageView:

        imageView.getViewTreeObserver().addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
        @Override
        public boolean onPreDraw() {
            MyLayerDrawable layerDrawable=(MyLayerDrawable)imageView.getDrawable();
            int numberOfLayers=layerDrawable.getNumberOfLayers();
            Drawable drawable;
            Rect bounds;

for(int i=0; i<numberOfLayers; i++){//for each layer
                drawable=layerDrawable.getDrawable(i);
                bounds=drawable.getBounds();
                bounds.top=layerDrawable.getLayer(i).getTopInset();
                bounds.left=layerDrawable.getLayer(i).getLeftInset();
                if(drawable.getIntrinsicHeight()<bounds.height()){
                    bounds.bottom=drawable.getIntrinsicHeight()+layerDrawable.getLayer(i).getTopInset();

}
                if(drawable.getIntrinsicWidth()<bounds.width()){
                    bounds.right=drawable.getIntrinsicWidth()+layerDrawable.getLayer(i).getLeftInset();
                }
            }
            return true;
        }
    });

As you can see, before drawing MyLayerDrawable, the boundaries of each layer are adjusted to the left and top inset maps; This is a key part of getting the drawable to draw at the correct size instead of stretching.

This is the shape xml file:

<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item
    android:height="5dp"
    android:gravity="center_vertical"
    android:left="5dp"
    android:top="8dp">
    <shape android:shape="rectangle">
        <size
            android:width="50dp"
            android:height="5dp" />
        <solid android:color="#125572" />
    </shape>
</item>

<item
android:width="20dp"
android:height="20dp">
<shape android:shape="oval">
    <solid android:color="#125572" />
    <size
        android:width="20dp"
        android:height="20dp" />
</shape>
</item>

</layer-list>

To center the rectangle vertically, I used android:top=”8dp” instead of android:gravity=”center_vertical”.

This is the code for the main activity:

public class MainActivity extends AppCompatActivity {

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

ImageView iv=(ImageView)findViewById(R.id.imageView);
    MyLayerDrawable myLayerDrawable=MyLayerDrawable.Builder.build(getApplicationContext(),R.drawable.layer_d_example);
    MyImageViewPatch myImageViewPatch=new MyImageViewPatch(iv);
    myImageViewPatch.setImageMyLayerDrawable(myLayerDrawable);

}

I have the complete code in github. You can check out: https://github.com/marcosbses/my_layout_drawable.git

Related Problems and Solutions