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:
Images using API 22:
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