Porter-Duff: different behavior for different shapes? - android

I have the following layout:
<LinearLayout
android:id="#+id/myButton"
android:layout_width="#dimen/logo_radius"
android:layout_height="match_parent"
android:background="#drawable/myShape">
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="#drawable/driver_half"/>
</LinearLayout>
and the following myShape drawable:
<selector>
<item><shape android:shape="oval">
<stroke android:color="#android:color/black" android:width="4dp"/>
<solid android:color="#android:color/white" />
</shape></item>
</selector>
I applied the following filter:
myButton.getBackground().setColorFilter( orange, PorterDuff.Mode.ADD );
the result looked that way:
Then I changed myShape to be a rectangle with rounded corners:
<selector>
<item>
<shape
android:shape="rectangle">
<corners android:bottomLeftRadius="#dimen/logo_radius" android:bottomRightRadius="2dp" android:topLeftRadius="#dimen/logo_radius" android:topRightRadius="2dp"/>
<stroke
android:width="4dp"
android:color="#android:color/black"/>
<solid android:color="#android:color/white"/>
</shape>
</item>
</selector>
the result looked like:
left part is without the filter applied, the right part with the filter.
what I want to get:
What should I do to properly paint the border orange using the Porter-Duff filter? Are there any other options?

Porter/Duff filtering depends on image alpha channel. To paint shapes border only (without other shape space) you should change shape background from white to transparent:
<selector>
<item>
<shape
android:shape="rectangle">
<corners android:bottomLeftRadius="#dimen/logo_radius" android:bottomRightRadius="2dp" android:topLeftRadius="#dimen/logo_radius" android:topRightRadius="2dp"/>
<stroke
android:width="4dp"
android:color="#android:color/black"/>
<solid android:color="#android:color/transparent"/>
</shape>
</item>
</selector>
and correct PorterDuff.Mode for this case should be SRC_IN.
But I don't know why oval-shape painted correctly.
UPD:
with SRC_IN the border is painted orange, but the filling remains transparent...
You can change your selector to layer-list drawable like this:
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item android:id="#+id/background">
<shape android:shape="rectangle">
<corners
android:bottomLeftRadius="32dp"
android:bottomRightRadius="2dp"
android:topLeftRadius="32dp"
android:topRightRadius="2dp" />
<solid android:color="#android:color/white" />
</shape>
</item>
<item android:id="#+id/border">
<shape android:shape="rectangle">
<corners
android:bottomLeftRadius="32dp"
android:bottomRightRadius="2dp"
android:topLeftRadius="32dp"
android:topRightRadius="2dp" />
<stroke
android:width="4dp"
android:color="#android:color/black" />
<solid android:color="#android:color/transparent" />
</shape>
</item>
</layer-list>
and set color filter only for border item:
LayerDrawable background = LayerDrawable.class.cast(findViewById(R.id.target).getBackground());
background.findDrawableByLayerId(R.id.border).setColorFilter(Color.CYAN, PorterDuff.Mode.SRC_IN);

Related

How to add faded edges to a rectangle drawable?

I currently have the following drawable which creates a rectangle with rounded edges, a semi-transparent black background and transparent edges (in case I want to show them later):
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item>
<color android:color="#android:color/transparent" />
</item>
<item>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<solid android:color="#50FFFFFF"/>
<stroke
android:width="5dip"
android:color="#android:color/transparent" />
<corners android:radius="30dp" />
</shape>
</item>
</layer-list>
My question is how could I modify this so that the edges of the rectangle's background fade, to give a softer appearance?
I did something like this for work once.
Just make a layer-list with multiple rectangle each with increasing opacity and padding
code below:
<?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="#03101010" />
<corners android:radius="24dp" />
</shape>
</item>
<item
android:bottom="4dp"
android:left="4dp"
android:right="4dp"
android:top="4dp">
<shape android:shape="rectangle">
<solid android:color="#06101010" />
<corners android:radius="20dp" />
</shape>
</item>
<item
android:bottom="8dp"
android:left="8dp"
android:right="8dp"
android:top="8dp">
<shape android:shape="rectangle">
<solid android:color="#09101010" />
<corners android:radius="16dp" />
</shape>
</item>
<item
android:bottom="12dp"
android:left="12dp"
android:right="12dp"
android:top="12dp">
<shape android:shape="rectangle">
<solid android:color="#12101010" />
<corners android:radius="12dp" />
</shape>
</item>
</layer-list>
The number of layers may vary depending on the color of your primary rectangle and the background.

Android Custom shape for EditText

I am trying to create a custom design for EditText. See the attached image below,
I am trying to achieve this using a custom shape like the one defined below.
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="#drawable/places_ic_search"
android:width="30dp">
<shape
android:shape="rectangle"></shape>
</item>
<item
android:left="30dp"
android:drawable="#color/white">
<shape android:shape="rectangle" >
<gradient android:startColor="#000000" android:endColor="#FFFFFF" android:angle="90"/>
</shape>
</item>
</layer-list>
But the search icon is not displayed. How can I achieve this?
Thanks.
I have applied the answer by tiny sunlight and the EditBox looks like this, Search icon is no aligned to the left.
I edit my answer. Edit Shape.xml as below:
Shape.xml
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item>
<shape android:shape="rectangle" >
<stroke android:width="5dp" android:color="#FF0000" />
</shape>
</item>
<item
android:left="3dp"
android:right="3dp"
android:top="3dp"
android:bottom="3dp"
>
<shape android:shape="rectangle" >
<stroke android:width="1dp" android:color="#FF0000" />\
<solid android:color="#FFFFFF" />
<corners android:radius="3dp" />
</shape>
</item>
<item android:gravity="left" android:left="10dp">
<bitmap android:src="#android:drawable/ic_menu_search" android:gravity="left">
</bitmap>
</item>
</layer-list>
I would do it like this :
For the border part, you could do it with a drawable like you did
For the search icon, instead of trying to put it in the drawable file, why not set it in you EditText xml layout, using android:drawableLeft, you can then set padding using android:paddingLeft and android:drawablePadding
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item>
<shape android:shape="rectangle" >
<gradient android:startColor="#FFFFFF" android:endColor="#FFFFFF" android:angle="90"/>
<corners android:radius="3dp"></corners>
</shape>
</item>
<item android:drawable="#drawable/places_ic_search"
android:gravity="center_vertical|left"
android:height="10dp"
android:width="30dp">
</item>
</layer-list>

Keep ripple within stroke

I'm trying to create a ripple background drawable for a Button with a stroke.
This is what I have so far:
<ripple xmlns:android="http://schemas.android.com/apk/res/android"
android:color="#336699">
<item>
<shape android:shape="rectangle">
<solid android:color="#998811" />
<stroke
android:width="2dp"
android:color="#119988" />
</shape>
</item>
</ripple>
But with this solution, the ripple overlaps with my stroke.
I only want the ripple within the stroke, How can I do this?
As per the documentation you add another item with id #android:id/mask - that will limit where the ripple goes. You can set that to be inset, like so:
<ripple xmlns:android="http://schemas.android.com/apk/res/android"
android:color="#336699">
<item>
<shape android:shape="rectangle">
<solid android:color="#998811" />
<stroke
android:width="2dp"
android:color="#119988" />
</shape>
</item>
<item android:id="#android:id/mask">
<inset android:insetBottom="2dp"
android:insetLeft="2dp"
android:insetRight="2dp"
android:insetTop="2dp">
<shape android:shape="rectangle">
<!-- Color doesn't matter -->
<solid android:color="#android:color/white"/>
</shape>
</inset>
</item>
</ripple>

Style bottom Line in Android

I need to create an android shape so that only the bottom has stroke (a dashed line). When I try the following, the stroke bisects the shape right through the center. Does anyone know how to get it right? the stroke needs to be the bottom line/border. I am using the shape as a background to a TextView. Please, never mind why I need it.
<?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="#1bd4f6" />
</shape>
</item>
<item>
<shape android:shape="line" >
<padding android:bottom="1dp" />
<stroke
android:dashGap="10px"
android:dashWidth="10px"
android:width="1dp"
android:color="#ababb2" />
</shape>
</item>
</layer-list>
It's kind of a hack, but I think this is probably the best way to do it. The dashed line will always be on the bottom, regardless of the height.
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item>
<shape android:shape="rectangle" >
<solid android:color="#1bd4f6" />
</shape>
</item>
<item android:top="-2dp" android:right="-2dp" android:left="-2dp">
<shape>
<solid android:color="#android:color/transparent" />
<stroke
android:dashGap="10px"
android:dashWidth="10px"
android:width="1dp"
android:color="#ababb2" />
</shape>
</item>
</layer-list>
Explanation:
The second shape is transparent rectangle with a dashed outline. The key in making the border only appear along the bottom lies in the negative margins set the other sides. These negative margins "push" the dashed line outside the drawn area on those sides, leaving only the line along the bottom. One potential side-effect (which I haven't tried) is that, for views that draw outside their own bounds, the negative-margin borders may become visible.
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:top="-6dp"
android:left="-6dp"
android:right="-6dp"
android:bottom="0dp">
<shape android:shape="rectangle">
<solid android:color="#88FFFF00"/>
<stroke
android:width="5dp"
android:color="#FF000000"/>
</shape>
</item>
</layer-list>
This does the trick...
<item >
<shape android:shape="rectangle">
<solid android:color="#YOUR_BOTTOM_LINE_COLOR"/>
</shape>
</item>
<item android:bottom="1.5dp">
<shape android:shape="rectangle">
<solid android:color="#YOUR_BG_COLOR"/>
</shape>
</item>
I feel it is straightforward, without all this negative paddings or storks.
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="#color/main_bg_color"/>
<item android:gravity="bottom">
<shape android:shape="rectangle">
<size android:height="5dp"/>
<solid android:color="#color/bottom_bar_color"/>
</shape>
</item>
</layer-list>
This answer is for those google searchers who want to show dotted bottom border of EditText like here
Create dotted.xml inside drawable folder and paste these
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:bottom="1dp"
android:left="-2dp"
android:right="-2dp"
android:top="-2dp">
<shape android:shape="rectangle">
<stroke
android:width="0.5dp"
android:color="#android:color/black" />
<solid android:color="#ffffff" />
<stroke
android:width="1dp"
android:color="#030310"
android:dashGap="5dp"
android:dashWidth="5dp" />
<padding
android:bottom="5dp"
android:left="5dp"
android:right="5dp"
android:top="5dp" />
</shape>
</item>
</layer-list>
Then simply set the android:background attribute to dotted.xml we just created. Your EditText looks like this.
<EditText
android:id="#+id/editText"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Some Text"
android:background="#drawable/dotted" />
Try next xml drawable code:
<layer-list>
<item android:top="-2dp" android:right="-2dp" android:left="-2dp">
<shape>
<solid android:color="#android:color/transparent" />
<stroke
android:width="1dp"
android:color="#fff" />
</shape>
</item>
</layer-list>
I think you do not need to use shape if I understood you.
If you are looking as shown in following image then use following layout.
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<RelativeLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:background="#1bd4f6"
android:paddingBottom="4dp" >
<TextView
android:layout_width="200dp"
android:layout_height="wrap_content"
android:background="#ababb2"
android:padding="5dp"
android:text="Hello Android" />
</RelativeLayout>
</RelativeLayout>
EDIT
play with these properties you will get result
android:top="dimension"
android:right="dimension"
android:bottom="dimension"
android:left="dimension"
try like this
<?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="#1bd4f6" />
</shape>
</item>
<item android:top="20px"
android:left="0px">
<shape android:shape="line" >
<padding android:bottom="1dp" />
<stroke
android:dashGap="10px"
android:dashWidth="10px"
android:width="1dp"
android:color="#ababb2" />
</shape>
</item>
</layer-list>
A Simple solution :
Create a drawable file as edittext_stroke.xml in drawable folder.
Add the below code:
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="line"
>
<stroke
android:width="1dp"
android:color="#android:color/white" >
</stroke>
</shape>
In layout file , add the drawable to edittext as
android:drawableBottom="#drawable/edittext_stroke"
<EditText
android:textColor="#android:color/white"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:drawableBottom="#drawable/edittext_stroke"
/>
Usually for similar tasks - I created layer-list drawable like this one:
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item>
<shape android:shape="rectangle">
<solid android:color="#color/underlineColor"/>
</shape>
</item>
<item android:bottom="3dp">
<shape android:shape="rectangle">
<solid android:color="#color/buttonColor"/>
</shape>
</item>
The idea is that first you draw the rectangle with underlineColor and then on top of this one you draw another rectangle with the actual buttonColor but applying bottomPadding. It always works.
But when I needed to have buttonColor to be transparent I couldn't use the above drawable. I found one more solution
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item>
<shape android:shape="rectangle">
<solid android:color="#android:color/transparent"/>
</shape>
</item>
<item android:drawable="#drawable/white_box" android:gravity="bottom" android:height="2dp"/>
</layer-list>
(as you can see here the mainButtonColor is transparent and white_box is just a simple rectangle drawable with white Solid)
use this xml change the color with your choice.
<item>
<layer-list>
<item>
<shape>
<solid android:color="#color/gray_500" />
</shape>
</item>
<!-- CONTENT LAYER -->
<item android:bottom="2dp" >
<shape>
<solid android:color="#ffffff" />
</shape>
</item>
</layer-list>
</item>
In Case if you want programmatically
public static Drawable getStorkLineDrawable(#ColorInt int colorStrok, int iSize, int left, int top, int right, int bottom)
{
GradientDrawable gradientDrawable = new GradientDrawable();
gradientDrawable.setShape(GradientDrawable.RECTANGLE);
gradientDrawable.setStroke(iSize, colorStrok);
LayerDrawable layerDrawable = new LayerDrawable(new Drawable[]{gradientDrawable});
layerDrawable.setLayerInset(0, left, top, right, bottom);
return layerDrawable;
}
call this method like
Drawable yourLineDrawable= getStorkLineDrawable(yourColor, iSize, -iSize, -iSize, -iSize, 0);
easiest way to do this is put after that view where you want bottom border
<?xml version="1.0" encoding="utf-8"?>
<View
android:layout_width="match_parent"
android:layout_height="1dp"
android:background="#color/colorPrimary" />
This worked best for me:
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item android:top="-5dp" android:left="-5dp" android:right="-5dp" android:bottom="0dp">
<shape android:shape="rectangle">
<stroke android:width="4dp" android:color="#ff0000"/>
</shape>
</item>
</layer-list>
Shows the line on the bottom only. You can easily change with stroke width to size you like and also update the top, left, right on the accordingly.
Simply add -
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<!--Minus (-) how much dp you gave in the stroke width from left right top-->
<item android:left="-10dp" android:right="-10dp" android:top="-10dp">
<shape
android:shape="rectangle">
<stroke
android:dashGap="10dp"
android:dashWidth="10dp"
android:width="10dp"
android:color="#android:color/holo_red_dark" />
<!--This is the main background -->
<solid android:color="#FFDDDDDD" />
</shape>
</item>
</layer-list>
Preview -
it is completely transparent Edittext with transparent background.
<item>
<shape android:shape="rectangle" >
<solid android:color="#color/transparent" />
</shape>
</item>
<item android:top="-3dp" android:right="-3dp" android:left="-3dp">
<shape>
<solid android:color="#android:color/transparent" />
<stroke
android:width="2dp"
android:color="#color/bottomline" />
</shape>
</item>
A Simple solution :
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:bottom="-1dp"
android:left="-1dp"
android:right="-1dp"
android:top="-1dp">
<shape android:shape="rectangle">
<solid android:color="#AARRGGBB" />
<stroke
android:width="5dp"
android:color="#android:color/red"
android:dashWidth="10dp"
android:dashGap="12dp" />
</shape>
</item>
</layer-list>
And finally we have something like that :)
This is rectangular background with bottom stroke
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item>
<shape android:shape="rectangle">
<corners android:radius="4dp" />
<solid android:color="#f2f4f5" />
<stroke
android:width="3dp"
android:color="#002f34" />
<padding android:bottom="4dp" />
</shape>
</item>
<item>
<shape android:shape="rectangle">
<corners android:radius="4dp" />
<solid android:color="#f2f4f5" />
</shape>
</item>
</layer-list>

Shape drawable as background, a line at the bottom

I am using a drawable as a background of a TextView just to have a divider line below the text. A achivied it with this drawable-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="#FFDDDDDD" />
<gradient
android:angle="0"
android:startColor="#FFAAAAAA"
android:endColor="#FFEEEEEE"
/>
</shape>
</item>
<item android:bottom="2dp">
<shape
android:shape="rectangle">
<solid android:color="#FF000000" />
</shape>
</item>
</layer-list>
But this method draws a colored rectangle above a black rectangle. I would like to have just the line at the bottom of the shape with no black rectangle because black is not transparent. How could I achieve that?
This is how I got a line at the bottom for mine. Draw a stroke but then shift the item up and to the sides to get the top and sides to not show the stroke:
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item android:top="-8dp" android:left="-8dp" android:right="-8dp">
<shape>
<solid android:color="#2b7996"/>
<stroke android:color="#33b5e5" android:width="6dp"/>
</shape>
</item>
</layer-list>
I think it's better solution:
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android" >
<item android:gravity="bottom">
<shape>
<size android:height="1dp" />
<solid android:color="#000000" />
</shape>
</item>
</layer-list>
In general, I try to mess as little as possible with backgrounds unless absolutely necessary, since doing so overrides the default background colors that have states for focused, pressed, etc. I suggest just using an additional view (in a vertical LinearLayout) that is as thick as you need it to be. For example:
<View
android:background="#FF000000"
android:layout_height="2dp"
android:layout_width="fill_parent"/>
Usually for similar tasks - I created layer-list drawable like this one:
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item>
<shape android:shape="rectangle">
<solid android:color="#color/underlineColor"/>
</shape>
</item>
<item android:bottom="3dp">
<shape android:shape="rectangle">
<solid android:color="#color/buttonColor"/>
</shape>
</item>
The idea is that first you draw the rectangle with underlineColor and then on top of this one you draw another rectangle with the actual buttonColor but applying bottomPadding. It always works.
But when I needed to have buttonColor to be transparent I couldn't use the above drawable. I found one more solution
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item>
<shape android:shape="rectangle">
<solid android:color="#android:color/transparent"/>
</shape>
</item>
<item android:drawable="#drawable/white_box" android:gravity="bottom" android:height="2dp"/>
</layer-list>
(as you can see here the mainButtonColor is transparent and white_box is just a simple rectangle drawable with white Solid)
With this solution where ever you require different line you can. My requirement was underline only. Even you can give different colors to the layout. You can see in below picture, white line
<?xml version="1.0" encoding="UTF-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item android:top="-5dp" android:left="-5dp" android:right="-5dp">
<shape>
<solid android:color="#android:color/transparent"/>
<stroke android:color="#color/white" android:width="5dp"/>
</shape>
</item>
<item android:top="-5dp" android:bottom="-5dp" android:right="-5dp">
<shape>
<solid android:color="#android:color/transparent"/>
<stroke android:color="#color/colorPrimaryDark" android:width="5dp"/>
</shape>
</item>
<item android:bottom="-5dp" android:left="-5dp" android:right="-5dp">
<shape>
<solid android:color="#android:color/transparent"/>
<stroke android:color="#color/colorPrimaryDark" android:width="5dp"/>
</shape>
</item>
<item android:top="-5dp" android:left="-5dp" android:bottom="-5dp">
<shape>
<solid android:color="#android:color/transparent"/>
<stroke android:color="#color/colorPrimaryDark" android:width="5dp"/>
</shape>
</item>
<item android:bottom="2dp" android:left="5dp" android:right="5dp" android:top="5dp">
<shape>
<solid android:color="#color/colorPrimary"/>
<corners android:bottomRightRadius="0dp"
android:bottomLeftRadius="0dp"
android:topLeftRadius="0dp"
android:topRightRadius="0dp" />
</shape>
</item>
</layer-list>
This is a slightly lighter variant of the above.
/drawable/rect_highlight.xml
<shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle">
<stroke android:width="1px" android:color="#color/colorHighlight"/>
</shape>
/drawable/underline.xml
<inset xmlns:android="http://schemas.android.com/apk/res/android" android:insetLeft="-1px" android:insetRight="-1px" android:insetTop="-1px" android:drawable="#drawable/rect_highlight"/>
Usage:
<TextView ... android:background="#drawable/underline"/>
It's not mine, somebody smarter than I came up with it. If I was smarter, I would have asked who. :)
This solution worked for most of the cases that I needed something like that.
<?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/transparent" /> <!--background color of box-->
</shape>
</item>
<item
android:height="2dp"
android:gravity="bottom">
<shape>
<solid android:color="#000000" />
</shape>
</item>
</layer-list>
A simple solution is extending a TextView, then override the onDraw.
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
Paint paint = new Paint();
paint.setColor(ContextCompat.getColor(getContext(),R.color.colorTextUnderLine));
paint.setStrokeWidth(10);
canvas.drawLine(0.0f,canvas.getHeight(),canvas.getWidth(),canvas.getHeight(),
paint);
}

Categories

Resources