Advanced Custom Widget - android

I googled a lot and spent many hours trying to figure out how to create custom Android UI widgets (in code and xml). But there are some limitations I don't know how to bypass. Therefore I mocked up an imaginary button and would like to know how to implement it.
This would help me a lot and I'm sure that I'm not the only one that has problems creating custom Android widgets.

About the multi-colored background, I was successful at these using shape drawables.
For example to define a grey solid zone, then a gradient transitioning to black, and a black solid zone:
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item>
<shape android:shape="rectangle">
<corners android:radius="10px" />
<solid android:color="#FF444444"/>
</shape>
</item>
<item
android:left="45dp">
<shape android:shape="rectangle">
<gradient
android:angle="0"
android:centerX="15%"
android:startColor="#FF444444"
android:centerColor="#FF000000"
android:endColor="#FF000000"
android:type="linear"/>
</shape>
</item>
<item>
<shape android:shape="rectangle">
<corners android:radius="10px" />
<stroke
android:width="2px"
android:color="#FF444444" />
<solid android:color="#00000000"/>
</shape>
</item>
</layer-list>
The cool thing here is the layered shapes (items). It should allow you to define a yellow triangle, a red triangle, and a green rectangle.
The above layout renders into the following:
(the icons and text not being rendered by the above layout)

If you choose to create a custom widget I recommend to check out a most similar standard widget from the android sources and modify them

Create a shape for background:
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<solid android:color="#color/GREEN"/>
<stroke
android:width="2dp"
android:color="#color/GREEN"/>
</shape>
Then Create a shape with layer-list that show your image:
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item>
<shape android:shape="rectangle">
<solid android:color="#color/GREEN"/>
</shape>
</item>
<item
android:left="15dp"
android:top="15dp"
android:right="15dp"
android:bottom="15dp"
android:drawable="#drawable/ic_image" />
</layer-list>
Finally, you can use above shapes in a button:
<Button
android:id="#+id/btnFavorite"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="15dp"
android:layout_below="#id/btnWorkout"
android:background="#drawable/selector_main_button"
android:drawableRight="#drawable/shape_favorite"
android:gravity="center"
android:text="#string/favorite"
android:textSize="#dimen/font_7"
android:textColor="#color/ORANGE"
android:textStyle="bold"
android:onClick="onClick"/>
Even, it should mentioned that you can use first shape as a selector.

Related

How to make a pill shaped button in android?

I am not sure what to call this... pill shaped maybe. Googling for rounded corners turns up lots of posts of people wanting a rectangular button with rounded corners. This is a little different but more like 2 circles and a rectangle which I tried to draw it as.
I would like to make a button shaped something like the first image below but with text and an icon image in it by using an xml drawable background on an android :
I have tried this which looks ok but if the button length varies it does not scale and you end up with a rectangle and some other strange stuff.
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item android:left="40dp" android:top="0dp" android:bottom="0dp">
<shape android:shape="oval" >
<solid android:color="#666666"/>
<size android:width="40dp" android:height="40dp"></size>
</shape>
</item>
<item android:left="20dp" android:right="20dp">
<shape android:shape="rectangle" >
<solid android:color="#666666"/>
<size android:width="20dp" android:height="20dp"></size>
</shape>
</item>
<item android:right="40dp">
<shape android:shape="oval" >
<solid android:color="#666666"/>
<size android:width="40dp" android:height="40dp"></size>
</shape>
</item>
</layer-list>
I have tried to create my xml drawable like this also:
<shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle">
<solid android:color="#666666" />
<corners android:radius="20dp"/>
<size android:height="20dp" android:width="60dp"></size>
</shape>
which looks like this:
I have looked at this persons example but when I do what he does my results are not completely rounded at the corners. They are more like the second image above.
You can try to save the following code to a drawable(e.g. pill_bg.xml):
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:dither="true"
android:shape="rectangle">
<corners android:radius="120dp" />
<solid android:color="#color/white" />
<stroke
android:width="1dp"
android:color="#efefef" />
<size
android:width="300dp"
android:height="120dp" />
</shape>
Take a look at size part and radius. Then apply the drawable to your view, for example:
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="250dp"
android:layout_height="60dp"
android:background="#drawable/pill_bg" />
As you can see, your view can have different sizes, the background will be scaled.
As of v1.1.0-beta01 of the Material Components Android library you can use MaterialButton and a shapeAppearance style to easily define a pill button that doesn't require you to know the height of the button beforehand or rely on guessing a radius value high enough to go halfway down the side of the button.
Simply define a style like this:
<style name="PillShapeAppearance">
<item name="cornerFamily">rounded</item>
<item name="cornerSize">50%</item>
</style>
and set it on your MaterialButton like this:
<com.google.android.material.button.MaterialButton
android:layout_width="wrap_content"
android:layout_height="wrap_content"
...
app:shapeAppearanceOverlay="#style/PillShapeAppearance"
/>
The key that every other answer I've looked at leaves out is to set cornerSize with a percent value so that the fraction value it supports is properly applied.
This resizes correctly for any view background.
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<corners android:radius="1000dp" />
<solid android:color="#android:color/holo_red_dark" />
</shape>
Well you can make that button in any design program, also make one similar make it look like it is pressed then you make a selector drawable and assign it to the button background. That´s the way i do pretty awesome buttons

Android xml shape drawable - how to draw a u-form?

I need to create a xml shape drawable that draws a rectangle without the top-line (a "u-form"). What I am able to do is draw a rectangle, like so:
<?xml version="1.0" encoding="UTF-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle" >
<solid android:color="#color/detailrow_bg_normal" />
<corners
android:bottomLeftRadius="0dp"
android:bottomRightRadius="0dp"
android:radius="1dp"
android:topLeftRadius="10dp"
android:topRightRadius="10dp" />
<padding
android:bottom="2dip"
android:left="1.5dip"
android:right="1.5dip"
android:top="8dip" />
<stroke
android:width="1dip"
android:color="#color/detailtable_border" />
But how - if possible, can I define the same shape without the top (or bottom) line?
You could try using a layer-list. See if something like this would work:
<layer-list xmlns:android="http://schemas.android.com/apk/res/android" >
<item>
<shape android:shape="rectangle">
<solid android:color="#color/detailtable_border" />
</shape>
</item>
<item android:left="1.5dp" android:right="1.5dp" android:bottom="2dp">
<shape android:shape="rectangle">
<solid android:color="#color/detailrow_bg_normal" />
</shape>
</item>
</layer-list>
This (should) fill the rectangle with the border color, then overlay it with the default background color, leaving the appropriate amount of the right/left/bottom border color showing.
try this code and also refer this link:::
I achieved a good solution with this one:
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<!-- This is the line -->
<item android:top="-1dp" android:right="-1dp" android:left="-1dp">
<shape>
<solid android:color="#android:color/transparent" />
<stroke android:width="1dp" android:color="#ffffff" />
</shape>
</item>
</layer-list>
This works well in case you need a transparent color but still an open stroke color (In my case i only needed a bottom line). If you need a background color you can add a solid shape color as in above answer.
You might be able to achieve this using two shapes and a LayerList, but I think it is both a better solution and easier to use a NinePatch-drawable.

Android shape within a shape

I want to have a shape element with a two color border outline. I can do a single color outline using the solid element, but this only allows me to draw a single line. I tried using two stroke elements within my shape, but that didn't work either.
Is there a way to either draw a shape within a shape or draw two lines around my shape (which has rounded corners, BTW).
I found that the <layer-list> is the best approach. Like this:
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:left="6dp"
android:right="6dp">
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<stroke
android:width="3dp"
android:color="#000000" />
</shape>
</item>
<item
android:bottom="1dp"
android:left="8dp"
android:right="8dp"
android:top="1dp">
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<corners
android:bottomLeftRadius="2dp"
android:bottomRightRadius="2dp"
android:topLeftRadius="2dp"
android:topRightRadius="2dp" />
<solid android:color="#android:color/white" />
<stroke
android:width="1dp"
android:color="#BDBDBD" />
</shape>
</item>
</layer-list>
You then need to put the proper margins on your listView row layout, but it works quite nicely.
so i have a work around but its ugly. the work around is to wrap my element inside another container element. i.e.
<RelativeLayout ... android:background="#drawable/outer">
<ListView ... android:background="#drawable/inner" />
</RelativeLayout>

Drawing top border on a shape on Android

I'm creating a custom progress bar (positioned under a WebView) and what I would like to draw is a 1dp-wide line between the WebView and the ProgressBar. I'm modifying existing drawable, namely progress_horizontal.xml, and tried something like this:
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
(...)
<item>
<shape android:shape="line">
<stroke android:width="1dp" android:color="#FF000000" />
</shape>
</item>
</layer-list>
This line however is vertically centered but I want it to be drawn on top of the drawable. The only idea I could come up with is to use this "hacky" gradient below:
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
(...)
<item>
<shape>
<gradient
android:startColor="#FF000000"
android:centerColor="#00000000"
android:centerY="0.01"
android:endColor="#00000000"
android:angle="270"
/>
</shape>
</item>
</layer-list>
Do you have better ideas how to draw a single line shape aligned to the top of the drawable defined with layer-list?
I spent a while trying to figure this out as well. Hopefully there is a better way to do it but here is the method I used, which is also kind of hackish, but in case it helps someone, I thought I would share.
The first item in your layer list should be a solid of whatever color you want to be your border. Following that would be the thing(s) you want to have the border, with padding on whatever side you want the border to be on.
For example:
<?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="YOUR BORDER COLOR" />
</shape>
</item>
<item android:top="YOUR TOP BORDER THICKNESS">
THE THING YOU WANT A BORDER ON
</item>
</layer-list>
The idea is that the padding on the item reveals the solid shape behind it, giving the border appearance. You could add padding to any side to add a border. I imagine you could get more complicated and have different colored borders this way as well.
Seems like a hack but it worked for me.
EDIT: I said "padding" but in layer-lists it's more of an offset.
i was going through all related topics but no one could solve my problem. But some of them were very useful to understand how the layer-list or shape parameters work.
My problem was to define a button with a linear gradient and draw a top and a bottom line in different colors. After all I hacked this solution. I saved the file unter res/drawable/blue_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 android:shape="rectangle">
<solid android:color="#color/blue_end" />
<padding
android:left="10dp"
android:top="10dp"
android:right="10dp"
android:bottom="10dp" />
<stroke
android:width="1dp"
android:color="#color/bottomline_btn" />
</shape>
</item>
<item>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android" >
<item>
<shape android:shape="rectangle">
<solid android:color="#color/blue" />
<gradient
android:startColor="#color/blue"
android:endColor="#color/blue_end"
android:angle="270" />
</shape>
</item>
<item android:top="0dp" android:bottom="-1dp" android:left="-1dp" android:right="-1dp">
<shape android:shape="rectangle">
<stroke
android:width="1dp"
android:color="#color/topline_btn" />
<solid android:color="#00000000" />
<corners android:radius="0dp" />
</shape>
</item>
<item android:top="-1dp" android:bottom="0dp" android:left="-1dp" android:right="-1dp">
<shape android:shape="rectangle">
<stroke
android:width="1dp"
android:color="#color/bottomline_btn" />
<solid android:color="#00000000" />
<corners android:radius="0dp" />
</shape>
</item>
</layer-list>
</item>
</selector>
<color name="topline_btn">#31ffffff</color>
<color name="bottomline_btn">#31000000</color>
<color name="blue">#449def</color>
<color name="blue_end">#2f6699</color>
Did you try offsetting it by something like this
http://android.amberfog.com/?p=9
My solution is to add 9-patch as the last layer item :
<layer-list xmlns:android="http://schemas.android.com/apk/res/android" >
<item>
<shape>
<solid android:color="#color/tab_button_active_layer2" />
</shape>
</item>
<item android:bottom="#dimen/tab_button_active_bottom_bar_width">
<shape>
<solid android:color="#color/tab_button_active_layer1" />
</shape>
</item>
<!-- 9-patch drawable -->
<item android:drawable="#drawable/tab_button_border_top"/>
</layer-list>

Padding doesn't affect <shape> in an XML Layout

I am trying to set padding in a <shape> declared within an XML file/layout. But whatever I set, nothing changes related to padding. If I modify any other properties, I see the effects on the UI. But it doesn't work with padding. Could you please advise on the possible reasons for which this might occur?
Here is the XML Shape I am trying to style:
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<stroke android:width="1dp"
android:color="#ffffff"
android:dashWidth="2dp"/>
<solid android:color="#color/button_white_cover"/>
<corners android:radius="11dp"/>
<padding android:left="1dp"
android:top="20dp"
android:right="20dp"
android:bottom="2dp"/>
</shape>
I have finally resolved my problem with padding.
So padding here will have no effect on the shape. Instead of this, you will have to apply padding in other drawable that will use it. So, I have a drawable that uses the shape and there I do following:
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="#drawable/shape_below"/>
<item android:top="10px" android:right="10px" android:drawable="#drawable/shape_cover"/>
</layer-list>
So, as you can see in the second <item>-element I set padding (top and right). And after this everything works.
Thanks.
As a couple of comments have alluded to, the best approach is to wrap the <shape> in an <item> element and set the padding there instead. There are however other options available which are detailed in the link below. eg
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:left="8dp"
android:right="8dp"
android:top="8dp"
android:bottom="8dp">
<shape android:shape="rectangle">
...
</shape>
</item>
</layer-list>
Depending on your needs, instead of using a layer-list, you could use any of the following drawable types:
layer-list
selector
level-list
transition
For more information about the available drawable types see this android documentation
Sources:
DustinB's comment
Yahel's answer
user924's comment
How to add padding to gradient <shape> in Android?
You can try insets:
<?xml version="1.0" encoding="UTF-8"?>
<inset xmlns:android="http://schemas.android.com/apk/res/android"
android:insetBottom="2dp"
android:insetLeft="1dp"
android:insetRight="20dp"
android:insetTop="20dp">
<shape android:shape="rectangle">
<stroke
android:width="1dp"
android:color="#ffffff"
android:dashWidth="2dp" />
<solid android:color="#color/button_white_cover" />
<corners android:radius="11dp" />
</shape>
</inset>
The answer by Lyobomyr works, but here is the same solution applied but without the overhead of creating a new drawable, hence minimizing the file clutter :
https://stackoverflow.com/a/3588976/578746
Copy/Paste of the anwser by anon on the SO answer above :
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item android:left="6dp"
android:right="6dp">
<shape android:shape="rectangle">
<gradient android:startColor="#ccd0d3"
android:centerColor="#b6babd"
android:endColor="#ccd0d3"
android:height="1px"
android:angle="0"/>
</shape>
</item>
</layer-list>
I solved this problem like this:
<shape android:shape="oval" >
<stroke android:width="4dp" android:color="#android:color/transparent" />
<solid android:color="#color/colorAccent" />
</shape>
just added a transparent stroke to it.

Categories

Resources