Problems resizing a vector drawable programatically - android

This album explains what I want to do, and the problem I'm having when the vector doesn't resize: http://imgur.com/a/HM3K2
This is the approach I've taken to try scaling the vector, but it doesn't seem to work:
private Bitmap drawCirclePreview(int color, Shape square) {
//An object from my 'VectorOverlay' class which resolves the resource...
VectorOverlay circleOverlay = new VectorOverlay(color);
//...and returns a drawable
Drawable drawable = circleOverlay.drawCircleOverlay(this);
Bitmap bitmap;
//Make bitmap with same dimensions as the square (redundant, I know)
bitmap = Bitmap.createBitmap(square.getWidth(), square.getHeight(), Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(bitmap);
drawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight());
drawable.draw(canvas);
return bitmap;
}
Inside the VectorOverlay method that is being used:
public Drawable drawCircleOverlay(Context context){
Drawable drawable = ContextCompat.getDrawable(context, R.drawable.circle_overlay);
drawable = DrawableCompat.wrap(drawable);
DrawableCompat.setTint(drawable, color);
return drawable;
Then I create layers for the new LayerDrawable:
private void drawSquarePreview(){
Drawable[] layers = new Drawable[2];
//[...] omitting code for when the square object is created and drawn
layers[0] = new BitmapDrawable(squareBmp);
layers[1] = new BitmapDrawable(drawCirclePreview(Color.parseColor("#4af9d8"), square));
LayerDrawable layerDrawable = new LayerDrawable(layers);
ivPreview.setImageDrawable(layerDrawable);
}
Yet, the outcome is still as the pictures show. I'm stumped. I've been searching for answers for a long time now and I still can't find a solution. Any help would be greatly appreciated.
ADDITIONAL INFO: I'm using the Support Library to handle vector drawables on older APIs.

There is no need to use a vector for this. Vectors are slower and messier. You can simply create a circle shape (Drawable) in XML.
Like this;
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="oval">
<solid android:color="#FF000000"/>
<size android:width="40dp"
android:height="40dp"/>
</shape>
Just remember that the size variable is a relative size, but the shape can be scaled to any size. I set the color to black, as that way you can tint it to any color.

Related

Get ripple effect on transparent custom button view

I am making a custom button view that has a gradient border, rounded corners and a transparant background. I am setting the background of the button to the drawable generated by the following code:
protected Drawable getBackgroundDrawable() {
GradientDrawable backgroundDrawable = new GradientDrawable(
mOrientation,
mGradientColors);
backgroundDrawable.setCornerRadius(getHeight() / 2);
backgroundDrawable.setShape(GradientDrawable.RECTANGLE);
if (!mFilled) {
Bitmap background = Bitmap.createBitmap(getWidth(), getHeight(), Bitmap.Config.ARGB_8888);
Canvas backgroundCanvas = new Canvas(background);
backgroundCanvas.drawARGB(0, 0, 0, 0);
backgroundDrawable.setBounds(0, 0, getWidth(), getHeight());
backgroundDrawable.draw(backgroundCanvas);
Paint rectPaint = new Paint();
rectPaint.setAntiAlias(true);
rectPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
backgroundCanvas.drawRoundRect(new RectF(mStroke, mStroke,
getWidth() - mStroke,
getHeight() - mStroke),
getHeight() / 2,
getHeight() / 2,
rectPaint);
return new BitmapDrawable(getResources(), background);
} else {
return backgroundDrawable;
}
}
The only problem is, when you click the button now, you have no feedback. How can I add a ripple effect at the back of the button when I already use the generated drawable as a background? Do I have to do something with a LayerDrawable?
My button looks like this:
What I really want to achieve
Create a Drawable programmatically that has the same result as this XML drawable, where the item in the ripple element is my generated Drawable in the image above:
<ripple android:color="#android:color/black" xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="#drawable/background"></item>
</ripple>
What I have tried
I now tried to make a RippleDrawable in combination with a GradientDrawable to recreate the XML above in code. Very easy, I thought:
GradientDrawable gradientDrawable = new GradientDrawable();
gradientDrawable.setColor(Color.TRANSPARENT);
gradientDrawable.setStroke(mStroke, Color.GREEN);
gradientDrawable.setCornerRadius(getHeight() / 2);
ColorStateList rippleStateList = ColorStateList.valueOf(Color.BLUE);
return new RippleDrawable(rippleStateList, gradientDrawable, gradientDrawable);
This returns a simple drawable with a green stroke. However, when you click it, nothing happens. Unlike the XML where you clearly can see a ripple effect over the border. When I set the third parameter to null, same effect. When I only set the second parameter to null it only returned an empty transparant drawable without any ripple effects.
You should use a RippleDrawable as the background of your custom button. The RippleDrawable has a mask layer, which is what you are looking for. To do this programmatically, you can create a RippleDrawable like so:
float[] outerRadii = new float[8];
Arrays.fill(outerRadii, height / 2);
RoundRectShape shape = new RoundRectShape(outerRadii, null, null);
ShapeDrawable mask = new ShapeDrawable(shape);
ColorStateList stateList = ColorStateList.valueof(ContextCompat.getColor(getContext(), R.color.ripple));
setBackground(new RippleDrawable(stateList, getBackgroundDrawable(), mask);
Keep in mind that RippleDrawable is only available for API Level 21+, so if you are looking to support older versions as well you will need to fall back to something else; possibly android.R.attr.selectableItemBackground or a StateListDrawable, which will give your button a basic "pressed" background for older platform versions.

How to combine shape drawable and drawable into single drawable

I am generating shape drawable and drawable runtime in android programmatically. What all I need is to combine two drawable into single drawable. I tried to implement through following methods, but nothing seems to work out.
Sample code to combine two drawables into single using LayerDrawable
public static LayerDrawable drawCircleWithIcon (Context context, int width, int height, int color,Drawable drawable) {
ShapeDrawable oval = new ShapeDrawable (new OvalShape ());
oval.setIntrinsicHeight (height);
oval.setIntrinsicWidth (width);
oval.getPaint ().setColor (color);
Drawable[] layers = new Drawable[2];
layers[0] = drawable;
layers[1] = oval;
LayerDrawable composite1 = new LayerDrawable (layers);
return composite1;
}
Arguments I am passing:
width - width of the circle
height - height of the circle
color - color of the circle
drawable - icon that needs to be fit inside the ShapeDrawable (i.e. Round circle inside placed with icon)
My requirement:
I need to combine two drawables (one is ShapeDrawable and drawable). The output must be like as following
Kindly please help me with your solutions or alternate methods to merge two drawable into one drawable icon. Thanks in advance.
You can try using an imageview an set the properties backgroundDrawable and imageDrawable or imageResource.
yourImageView.setBackgroundDrawable(yourShape);
YourImageView.setImageDrawable(yourImage);
To adjust the drawable and keep aspect ratio try using, his methods.
yourImageView.setAdjustViewBounds(true);
yourImageView.setScaleType(ScaleType.FIT_CENTER);
Due to this answer does't adjust to the question, if what you want is to create a single image combining multiple drawables, you can try something like this:
Resources res = getResources();
// Front image
Bitmap image = BitmapFactory.decodeResource(res, R.drawable.frontImage);
// The rounded background
Bitmap background = BitmapFactory.decodeResource(res, shapeDrawable);
RoundedBitmapDrawable dr = RoundedBitmapDrawableFactory.create(res, background);
dr.setCornerRadius(Math.max(background.getWidth(), background.getHeight()) / 2.0f);
Bitmap combined = Bitmap.createBitmap(dr.getWidth(), dr.getHeight(), Bitmap.Config.ARGB_8888);
// Creates the combined drawable
Canvas canvas = new Canvas(combined);
canvas.drawBitmap(dr,0, 0, null);
canvas.drawBitmap(image, dr.getWidht() / 2.0f, dr.getHeight() / 2.0f, null);
I have not tried the code above, but i'm showing something could help you.

setColorFilter doesn't seem to work on kitkat

I'm attempting to colorize a graphic using setColorFilter. The following code seems to work fine on lollipop, but it seems to have no effect on kitkat, the icon is rendered in it's original colors:
Drawable icon = ContextCompat.getDrawable(context, R.drawable.ic_chat_button).mutate();
icon.setColorFilter(context.getResources().getColor(R.color.control_tint_color), PorterDuff.Mode.SRC_ATOP);
icon.invalidateSelf();
The mutate and invalidateSelf calls don't seem to have any effect on the problem here, just leaving them in as an example of part of what's been tried to figure out what's going on.
FWIW, I'm using the drawable as part of a LayerDrawable in a StateListDrawable that gets used as either the background for a button or as the drawable for an ImageView The results are consistent (ie., wrong on kitkat) either way. I've also tried putting the icon drawable directly into the StateListDrawable again with no change in behavior. In all cases, it works fine on lollipop, but doesn't work on kitkat.
As an experiment, I took the tinted Drawable out of the StateListDrawable but not the LayerDrawable and it works as expected. Apparently there's something flawed in KitKat's implementation of StateListDrawable that prevents it from working, that has been remedied in later versions.
Ultimately, it seems like the problem is that KitKat doesn't support using a ColorFilter (or implicitly an alpha) on a Drawable that will in turn be in a StateListDrawable. My solution was to use the same to code to construct the complex Drawable and then render that into a simple BitMapDrawable:
static Drawable createDrawable(Context context, int color, boolean disabled) {
OvalShape oShape = new OvalShape();
ShapeDrawable background = new ShapeDrawable(oShape);
background.getPaint().setColor(color);
ShapeDrawable shader = new ShapeDrawable(oShape);
shader.setShaderFactory(new ShapeDrawable.ShaderFactory() {
#Override
public Shader resize(int width, int height) {
return new LinearGradient(0, 0, 0, height,
new int[]{
Color.WHITE,
Color.GRAY,
Color.DKGRAY,
Color.BLACK
}, null, Shader.TileMode.REPEAT);
}
});
Drawable icon = ContextCompat.getDrawable(context, R.drawable.ic_chat_button).mutate();
icon.setColorFilter(context.getResources().getColor(R.color.control_tint_color), PorterDuff.Mode.SRC_IN);
Drawable layer = new LayerDrawable(new Drawable[]{ shader, background, icon });
layer.setAlpha(disabled ? 128 : 255);
// Note that on KitKat, setting a ColorFilter on a Drawable contained in a StateListDrawable
// apparently doesn't work, although it does on later versions, so we have to render the colored
// bitmap into a BitmapDrawable and then put that into the StateListDrawable
Bitmap bitmap = Bitmap.createBitmap(icon.getIntrinsicWidth(), icon.getIntrinsicHeight(), Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(bitmap);
layer.setBounds(0, 0, layer.getIntrinsicWidth(), layer.getIntrinsicHeight());
layer.draw(canvas);
return new BitmapDrawable(context.getResources(), bitmap);
}
Rather than attach the coloring to something like "disabled" state (as in the accepted answer) I found the answer to be simpler by focusing on recoloring and let my usage leverage how to include the now-tinted image in the StateListDrawable. (And FYI, I've tried to translate from the Xamarin C# I'm using, but the below code may not complie correctly as Java)
static Drawable recolorDrawable(Drawable icon, int toColor)
{
Bitmap bitmap = Bitmap.createBitmap(icon.getIntrinsicWidth(), icon.getIntrinsicHeight(), Bitmap.Config.ARGB_8888);
Canvas myCanvas = new Canvas(bitmap);
icon.setColorFilter(toColor, PorterDuff.Mode.SRC_IN);
icon.setBounds(0, 0, icon.getIntrinsicWidth(), icon.getIntrinsicHeight());
icon.draw(myCanvas);
return new BitmapDrawable(context.getResources(), bitmap);
}
Finally, in all fairness to the accepted answer, I'm rather foreign to Android development and can thank him for showing the pieces I needed before then simplifying them.

Creating bitmap from a drawable

I am going to be working on an app which requires drag/drop on a Canvas. Basically, I want to take a ShapeDrawable and convert it into a Bitmap which I can have the user drag around the screen. This is a simple exercise in itself.
However, I want to add text inside my shape. Is there a way I can add text to the drawable itself then convert to a bitmap? I looked into creating a TextView with a drawable as a background.
Is this the best way to do it? I sort of want to avoid creating TextViews in my code. Any advice is appreciated.
Edit 2/21/2013:
In response to JustDanyul's post I have the following code:
int width = 40;
int height = 40;
Bitmap.Config config = Bitmap.Config.ARGB_8888;
bitmap = Bitmap.createBitmap(width, height, config);
Canvas canvas = new Canvas(bitmap);
Resources res = context.getResources();
Drawable shape = res.getDrawable(R.drawable.miss_scarlet);
shape.draw(canvas);
Paint paint = new Paint();
paint.setTextSize(fontSize);
paint.setColor(Color.BLACK);
canvas.drawText(gameToken.getDbName(), 5, 5, paint);
My drawable is not showing up when I draw the bitmap on another canvas. The drawable itself is fine (I tested it as a background to a TextView). The text shows up. Am i missing something in this code?
<?xml version="1.0" encoding="utf-8"?>
<shape
xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<corners
android:radius="4dp" />
<solid
android:color="#FF0000" />
<stroke
android:width="3dp"
android:color="#000000" />
</shape>
Edit #2 2/21/2013:
I added:
shape.setBounds(0, 0, bitmap.getWidth(), bitmap.getHeight());
to my code and now the drawable appears but my text is gone (or just hidden).
I would suggest you to try something like this, first, create an empty bitmap:
int w = 500
int h = 500; // or whatever sizes you need
Bitmap.Config config = Bitmap.Config.ARGB_8888;
Bitmap bitmap = Bitmap.createBitmap(w, h, config);
Next step, create a new canvas instance, which renders onto your newly created bitmap
Canvas canvas = new Canvas(bitmap);
Now, you can draw the ShapeDrawable onto your empty bitmap using the ShapeDrawable's draw method
myshapedrawable.draw(canvas);
Finally, you can use the drawText method of the canvas instance to draw your text on the canvas.

How to make a Drawable object with my selected color in code

I am trying to set icon of selected color to a preference:
Preference prf = (Preference) findPreference("SelectColorPref");
prf.setIcon(Drawbale icon);
For this i need an object Drawable of selected color.
Is this possible to make a Drawable icon in java code? Please guide me..
Regards,
///////////////////////////////////////////////////////////
After following Aleks G's concept it solved as:
Preference TextClrPref = (Preference) findPreference("text_color_preference");
Bitmap bm = Bitmap.createBitmap(30, 30, Bitmap.Config.ARGB_8888);
Canvas cnv = new Canvas(bm);
int red = 0xffff0000;
cnv.drawColor(red);
Drawable drawable = new BitmapDrawable(bm);
TextClrPref .setIcon(drawable);
You should be able to create a solid-colour Drawable using code similar to this:
Bitmap bm = BitmapFactory.createBitmap(50, 50, Bitmap.Config.ARGB_8888);
Canvas cnv = new Canvas(bm);
int red = 0xff0000;
cnv.drawColor(red);
Drawable drawable = new BitmapDrawable(bm);
This will create a Drawable containing a 50x50 pixels red square.
(Please note that I haven't tested this code, but I use something similar in my code.)
the easiest way using ShapeDrawable
ShapeDrawable sdrawable = new ShapeDrawable(new RectShape());
sdrawable.paint.Color = color.RED;
sdrawable.setIntrinsicWidth(10);
sdrawable.setIntrinsicHeight(40);

Categories

Resources