I have a layout border_bottom_black_bluegrey_background.xml
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android" >
<item>
<shape android:shape="rectangle" >
<stroke
android:width="1dp"
android:color="#color/black" />
</shape>
</item>
<item android:bottom="1dp">
<shape android:shape="rectangle" >
<solid android:color="#color/blue_grey" />
</shape>
</item>
</layer-list>
I'm trying to reuse this layout so that I don't have to create one for each different border and background color.
I know this can be accomplished with styles and attrs, I had a look on the internet for good tutorial but with no avail.
This is what i've got so far:
attrs.xml
<resources>
<declare-styleable>
<attr name="backgroundColor" format="reference"/>
<attr name="borderColor" format="reference"/>
</declare-styleable>
</resources>
styles.xml
<style name="BorderBottom">
<item name="android:background">#drawable/border_bottom</item>
</style>
<style name="BlackBorderBlueGreyBackground" parent="BorderBottom">
<item name="borderColor">#color/black</item>
<item name="backgroundColor">#color/blue_grey</item>
</style>
border_bottom.xml
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android" >
<item>
<shape
android:shape="rectangle">
<stroke android:width="1dp" android:color="#color/black" />
<solid android:color="?attr/borderColor" />
</shape>
</item>
<item android:bottom="1dp">
<shape
android:shape="rectangle">
<solid android:color="?attr/backgroundColor" />
</shape>
</item>
</layer-list>
My implementation of the style
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
style="#style/BlackBorderBlueGreyBackground"
android:orientation="vertical" >
</RelativeLayout>
Can someone point me in the right direction?
I think the issue is that when you use the attrs.xml that is for use in creating custom views. The problem I see with your code above is that border_bottom.xml is pointing to color attributes that have not been explicitly declared and it has no way of knowing what color it should be. If want to create a layout that you can continuously use my suggestion would be to create a class that creates backgrounds for you and if need be you can just save the drawable created. This is a class that I use to draw underlines under views I think it should suffice.
public final class Background extends ShapeDrawable {
public final static int RECTANGLE = 0x00;
public final static int ROUND_RECTANGLE = 0x01;
public final static int UNDERLINE = 0x02;
//For future use
//public final static int CIRCLE = 0x03;
private int shape_type;
private final Paint fill_paint, stroke_paint;
private int stroke_width;
private static RectShape rectangle = new RectShape();
private static RoundRectShape round_rectangle;
//private static OvalShape circle = new OvalShape();
public static final Background newInstance(int shape, int background_color, int stroke_color, int stroke_width) {
switch (shape) {
case UNDERLINE:
case RECTANGLE:
return new Background(shape, rectangle, background_color, stroke_color, stroke_width);
case ROUND_RECTANGLE:
round_rectangle = new RoundRectShape(new float[] { stroke_width, stroke_width,
stroke_width, stroke_width, stroke_width, stroke_width, stroke_width, stroke_width }, null, null);
return new Background(shape, round_rectangle, background_color, stroke_color, stroke_width);
default:
return null;
}
}
private Background(int shape_type, Shape shape, int fill_color, int stroke_color, int stroke_width) {
super(shape);
this.shape_type = shape_type;
this.stroke_width = stroke_width;
//set the fill_paint
fill_paint = new Paint(getPaint());
fill_paint.setColor(fill_color);
stroke_paint = new Paint(fill_paint);
//set the stroke paint
stroke_paint.setStyle(Paint.Style.STROKE);
stroke_paint.setAntiAlias(false);
stroke_paint.setStrokeWidth(stroke_width);
stroke_paint.setColor(stroke_color);
}
#Override
protected void onDraw(Shape shape, Canvas canvas, Paint paint) {
switch(shape_type) {
case UNDERLINE:
final RectF underline = new RectF(getBounds().left - stroke_width, getBounds().top - stroke_width,
getBounds().right + stroke_width,
getBounds().bottom - stroke_width);
canvas.drawRect(underline, stroke_paint);
break;
case RECTANGLE:
final RectF rectangle = new RectF(stroke_width, stroke_width, getBounds().right - stroke_width,
getBounds().bottom - stroke_width);
canvas.drawRect(rectangle, fill_paint);
canvas.drawRect(rectangle, stroke_paint);
break;
}
}
}
Then you can just save the drawable created and use it on multiple layouts in code.
//untested code
Layout layout = (RelativeLayout)findViewById(R.id.your_layout_id);
Background background = Background.newInstance(Background.RECTANGLE, Color.GRAY, Color.BLUE, 1);
layout.setBackground(background);
Related
I am trying to have a layout background drawable, which will be only gradient underline with 1-2 dp height and rest is transparent, so the upper part will have the parent's background.
Here is what I have.
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android>
<!-- underline color -->
<item>
<shape>
<gradient
android:startColor="#color/colorPrimaryDark"
android:endColor="#FFFFFFFF"
android:centerY="0.5"
android:angle="0"/>
</shape>
</item>
<!-- main color -->
<item android:bottom="2.5dp">
<shape android:shape="rectangle">
<solid android:color="#color/white" />
<padding
android:top="4dp"
android:bottom="4dp" />
</shape>
</item>
If I change the solid color in "main color" to transparent, whole background will be using "underline color" settings.
The technique you use to create a line on the bottom of the view works if the color of the layer overlaying the gradient layer is opaque. What you are trying to do is to apply a transparent layer that replaces (erases) the underlying gradient. That is not how it works: A transparent overlay leaves the underlying color, here a gradient, untouched.
Here is an alternate layer-list drawable that you can use for API 23+:
underline_drawable.xml
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item android:gravity="bottom">
<shape>
<size android:height="2dp" />
<gradient
android:angle="0"
android:centerY="0.5"
android:endColor="#FFFFFFFF"
android:startColor="#color/colorPrimaryDark" />
</shape>
</item>
</layer-list>
Here is what it looks like:
Prior to API 23, you can use the following custom drawable, but it must be set in code.
GradientUnderline.java
public class GradientUnderline extends Drawable {
private Shader mShader;
private final Paint mPaint;
private int mHeight = -1;
private int mStartColor = Color.BLACK;
private int mEndColor = Color.WHITE;
private int mLastWidth;
public GradientUnderline() {
mPaint = new Paint();
}
public GradientUnderline(int lineHeight, int startColor, int endColor) {
mPaint = new Paint();
mHeight = lineHeight;
mStartColor = startColor;
mEndColor = endColor;
}
#Override
public void draw(#NonNull Canvas canvas) {
if (mShader == null || getBounds().width() != mLastWidth) {
mLastWidth = getBounds().width();
mShader = new LinearGradient(0, 0, getBounds().width(), mHeight, mStartColor,
mEndColor, Shader.TileMode.CLAMP);
mPaint.setShader(mShader);
}
canvas.drawRect(0, getBounds().height() - mHeight, getBounds().width(),
getBounds().height(), mPaint);
}
#Override
public void setAlpha(int alpha) {
}
#Override
public void setColorFilter(#Nullable ColorFilter colorFilter) {
}
#Override
public int getOpacity() {
return PixelFormat.OPAQUE;
}
}
I missed the availability of android:gravity initially because it is not mentioned on the "Drawable Resources" page. It is mentioned, however, in the LayerDrawable documentation.
Why problem occurs: Shape at first item will draw the gradient in entire region. After setting colour to second item will hide the top item region except ay 2.5dp at bottom. So whenever you set transparent colour to second item it automatically show the top level item that is gradient region..
Here i suggest the way to use but you can set to fixed height in view.
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item android:top="47dp">
<shape>
<gradient
android:startColor="#color/colorPrimaryDark"
android:endColor="#FFFFFFFF"
android:centerY="0.5"
android:angle="0"/>
</shape>
</item>
</layer-list>
View.xml
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="50dp"
android:background="#drawable/bottom_line">
</RelativeLayout>
Change size according to your needs..!
OUTPUT
I created an Android gradient drawable where the top and bottom are black and the center is transparent:
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle" >
<gradient
android:startColor="#android:color/black"
android:centerColor="#android:color/transparent"
android:endColor="#android:color/black"
android:angle="90"/>
</shape>
The rendered gradient looks like this:
As you can see, the black parts spread to most of the screen. I want the black to show only on a small portion of the top and bottom. Is there a way I can make the transparent center larger, or make the top and bottom black stripes smaller?
I tried playing around with some of the other XML attributes mentioned in the linked GradientDrawable documentation, yet none of them seem to make and difference.
For an XML only solution, you can create a layer-list with two separate gradient objects.
The following code creates two overlapping gradient objects and uses centerY with centerColor to offset the black section. Here, the centerY attributes are set to 0.9 and 0.1, so the black color is restricted to the top and bottom 10% of the view height.
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item>
<shape>
<gradient
android:angle="90"
android:centerColor="#android:color/transparent"
android:centerY="0.9"
android:endColor="#android:color/black"
android:startColor="#android:color/transparent" />
</shape>
</item>
<item>
<shape>
<gradient
android:angle="90"
android:centerColor="#android:color/transparent"
android:centerY="0.1"
android:endColor="#android:color/transparent"
android:startColor="#android:color/black" />
</shape>
</item>
</layer-list>
For API level 23 or higher, the following solution will also work, using android:height. This solution can work even if you don't know the total height of your view, as long as you know how large you want the gradient to be.
This code creates two separate gradients, each with a height of 60sp, and then uses android:gravity to float the gradients to the top and bottom of the view.
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:height="60sp"
android:gravity="top">
<shape android:shape="rectangle">
<gradient
android:angle="90"
android:endColor="#android:color/black"
android:startColor="#android:color/transparent" />
</shape>
</item>
<item
android:height="65sp"
android:gravity="bottom">
<shape android:shape="rectangle">
<gradient
android:angle="90"
android:endColor="#android:color/transparent"
android:startColor="#android:color/black" />
</shape>
</item>
</layer-list>
Thank you #Luksprog for the code help, and #thenaoh for the start of the idea.
The above solutions work and it is nice that they are pure XML. If your gradient is showing with stripes, you may want to try a programmatic solution, like shown in #lelloman's answer, to create a smoother gradient.
Here is how it could be done with a custom Drawable. You can tune the LinearGradient as you want, and then set it as the view's background with view.setBackground(new CustomDrawable());.
public class CustomDrawable extends Drawable {
private Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
private int[] colors;
private float[] positions;
public CustomDrawable() {
paint.setStyle(Paint.Style.FILL);
this.colors = new int[]{0xff000000, 0xffaaaaaa, 0xffffffff, 0xffaaaaaa, 0xff000000};
this.positions = new float[]{.0f, .2f, .5f, .8f, 1.f};
}
#Override
public void setBounds(int left, int top, int right, int bottom) {
super.setBounds(left, top, right, bottom);
LinearGradient linearGradient = new LinearGradient(left, top,left, bottom, colors, positions, Shader.TileMode.CLAMP);
paint.setShader(linearGradient);
}
#Override
public void draw(#NonNull Canvas canvas) {
canvas.drawRect(getBounds(), paint);
}
#Override
public void setAlpha(#IntRange(from = 0, to = 255) int alpha) {
paint.setAlpha(alpha);
}
#Override
public void setColorFilter(#Nullable ColorFilter colorFilter) {
paint.setColorFilter(colorFilter);
}
#Override
public int getOpacity() {
return PixelFormat.TRANSPARENT;
}
}
There is a solution, assuming that you know in advance the height of your view (let's say here 60dp):
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:bottom="40dp">
<shape android:shape="rectangle">
<gradient
android:type="linear"
android:angle="90"
android:startColor="#FFFFFF"
android:endColor="#000000"/>
</shape>
</item>
<item
android:top="20dp"
android:bottom="20dp"
android:gravity="center_vertical">
<shape android:shape="rectangle">
<solid android:color="#FFFFFF"/>
</shape>
</item>
<item
android:top="40dp"
android:gravity="bottom">
<shape android:shape="rectangle">
<gradient
android:type="linear"
android:angle="90"
android:startColor="#000000"
android:endColor="#FFFFFF"/>
</shape>
</item>
</layer-list>
But if you don't know the height in advance, another solution would be to make your own custom view, like this:
public class MyView extends ImageView
{
private Paint paint = null;
public MyView(Context context, AttributeSet attrs)
{
super(context, attrs);
paint = new Paint(Paint.ANTI_ALIAS_FLAG);
}
#Override
protected void onDraw(Canvas canvas)
{
super.onDraw(canvas);
paint.setShader(getLinearGradient(0, getHeight()));
canvas.drawPaint(paint);
}
private LinearGradient getLinearGradient(float y0, float y1)
{
// colors :
int[] listeColors = new int[3];
listeColors[0] = 0xFF000000;
listeColors[1] = 0xFFFFFFFF;
listeColors[2] = 0xFFFFFFFF;
// positions :
float[] listPositions = new float[3];
listPositions[0] = 0;
listPositions[1] = 0.25F;
listPositions[2] = 1;
// gradient :
return new LinearGradient(0, y0, 0, y0 + (y1 - y0) / 2, listeColors, listPositions, Shader.TileMode.MIRROR);
}
}
Hope it helps.
Currently, the ImageButton have set android:background="#drawable/mem_btn",
mem_btn.xml
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_pressed="true" >
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<stroke android:width="0dp" android:color="#color/black" />
<solid android:color="#color/pressed"/>
<padding android:left="5dp" android:top="5dp"
android:right="5dp" android:bottom="5dp" />
<corners android:radius="14dp" />
</shape>
</item>
<item>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<stroke android:width="0dp" android:color="#color/black" />
<solid android:color="#3366CC"/>
<padding android:left="5dp" android:top="5dp"
android:right="5dp" android:bottom="2dp" />
<corners android:radius="14dp" />
</shape>
</item>
</selector>
The above works perfectly.
Question:
I would like to let the user change the background color: i.e. other colors upon users' choice, yet with the pressed_color, radius remain unchanged.
In this case, how to set the information in the xml programmatically such that the color for the non-pressed state is a variable?
Thanks!
Thanks Ranjit Pati for the reference, such that I can further researched on right track and found out StateListDrawable, and the following works perfectly:
public void set_buttons(int t, int color_idd)
{
ShapeDrawable activeDrawable = new ShapeDrawable();
ShapeDrawable inactiveDrawable = new ShapeDrawable();
// The corners are ordered top-left, top-right, bottom-right, bottom-left. // For each corner, the array contains 2 values, [X_radius, Y_radius]
float[] radii = new float[8];
for (int i = 0; i <= 7; i++)
{
radii[i] = (int) getResources().getDimension(R.dimen.footer_corners);
}
inactiveDrawable.setShape(new RoundRectShape(radii, null, null));
inactiveDrawable.getPaint().setColor(color_idd);
activeDrawable.setShape(new RoundRectShape(radii, null, null));
activeDrawable.getPaint().setColor( (Color.parseColor ("#008B00")));
StateListDrawable states = new StateListDrawable();
states.addState(new int[] {-android.R.attr.state_enabled}, inactiveDrawable);
states.addState(new int[] {android.R.attr.state_pressed}, activeDrawable);
btns[t].setBackground(states);
}
if I got your main problem point correctly, this custom button solve your problem.
public class customButton extends Button{
int dColor, pColor;
public customButton(Context context, int defaultColor, final int pressedColor) {
super(context);
dColor = defaultColor;
pColor = pressedColor;
this.setBackgroundDrawable(getShape(defaultColor));
this.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
v.setBackgroundDrawable(getShape(pressedColor));
}
});
}
//A method that return a single color shape with radius corner
private Drawable getShape(int color){
GradientDrawable gradientDrawable = new GradientDrawable(GradientDrawable.Orientation.TL_BR, new int[] { color,
color, color});
gradientDrawable.setGradientType(GradientDrawable.RECTANGLE);
float[] f = {1,1,1,1,1,1,1,1}; //set the radius as you like
gradientDrawable.setCornerRadii(f);
return gradientDrawable;
}
//A method that change the default and pressed color of the button
public void changeColor(int defaultColor, int pressedColor){
this.setBackgroundDrawable(getShape(defaultColor));
pColor = pressedColor;
}
}
I'd like to create a rectangle shape with two solid colors (horizontally) to achieve something like this:
I heard about layer-list, i though i could use it to contains two rectangle with a different color but it seems that it only lays shapes vertically.
Is there a way to achieve this using lalyer-list or should i use something totally different? I'd like to keep it simple with ability to change the shape colors at runtime.
Thanks.
this will surely draw the shape as per your Requirement :
Adjust size of <item> as you need !
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android" >
<item
android:left="50dip">
<shape
xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle" >
<solid android:color="#0000FF" />
</shape>
</item>
<item android:right="50dip">
<shape
xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle" >
<solid android:color="#ff0000" />
</shape>
</item>
</layer-list>
You can create custom drawable for this. Just extend Drawable class.
Here is a sample code which draws a rectangle like you wanted, you can provide any number of colors.
public class ColorBarDrawable extends Drawable {
private int[] themeColors;
public ColorBarDrawable(int[] themeColors) {
this.themeColors = themeColors;
}
#Override
public void draw(Canvas canvas) {
// get drawable dimensions
Rect bounds = getBounds();
int width = bounds.right - bounds.left;
int height = bounds.bottom - bounds.top;
// draw background gradient
Paint backgroundPaint = new Paint();
int barWidth = width / themeColors.length;
int barWidthRemainder = width % themeColors.length;
for (int i = 0; i < themeColors.length; i++) {
backgroundPaint.setColor(themeColors[i]);
canvas.drawRect(i * barWidth, 0, (i + 1) * barWidth, height, backgroundPaint);
}
// draw remainder, if exists
if (barWidthRemainder > 0) {
canvas.drawRect(themeColors.length * barWidth, 0, themeColors.length * barWidth + barWidthRemainder, height, backgroundPaint);
}
}
#Override
public void setAlpha(int alpha) {
}
#Override
public void setColorFilter(ColorFilter cf) {
}
#Override
public int getOpacity() {
return PixelFormat.OPAQUE;
}
}
This will give you two colors half and half vertically. Put this code in a drawable resource.
<item
android:top="320dip">
<shape
xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle" >
<solid android:color="#color/red" />
</shape>
</item>
<item android:bottom="320dip">
<shape
xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle" >
<solid android:color="#color/yellow" />
</shape>
</item>
How can I show shadow for my linear layout. I want white colored rounded background with shadow around the linearlayout. I have done this so far.
<LinearLayout
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_margin="10dp"
android:background="#xml/rounded_rect_shape"
android:orientation="vertical"
android:padding="10dp">
<-- My buttons, textviews, Imageviews go here -->
</LinearLayout>
And rounded_rect_shape.xml under xml directory
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle" >
<solid android:color="#ffffff" />
<corners
android:bottomLeftRadius="3dp"
android:bottomRightRadius="3dp"
android:topLeftRadius="3dp"
android:topRightRadius="3dp" />
</shape>
There is also another solution to the problem by implementing a layer-list that will act as the background for the LinearLayoout.
Add background_with_shadow.xml file to res/drawable. Containing:
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item >
<shape
android:shape="rectangle">
<solid android:color="#android:color/darker_gray" />
<corners android:radius="5dp"/>
</shape>
</item>
<item android:right="1dp" android:left="1dp" android:bottom="2dp">
<shape
android:shape="rectangle">
<solid android:color="#android:color/white"/>
<corners android:radius="5dp"/>
</shape>
</item>
</layer-list>
Then add the the layer-list as background in your LinearLayout.
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="#drawable/background_with_shadow"/>
Well, this is easy to achieve .
Just build a GradientDrawable that comes from black and goes to a transparent color, than use parent relationship to place your shape close to the View that you want to have a shadow, then you just have to give any values to height or width .
Here is an example, this file have to be created inside res/drawable , I name it as shadow.xml :
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<gradient
android:startColor="#9444"
android:endColor="#0000"
android:type="linear"
android:angle="90"> <!-- Change this value to have the correct shadow angle, must be multiple from 45 -->
</gradient>
</shape>
Place the following code above from a LinearLayout , for example, set the android:layout_width and android:layout_height to fill_parent and 2.3dp, you'll have a nice shadow effect on your LinearLayout .
<View
android:id="#+id/shadow"
android:layout_width="fill_parent"
android:layout_height="2.3dp"
android:layout_above="#+id/id_from_your_LinearLayout"
android:background="#drawable/shadow">
</View>
Note 1: If you increase android:layout_height more shadow will be shown .
Note 2: Use android:layout_above="#+id/id_from_your_LinearLayout" attribute if you are placing this code inside a RelativeLayout, otherwise ignore it.
Hope it help someone.
There is no such attribute in Android, to show a shadow. But possible ways to do it are:
Add a plain LinearLayout with grey color, over which add your actual layout, with margin at bottom and right equal to 1 or 2 dp
Have a 9-patch image with a shadow and set it as the background to your Linear layout
For lollipop and above you can use elevation.
For older versions:
Here is a lazy hack from:
http://odedhb.blogspot.com/2013/05/android-layout-shadow-without-9-patch.html
(toast_frame does not work on KitKat, shadow was removed from toasts)
just use:
android:background="#android:drawable/toast_frame"
or:
android:background="#android:drawable/dialog_frame"
as a background
examples:
<TextView
android:layout_width="fill_parent"
android:text="I am a simple textview with a shadow"
android:layout_height="wrap_content"
android:textSize="18sp"
android:padding="16dp"
android:textColor="#fff"
android:background="#android:drawable/toast_frame"
/>
and with different bg color:
<LinearLayout
android:layout_height="64dp"
android:layout_width="fill_parent"
android:gravity="center"
android:background="#android:drawable/toast_frame"
android:padding="4dp"
>
<Button
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:text="Button shadow"
android:background="#33b5e5"
android:textSize="24sp"
android:textStyle="bold"
android:textColor="#fff"
android:layout_gravity="center|bottom"
/>
</LinearLayout>
Try this.. layout_shadow.xml
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item>
<shape android:shape="rectangle">
<solid android:color="#CABBBBBB"/>
<corners android:radius="2dp" />
</shape>
</item>
<item
android:left="0dp"
android:right="0dp"
android:top="0dp"
android:bottom="2dp">
<shape android:shape="rectangle">
<solid android:color="#android:color/white"/>
<corners android:radius="2dp" />
</shape>
</item>
</layer-list>
Apply to your layout like this
android:background="#drawable/layout_shadow"
I know this is old, but most of these answers require a ton of extra code.
If you have a light colored background, you can simply use this:
android:elevation="25dp"
Actually I agree with #odedbreiner but I put the dialog_frame inside the first layer and hide the black background under the white layer.
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:drawable="#android:drawable/dialog_frame"
android:right="2dp" android:left="2dp" android:bottom="2dp" android:top="5dp" >
<shape android:shape="rectangle">
<corners android:radius="5dp"/>
</shape>
</item>
<item>
<shape
android:shape="rectangle">
<solid android:color="#android:color/white"/>
<corners android:radius="5dp"/>
</shape>
</item>
</layer-list>
save this 9.png. (change name it to 9.png)
2.save it in your drawable.
3.set it to your layout.
4.set padding.
For example :
<LinearLayout
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:background="#drawable/shadow"
android:paddingBottom="6dp"
android:paddingLeft="5dp"
android:paddingRight="5dp"
android:paddingTop="6dp"
>
.
.
.
</LinearLayout>
Create a new XML by example named "shadow.xml" at DRAWABLE with the following code (you can modify it or find another better):
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item>
<shape android:shape="rectangle">
<solid android:color="#color/middle_grey"/>
</shape>
</item>
<item android:left="2dp"
android:right="2dp"
android:bottom="2dp">
<shape android:shape="rectangle">
<solid android:color="#color/white"/>
</shape>
</item>
</layer-list>
After creating the XML in the LinearLayout or another Widget you want to create shade, you use the BACKGROUND property to see the efect. It would be something like :
<LinearLayout
android:orientation="horizontal"
android:layout_height="wrap_content"
android:layout_width="match_parent"
android:paddingRight="#dimen/margin_med"
android:background="#drawable/shadow"
android:minHeight="?attr/actionBarSize"
android:gravity="center_vertical">
You can use following class for xml tag:
import android.annotation.SuppressLint;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Bitmap;
import android.graphics.BlurMaskFilter;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.PorterDuff;
import android.graphics.Rect;
import android.os.Build;
import android.support.annotation.FloatRange;
import android.util.AttributeSet;
import android.view.ViewGroup;
import android.view.ViewTreeObserver;
import android.widget.FrameLayout;
import com.webappmate.weeassure.R;
/**
* Created by GIGAMOLE on 13.04.2016.
*/
public class ShadowLayout extends FrameLayout {
// Default shadow values
private final static float DEFAULT_SHADOW_RADIUS = 30.0F;
private final static float DEFAULT_SHADOW_DISTANCE = 15.0F;
private final static float DEFAULT_SHADOW_ANGLE = 45.0F;
private final static int DEFAULT_SHADOW_COLOR = Color.DKGRAY;
// Shadow bounds values
private final static int MAX_ALPHA = 255;
private final static float MAX_ANGLE = 360.0F;
private final static float MIN_RADIUS = 0.1F;
private final static float MIN_ANGLE = 0.0F;
// Shadow paint
private final Paint mPaint = new Paint(Paint.ANTI_ALIAS_FLAG) {
{
setDither(true);
setFilterBitmap(true);
}
};
// Shadow bitmap and canvas
private Bitmap mBitmap;
private final Canvas mCanvas = new Canvas();
// View bounds
private final Rect mBounds = new Rect();
// Check whether need to redraw shadow
private boolean mInvalidateShadow = true;
// Detect if shadow is visible
private boolean mIsShadowed;
// Shadow variables
private int mShadowColor;
private int mShadowAlpha;
private float mShadowRadius;
private float mShadowDistance;
private float mShadowAngle;
private float mShadowDx;
private float mShadowDy;
public ShadowLayout(final Context context) {
this(context, null);
}
public ShadowLayout(final Context context, final AttributeSet attrs) {
this(context, attrs, 0);
}
public ShadowLayout(final Context context, final AttributeSet attrs, final int defStyleAttr) {
super(context, attrs, defStyleAttr);
setWillNotDraw(false);
setLayerType(LAYER_TYPE_HARDWARE, mPaint);
// Retrieve attributes from xml
final TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.ShadowLayout);
try {
setIsShadowed(typedArray.getBoolean(R.styleable.ShadowLayout_sl_shadowed, true));
setShadowRadius(
typedArray.getDimension(
R.styleable.ShadowLayout_sl_shadow_radius, DEFAULT_SHADOW_RADIUS
)
);
setShadowDistance(
typedArray.getDimension(
R.styleable.ShadowLayout_sl_shadow_distance, DEFAULT_SHADOW_DISTANCE
)
);
setShadowAngle(
typedArray.getInteger(
R.styleable.ShadowLayout_sl_shadow_angle, (int) DEFAULT_SHADOW_ANGLE
)
);
setShadowColor(
typedArray.getColor(
R.styleable.ShadowLayout_sl_shadow_color, DEFAULT_SHADOW_COLOR
)
);
} finally {
typedArray.recycle();
}
}
#Override
protected void onDetachedFromWindow() {
super.onDetachedFromWindow();
// Clear shadow bitmap
if (mBitmap != null) {
mBitmap.recycle();
mBitmap = null;
}
}
public boolean isShadowed() {
return mIsShadowed;
}
public void setIsShadowed(final boolean isShadowed) {
mIsShadowed = isShadowed;
postInvalidate();
}
public float getShadowDistance() {
return mShadowDistance;
}
public void setShadowDistance(final float shadowDistance) {
mShadowDistance = shadowDistance;
resetShadow();
}
public float getShadowAngle() {
return mShadowAngle;
}
#SuppressLint("SupportAnnotationUsage")
#FloatRange
public void setShadowAngle(#FloatRange(from = MIN_ANGLE, to = MAX_ANGLE) final float shadowAngle) {
mShadowAngle = Math.max(MIN_ANGLE, Math.min(shadowAngle, MAX_ANGLE));
resetShadow();
}
public float getShadowRadius() {
return mShadowRadius;
}
public void setShadowRadius(final float shadowRadius) {
mShadowRadius = Math.max(MIN_RADIUS, shadowRadius);
if (isInEditMode()) return;
// Set blur filter to paint
mPaint.setMaskFilter(new BlurMaskFilter(mShadowRadius, BlurMaskFilter.Blur.NORMAL));
resetShadow();
}
public int getShadowColor() {
return mShadowColor;
}
public void setShadowColor(final int shadowColor) {
mShadowColor = shadowColor;
mShadowAlpha = Color.alpha(shadowColor);
resetShadow();
}
public float getShadowDx() {
return mShadowDx;
}
public float getShadowDy() {
return mShadowDy;
}
// Reset shadow layer
private void resetShadow() {
// Detect shadow axis offset
mShadowDx = (float) ((mShadowDistance) * Math.cos(mShadowAngle / 180.0F * Math.PI));
mShadowDy = (float) ((mShadowDistance) * Math.sin(mShadowAngle / 180.0F * Math.PI));
// Set padding for shadow bitmap
final int padding = (int) (mShadowDistance + mShadowRadius);
setPadding(padding, padding, padding, padding);
requestLayout();
}
private int adjustShadowAlpha(final boolean adjust) {
return Color.argb(
adjust ? MAX_ALPHA : mShadowAlpha,
Color.red(mShadowColor),
Color.green(mShadowColor),
Color.blue(mShadowColor)
);
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
// Set ShadowLayout bounds
mBounds.set(
0, 0, MeasureSpec.getSize(widthMeasureSpec), MeasureSpec.getSize(heightMeasureSpec)
);
}
#Override
public void requestLayout() {
// Redraw shadow
mInvalidateShadow = true;
super.requestLayout();
}
#Override
protected void dispatchDraw(final Canvas canvas) {
// If is not shadowed, skip
if (mIsShadowed) {
// If need to redraw shadow
if (mInvalidateShadow) {
// If bounds is zero
if (mBounds.width() != 0 && mBounds.height() != 0) {
// Reset bitmap to bounds
mBitmap = Bitmap.createBitmap(
mBounds.width(), mBounds.height(), Bitmap.Config.ARGB_8888
);
// Canvas reset
mCanvas.setBitmap(mBitmap);
// We just redraw
mInvalidateShadow = false;
// Main feature of this lib. We create the local copy of all content, so now
// we can draw bitmap as a bottom layer of natural canvas.
// We draw shadow like blur effect on bitmap, cause of setShadowLayer() method of
// paint does`t draw shadow, it draw another copy of bitmap
super.dispatchDraw(mCanvas);
// Get the alpha bounds of bitmap
final Bitmap extractedAlpha = mBitmap.extractAlpha();
// Clear past content content to draw shadow
mCanvas.drawColor(0, PorterDuff.Mode.CLEAR);
// Draw extracted alpha bounds of our local canvas
mPaint.setColor(adjustShadowAlpha(false));
mCanvas.drawBitmap(extractedAlpha, mShadowDx, mShadowDy, mPaint);
// Recycle and clear extracted alpha
extractedAlpha.recycle();
} else {
// Create placeholder bitmap when size is zero and wait until new size coming up
mBitmap = Bitmap.createBitmap(1, 1, Bitmap.Config.RGB_565);
}
}
// Reset alpha to draw child with full alpha
mPaint.setColor(adjustShadowAlpha(true));
// Draw shadow bitmap
if (mCanvas != null && mBitmap != null && !mBitmap.isRecycled())
canvas.drawBitmap(mBitmap, 0.0F, 0.0F, mPaint);
}
// Draw child`s
super.dispatchDraw(canvas);
}
}
use Tag in xml like this:
<yourpackagename.ShadowLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_centerHorizontal="true"
android:layout_gravity="center_horizontal"
app:sl_shadow_color="#9e000000"
app:sl_shadow_radius="4dp">
<child views>
</yourpackagename.ShadowLayout>
UPDATE
put the below code in attrs.xml in resource>>values
<declare-styleable name="ShadowLayout">
<attr name="sl_shadowed" format="boolean"/>
<attr name="sl_shadow_distance" format="dimension"/>
<attr name="sl_shadow_angle" format="integer"/>
<attr name="sl_shadow_radius" format="dimension"/>
<attr name="sl_shadow_color" format="color"/>
</declare-styleable>
One possible solution is using nine patch image like this http://developer.android.com/guide/topics/graphics/2d-graphics.html#nine-patch
OR
I have done this in the following way. This is my main layout in which round_corner.xml and drop_shadow.xml used as background resource. round_corner_two is same like round_corner.xml only the color attribute is different. copy the round_corner.xml,drop_shadow.xml and round_conere_two.xml into drawable folder.
<RelativeLayout
android:id="#+id/facebook_id"
android:layout_width="250dp"
android:layout_height="52dp"
android:layout_centerHorizontal="true"
android:layout_marginTop="28dp"
android:background="#drawable/round_corner" >
<LinearLayout
android:id="#+id/shadow_id"
android:layout_width="match_parent"
android:layout_height="48dp"
android:layout_margin="1dp"
android:background="#drawable/drop_shadow" >
<TextView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="center"
android:layout_marginBottom="2dp"
android:background="#drawable/round_corner_two"
android:gravity="center"
android:text="#string/fb_butn_text"
android:textColor="#color/white" >
</TextView>
</LinearLayout>
</RelativeLayout>
round_corner.xml:
<?xml version="1.0" encoding="utf-8"?>
<shape
xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<!-- view background color -->
<solid
android:color="#ffffff" >
</solid>
<!-- view border color and width -->
<stroke
android:width="0dp"
android:color="#3b5998" >
</stroke>
<!-- If you want to add some padding -->
<padding
android:left="1dp"
android:top="1dp"
android:right="1dp"
android:bottom="1dp" >
</padding>
<!-- Here is the corner radius -->
<corners
android:radius="10dp" >
</corners>
</shape>
drop_shadow.xml
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item >
<shape
android:shape="rectangle">
<solid android:color="#android:color/darker_gray" />
<corners android:radius="12dp"/>
</shape>
</item>
<item android:right="1dp" android:left="1dp" android:bottom="5dp">
<shape
android:shape="rectangle">
<solid android:color="#android:color/white"/>
<corners android:radius="5dp"/>
</shape>
</item>
</layer-list>
i know this is way too late. but i had the same requirement. i solved like this
<android.support.v7.widget.CardView
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:cardUseCompatPadding="true"
app:cardElevation="4dp"
app:cardCornerRadius="3dp" >
<!-- put whatever you want -->
</android.support.v7.widget.CardView>
you need to add dependency:
compile 'com.android.support:cardview-v7:25.0.1'
set this xml drwable as your background;---
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android" >
<!-- Bottom 2dp Shadow -->
<item>
<shape android:shape="rectangle" >
<solid android:color="#d8d8d8" />-->Your shadow color<--
<corners android:radius="15dp" />
</shape>
</item>
<!-- White Top color -->
<item android:bottom="3px" android:left="3px" android:right="3px" android:top="3px">-->here you can customize the shadow size<---
<shape android:shape="rectangle" >
<solid android:color="#FFFFFF" />
<corners android:radius="15dp" />
</shape>
</item>
</layer-list>