Android: Setting padding in a overlay image background - android

I'm trying to create a dynamic icon menu for my android application.
The icon is made from 2 drawable, a 9-patch background image with the icon image overlay on it.
With reference to overlay two images in android to set an imageview, I've the following code which have the overlay nicely.
Resources res = parent.getResources();
Drawable icon_bg = res.getDrawable(R.drawable.menu_icon_bg);
Drawable icon = res.getDrawable(R.drawable.menu_icon);
// Not working
int int_icon_height = icon.getIntrinsicHeight();
int int_icon_width = icon.getIntrinsicWidth();
Rect rect_icon_bg_bound = new Rect();
rect_icon_bg_bound.left = 0;//(int) Math.round(int_icon_width*-1.5);
rect_icon_bg_bound.right = (int) Math.round(int_icon_width*1.5);
rect_icon_bg_bound.top = 0;//(int)Math.round(int_icon_height*-1.5);
rect_icon_bg_bound.bottom = (int)Math.round(int_icon_height*1.5);
icon_bg.setBounds(rect_icon_bg_bound);
Drawable[] layers = new Drawable[2];
layers[0] = icon_bg;
layers[1] = icon;
LayerDrawable layerDrawable = new LayerDrawable(layers);
ImageView iv_icon_combined = new ImageView(getApplicationContext());
iv_icon_combined.setImageDrawable(layerDrawable);
The problem I'm facing right now is adding a padding so that the icon will have some spacing within the background.
Hope to get some enlightenment here, thanks in advance.

I just ran into this same issue. Take a look at the setLayerInset method on LayerDrawable. It takes the layer level as an argument and then the modifiers for the left, top, right, bottom bounds of the drawable.

Related

How to merge two Drawables dynamically in Android?

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:
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.
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:
This is how i solved this:
final View view = findViewById(R.id.view_in_layout);
Drawable bottom = ContextCompat.getDrawable(context, R.drawable.ic_bottom);
Drawable top = ContextCompat.getDrawable(context, R.drawable.ic_top);
//bottom = index 0, top = index 1
final LayerDrawable layer = new LayerDrawable(new Drawable[]{bottom, top});
view.addOnLayoutChangeListener(new View.OnLayoutChangeListener() {
#Override
public void onLayoutChange(View v, int left, int top, int right, int bottom,
int oldLeft, int oldTop, int oldRight, int oldBottom) {
//get the real height after it is calculated
int height = view.getHeight();
//set the height half of the available space for example purposes,
//the drawables will have half of the available height
int halfHeight = height / 2;
//the bottom drawable need to start when the top drawable ends
layer.setLayerInset(0, 0, halfHeight, 0, 0);
//the top drawable need to end before the bottom drawable starts
layer.setLayerInset(1, 0, 0, 0, halfHeight);
view.setBackground(layer);
view.removeOnLayoutChangeListener(this);
}
});
You can choose different dimensions for your drawables or moving them with the indexes. I used View.OnLayoutChangeListener(...) for example to wait for the current available height of the view

Android Drawable Overflowing From Button Borders

I am using the following code to scale an image to 150dpx150dp and place in inside a 150dpx150dp button but the image overflows the button in all dimensions:
float densityScale = getResources().getDisplayMetrics().density;
float scaledImageWidth = 150 * densityScale;
float scaledImageHeight = 150 * densityScale;
Drawable image = getResources().getDrawable(R.drawable.user_photo);
Bitmap bitmap = ((BitmapDrawable)image).getBitmap();
Drawable scaledImage = new BitmapDrawable(Bitmap.createScaledBitmap(bitmap, (int)scaledImageWidth, (int)scaledImageHeight, true));
scaledImage.setBounds(0, 0, (int)scaledImageWidth, (int)scaledImageHeight);
((Button)findViewById(R.id.button)).setCompoundDrawables(null, scaledImage, null, null);
((Button)findViewById(R.id.button)).setPadding(0,0,0,0);
Button and image are of the same size. Why does this overflow happen and how can it be fixed?
(I know I can use an ImageButton for this but I want to use button drawable because there are cases where I will need to add text etc.)
You need to have a smaller drawable. Button already has a background with a certain padding

Why dynamically created SeekBar is not with my color?

I have to create SeekBar with primary and secondary progress at runtime. Looking at tileify function for how should I treat LayerDrawable, and progress_horizontal_holo_dark.xml for proper IDs, i came up with following:
// parent view
LinearLayout parent = (LinearLayout) findViewById(R.id.parent);
// create widget
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(
RelativeLayout.LayoutParams.MATCH_PARENT, RelativeLayout.LayoutParams.WRAP_CONTENT);
SeekBar seekBar = new SeekBar(this);
seekBar.setLayoutParams(params);
// set bg resources
Drawable background = getResources().getDrawable(R.drawable.scrubber_track_holo_light);
ScaleDrawable primary = new ScaleDrawable(getResources().getDrawable(R.drawable.scrubber_primary_holo), 0x10|0x03, 100.0f, 100);
ScaleDrawable secondary = new ScaleDrawable(getResources().getDrawable(R.drawable.scrubber_secondary_holo), 0x100x03, 100.0f, 85);
LayerDrawable layerDrawable = new LayerDrawable(new Drawable[] { background, secondary, primary});
layerDrawable.setId(0, android.R.id.background);
layerDrawable.setId(1, android.R.id.secondaryProgress);
layerDrawable.setId(2, android.R.id.progress);
seekBar.setBackgroundDrawable(layerDrawable);
seekBar.setThumb(getResources().getDrawable(R.drawable.seekbar_btn));
// set values
seekBar.setMax(50);
seekBar.setSecondaryProgress(25);
seekBar.setProgress(15);
parent.addView(seekBar);
This is how SeekBar should look like:
And this are mine resource files:
- scrubber_primary_holo.9.png
- scrubber_secondary_holo.9.png
Problem:
setProgressDrawable(layerDrawable) doesn't work as expected. I use this function in XML code, but if I use it at runtime, I get resultult as shown at the picture below.
Any ideas?
In case someone else has the same problem, I found the solution. There were two issues. First one is that ScaleBitmap although used in XML doesn't work from code. I had to use ClipDrawable. The second issue was with showing primary and secondary progress - they were shown after the first progress change. This is solved by adding seekBar to parent view first and then setting primary and secondary progress. You can see the source code below. Cheers!
// CREATE WIDGET
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(
RelativeLayout.LayoutParams.MATCH_PARENT, RelativeLayout.LayoutParams.WRAP_CONTENT);
SeekBar seekBar = new SeekBar(this, null, android.R.attr.seekBarStyle);
seekBar.setLayoutParams(params);
parent.addView(seekBar);
seekBar.setMax(50);
seekBar.setProgress(15);
seekBar.setSecondaryProgress(25);
// CREATE PROGRESS BAR
LayerDrawable layerDrawable = (LayerDrawable) seekBar.getProgressDrawable();
seekBar.setThumb(getResources().getDrawable(R.drawable.seekbar_btn));
Drawable progress = (Drawable) layerDrawable.findDrawableByLayerId(android.R.id.progress);
Bitmap progressBmp = BitmapFactory.decodeResource(getResources(), R.drawable.scrubber_secondary_holo);
progress = new ClipDrawable(new BitmapDrawable(getResources(), progressBmp), Gravity.AXIS_PULL_BEFORE, ClipDrawable.HORIZONTAL);
layerDrawable.setDrawableByLayerId(android.R.id.progress, progress);
Drawable secondary = (Drawable) layerDrawable.findDrawableByLayerId(android.R.id.secondaryProgress);
Bitmap secondaryBmp = BitmapFactory.decodeResource(getResources(), R.drawable.scrubber_primary_holo);
secondary = new ClipDrawable(new BitmapDrawable(getResources(), secondaryBmp), Gravity.AXIS_PULL_BEFORE, ClipDrawable.HORIZONTAL);
layerDrawable.setDrawableByLayerId(android.R.id.secondaryProgress, secondary);
Drawable background = (Drawable) layerDrawable.findDrawableByLayerId(android.R.id.background);
Bitmap backgroundBmp = BitmapFactory.decodeResource(getResources(), R.drawable.scrubber_track_holo_light);
background = new BitmapDrawable(getResources(), backgroundBmp);
layerDrawable.setDrawableByLayerId(android.R.id.background, background);

Create Custom Seekbar Programmatically (No XML)

Currently I am trying to create a custom Seekbar without using xml as the images for the background and progress bar need to be changed. This is what I have at the moment:
Drawable drawable = new BitmapDrawable(appState.images.get(Integer.parseInt(image)));
this.setBackgroundDrawable(drawable);
Drawable drawable2 = new BitmapDrawable(appState.images.get(Integer.parseInt(image_a)));
this.setProgressDrawable(drawable2);
this.setProgress(0);
But the progressDrawable is just set to the entire size of Seekbar and when I move the thumb from side to side nothing happens. Does anybody have any ideas as I am completely stumped.
So this previous answer does the trick for you:
Changing the size of the seekbar programmatically but cant get the thumb to be larger than the bar
From the original asker:
In the end I am using this code:
public void setSeekBarImages(){
Drawable drawable = new
BitmapDrawable(appState.images.get(Integer.parseInt(image_a)));
ClipDrawable clip = new ClipDrawable(drawable,
Gravity.LEFT,ClipDrawable.HORIZONTAL);
Drawable drawable2 = new
BitmapDrawable(appState.images.get(Integer.parseInt(image)));
InsetDrawable d1= new InsetDrawable(drawable2,5,5,5,5);
//the padding u want to use
setThumb(null);
LayerDrawable mylayer = new LayerDrawable(new Drawable[]{d1,clip});
setProgressDrawable(mylayer);
setProgress(0);
}

Drawing shapes in background TextView

I,m trying to paint circle in background of my textView. It should by a small object in up/left corner.
temporar = new TextView(cxt);
OvalShape oval = new OvalShape();
//oval.resize(10, 10);
qualitySignalDrawable = new ShapeDrawable(oval);
qualitySignalDrawable.getPaint().setColor(0xff74AC23);
qualitySignalDrawable.setBounds(2,2,10,10);
signalQualityText.setGravity(Gravity.CLIP_HORIZONTAL);
temporar.setBackgroundDrawable(qualitySignalDrawable);
temporar.setText("aaaaaaaaaaaaa");
I tried set difrents gravity and size and Bounds but id allways stretch shape to container size. I'm missing something?

Categories

Resources