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.
Related
I'm trying to make a drop shadow blur effect WITH COLOR on a rectangular layout view. I've tried to use this code but to no avail.
int glowRadius = 14;
int glowColor = Color.parseColor("#acc5fe");
Paint paint = new Paint();
paint.setColor(glowColor);
paint.setMaskFilter(new BlurMaskFilter(glowRadius, BlurMaskFilter.Blur.OUTER));
RectF rectF = new RectF(mRootView.getHeight(), mRootView.getWidth(),
mRootView.getHeight(), mRootView.getWidth());
Canvas canvas = new Canvas();
canvas.drawRect(rectF, paint);
mRootView.draw(canvas);
It doesn't seem to do anything though.
I also tried to use shadowDx and shadowDy etc. but that does nothing.
How to add a blurred drop shadow to a button? is a very similar question but I don't think the 9-patch is a viable solution since it's a layout and not an image.
How do you create a drop outer shadow blur effect on Android?
Update
Still haven't found a successful answer. I want something like
where the shadow effect is on the email edittext with a different color than black.
try following A library for supporting convex material shadows
https://github.com/harjot-oberai/MaterialShadows
What I understand from your question is first you want to drop a shadow. Here is what you can do to drop a shadow.
Just add this to your parent layout in xml.
android:background="#android:drawable/dialog_holo_light_frame"
Check this answer for more information.
how to add shadow effect in alert dialog box in android
Let me know if this works for you.
Edit:
Please check this answer. This might help you.
Extending Android View class to add a dropshadow
It can be achieved using NinePatchDrawable. Below is a simple example.
protected NinePatchDrawable bg;
protected Paint paint;
protected Rect padding = new Rect();
protected Bitmap bmp;
protected void init() {
// decode the 9patch drawable
bg = (NinePatchDrawable) getResources().getDrawable(R.drawable.balloon);
// get paddings from the 9patch and apply them to the View
bg.getPadding(padding);
setPadding(padding.left, padding.top, padding.right, padding.bottom);
// prepare the Paint to use below
paint = new Paint();
paint.setAntiAlias(true);
paint.setColor(Color.rgb(255,255,255));
paint.setStyle(Style.FILL);
// this check is needed in order to get this code
// working if target SDK>=11
if( Build.VERSION.SDK_INT >= 11 )
setLayerType(View.LAYER_TYPE_SOFTWARE, paint);
// set the shadowLayer
paint.setShadowLayer(
padding.left * .2f, // radius
0f, // blurX
padding.left * .1f, // blurY
Color.argb(128, 0, 0, 0) // shadow color
);
}
Also please try this color:
Color.argb(128, 0, 0, 0)
I have a drawable created beforehand which is a shape of rectangle but is fully transparent. Now I would like to assign this drawable to the Button in code but also set the Color of this drawable from transparent to some specific color like Orange etc.
I have already tried setting the same using some other posts like -
Drawable mDrawable = ContextCompat.getDrawable(this, R.drawable.square_transparent);
mDrawable.setColorFilter(
new PorterDuffColorFilter(
Color.Orange, Mode.SRC_IN)
);
but it doesn't work. When the activity renders the button ,it is still transparent only.
I also tried explicitly setting the mDrawable.setAlpha to 255 (fully opaque) before assigning the drawable to the button, but even that doesn't work.
Please suggest, if anyone has this working in some other fashion.
Use Mode.SRC instead of Mode.SRC_IN.
See the PorterDuff modes for more details.
Finally I got the end results using a 2 step solution -
I fixed the drawable from being a transparent one to pure white one with no opacity (it seems the color Filtering / tinting, works best on whites)
I also used the below lines of code to do the trick -
Drawable drawable = ResourcesCompat.getDrawable(context.getResources(), R.drawable.yourcustomshape, null);
drawable = DrawableCompat.wrap(drawable);
DrawableCompat.setTintList(drawable, ColorStateList.valueOf(Color.BLUE)); // Can be any color you need to color the shape
Using Two Methods You can Set background color and Border
This For Without background Color
public static GradientDrawable backgroundWithoutBorder(int color) {
GradientDrawable gdDefault = new GradientDrawable();
gdDefault.setColor(color);
gdDefault.setCornerRadii(new float[] { radius, radius, 0, 0, 0, 0,
radius, radius });
return gdDefault;
}
This For With background Color
public static GradientDrawable backgroundWithBorder(int bgcolor,
int brdcolor) {
GradientDrawable gdDefault = new GradientDrawable();
gdDefault.setColor(bgcolor);
gdDefault.setStroke(2, brdcolor);
gdDefault.setCornerRadii(new float[] { radius, radius, 0, 0, 0, 0,
radius, radius });
return gdDefault;
}
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.
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.
Currently I am using this code to add color:
ShapeDrawable drawable = new ShapeDrawable(new OvalShape());
drawable.getPaint().setColor(color);
Now I need to apply some gradient colors to it along with stroke(like border with different color). I am setting this as background to button.
Here is what I am expecting, I need to do it programmatically.
Add a RadialGradient to your drawable like this:
Shader shader = new RadialGradient(x, y, radius, color0, color1, Shader.TileMode.REPEAT);
drawable.getPaint().setShader(shader);
Obviously, you can interchange LinearGradient, SweepGradient, and any of the parameters.
Here is how to add the stroke:
drawable.getPaint().setStrokeWidth(3);
drawable.getPaint().setColor(Color.WHITE);
drawable.getPaint().setStyle(Paint.Style.FILL_AND_STROKE);
Hmmm, I think I have to defer to #StinePike with the GradientDrawable:
GradientDrawable gd = new GradientDrawable();
gd.setColor(Color.RED);
gd.setCornerRadius(10);
gd.setStroke(2, Color.WHITE);
gd.setShape(GradientDrawable.OVAL);
use GradientDrawable to create gradient
or
you can see this answer