When I define a style, it can be inherited from a parent style, e.g:
<style name="DialogNoTitle" parent="#android:style/Theme.Dialog">
<item name="android:windowNoTitle">true</item>
</style>
Now I want to do the same with drawable resources, something like this:
<shape xmlns:android="http://schemas.android.com/apk/res/android" name="ChildDrawable" parent="#drawable/ParentDrawable">
<solid android:color="#android:color/white"/>
<corners android:radius="6dp"/>
</shape>
Is there a way to accomplish this functionality?
Short answer - you cannot extend and override drawables. However,
Some drawables can be created from other drawables. e.g. layer-list will allow you to draw layers of drawables one on top of another. Refer to http://developer.android.com/guide/topics/resources/drawable-resource.html .
You create Drawable objects in code.
Related
I am working on a project where I will have many buttons of the same size.
I was wondering if there is a way to create a standard button formatting with a set
height
weight
padding
etc.
I have looked into styles but I am not sure if that is the road I should be taking. I also thought about extending the button class and programmatically setting these values to a standard.
Hello, how are you!
The best way is to create a style in the style.xml resource, all the necessary features.
just add the style with style = "#style/mystyle" to all the buttons.
example:
<style name="mystyle">
<item name="android:textSize">20sp</item>
<item name="android:gravity">center|center_horizontal</item>
<item name="android:textAlignment">center</item>
</style>
In the same way when you want to create a customized button (java/kotlin)
it will also be added in the same way.
Greetings.
You can create a reusable component in a separate .xml file and use it everywhere since you said the dimensions exactly the same all across the app. Here is a sample:
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle" >
<corners android:radius="4dp" />
<!--customize and animate stuff-->
<gradient
android:angle="270"
android:endColor="#88b823"
android:startColor="#b0dc54" />
</shape>
To make it short, this is what I would like to add a custom button that looks like this:
IMG is a .png file in my mipmap folders and SOME TEXT is just a string value. What the dashed line is added just as a separator in the image, not in the button.
The issue is that the rounded edges don't appear where the image is added. It looks like this:
My questions are the following:
Can this be achieved?
Is there a way to override the <solid /> attribute in <shape />?
I will have to create 10 of these buttons each with different colors and if I add android:color with a different value, the color does not change
When adding the image, it makes me choose only one (e.g. the mdpi one). If this will be displayed on larger screens, will it take a different .png image based on the size?
Is there a specific type of button I should use? I would like to revert the colors when the button is pressed and stay as pressed. I have a vague idea about how this can be achieved, but is there a way to do this for the .png files as well or do I need to import into the project others with the colors already inverted and just switch them?
custom_button.xml
<shape
xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<corners
android:topLeftRadius="250px"
android:bottomLeftRadius="250px"
android:topRightRadius="50px"
android:bottomRightRadius="50px" />
<solid
android:color="#color/YellowPrimary"/>
</shape>
button_styles.xml
<resources>
<style name="CategoryToggle">
<item name="android:background">#drawable/custom_button</item>
<item name="android:textAllCaps">true</item>
</style>
<style name="CategoryToggle.First">
<item name="android:color">#color/bluePrimary</item> // Does not override <solid>
<item name="android:drawableLeft">#mipmap/icon_48mdpi</item>
<item name="android:text">#string/first_cat</item>
</style>
</resources>
button_layout.xml
<android.support.constraint.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent">
<Button
style="#style/CategoryToggle.History"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:layout_marginEnd="8dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</android.support.constraint.ConstraintLayout>
I have no java code at the moment as I just started and trying to implement this weird button format.
This is how it looks at my end:
Absolutely. As I mentioned in the comments, either make sure your drawable has a transparent background, or create a custom button to mask the drawable.
You'll want to use a style attribute apply a different style to each button. By this I mean the color defined in your custom_button.xml should reference a color attribute (something like colorAccent should work in your case), instead of a static color.
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<solid android:color="?attr/colorAccent"/>
</shape>
Then change this color in your button styles instead of android:color.
<style name="CategoryToggle">
<item name="colorAccent">#color/YellowPrimary</item>
</style>
Make sure you have the support library dependency added, or colorAccent will not be available.
Use the android:theme attribute, instead of the style attribute to apply the button theme.
<Button
android:width="wrap_content"
android:height="wrap_content"
android:theme="#style/CategoryToggle"/>
It looks like your drawables are not using resource qualifiers. You'll need to make sure each alternative resource has the exact same name as the original (i.e. your icon_48mdpi.png should instead be called icon_48dp.png for all configurations) and is placed in the corresponding drawable folder for its density. Your drawable resources should look like the following (in the Project view structure, not the Android view structure).
res/
|-- drawable/
| +-- custom_button.xml
|-- drawable-hdpi/
| +-- icon_48dp.png
|-- drawable-mdpi/
| +-- icon_48dp.png
|-- drawable-xhdpi/
| +-- icon_48dp.png
|-- drawable-xxhdpi/
| +-- icon_48dp.png
|-- drawable-xxxhpdi/
| +-- icon_48dp.png
~
To change the color of a drawable based on state, you will need to abstract your color one step further and create a color state list.
res/color/button_color_selector.xml
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:color="#color/pressed_color" android:state_pressed="true"/>
<item android:color="#color/focused_color" android:state_focused="true"/>
<item android:color="#color/state_hovered" android:state_hovered="true"/>
<item android:color="?attr/colorAccent"/>
</selector>
Then you can use this color resource in your shape drawable instead of colorAccent.
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<solid android:color="#color/button_color_selector"/>
</shape>
You can also make each of the colors in your color state list a styleable attribute by defining custom attributes and referencing those attributes in your styles. I won't go into that further for the sake of brevity though.
You can do this for drawables similarly by creating a state list drawable.
Lastly, you'll want to get into the habit of using dp instead of px unless you are absolutely certain you want to use px. This will prevent strange appearances at different screen densities.
On 4 "is there a way to (revert colors when pressed) for the .png files".. yes there are ways.
The by-the-book option is to use xml styles to specify different drawables for different button states. Consider ImageButton which has an example of this in its docs: https://developer.android.com/reference/android/widget/ImageButton.html
Another option is to apply tints or other filters to the drawable in your own code in button interaction callbacks. Compared to using different drawables, in this approach you add custom code instead of leveraging builtin resources, so you may wind up testing and debugging more on different devices, and you get full control so you can do custom animations and other elaborate things that may not fit well into Android resource tooling. Can checkout setColorFilter for starters:
https://developer.android.com/reference/android/widget/ImageView.html#setColorFilter(int,%20android.graphics.PorterDuff.Mode)
Thanks for adding some code and images or your problem, helps in understanding it.
Big shout out to Bryan for his insight on this matter. It led me to the paths I needed to follow in order to solve this issue.
I am posting this answer so that others with similar cases will know the steps. Although I have quite a number of files, this procedure did the trick perfectly.
I have my custom button as described in the link provided by Bryan.
For the rest I have the following:
Change the text color:
button_text_color.xml
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:color="#color/redPrimary" android:state_pressed="true"/>
<item android:color="#color/redDark"/>
</selector>
Change the background: button_background.xml
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_pressed="true">
<shape android:shape="rectangle">
<corners android:bottomLeftRadius="400dp" android:radius="100dp" android:topLeftRadius="400dp" />
<solid android:color="#color/redDark" />
</shape>
</item>
<item android:state_enabled="true">
<shape android:shape="rectangle">
<corners android:bottomLeftRadius="400dp" android:radius="100dp" android:topLeftRadius="400dp" />
<solid android:color="#color/redPrimary" />
</shape>
</item>
</selector>
Change the drawable: button_drawable.xml
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="#drawable/ic_button_inverted" android:state_pressed="true" />
<item android:drawable="#drawable/ic_button"/>
</selector>
button_style.xml
<style name="MyButtonStyle">
<item name="android:background">#drawable/button_background</item>
<item name="android:drawableLeft">#drawable/button_drawable</item>
<item name="android:text">#string/button_text</item>
<item name="android:textColor">#color/button_text_color</item>
</style>
Implementing it in main_layout.xml
<packagename.MyCustomButton xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="#+id/myButton"
style="#style/MyBUttonStyle"
android:layout_marginBottom="8dp" />
Just use the official library.
Add the MaterialButton component using these attributes:
app:icon to add the icon
app:iconGravity to decide the position of the icon
app:iconPadding to increase/decrease the space between the icon and the text
android:paddingLeft the padding between the edge and the icon
<com.google.android.material.button.MaterialButton
android:paddingLeft="2dp"
app:icon="#drawable/ic_add_24px"
app:iconGravity="start"
app:iconPadding="4dp"
app:shapeAppearanceOverlay="#style/ShapeAppearanceOverlay.Button.RoundedRight"
.../>
With the app:shapeAppearanceOverlay you can customize the shape of the component.
<style name="ShapeAppearanceOverlay.Button.RoundedRight" parent="">
<item name="cornerFamily">rounded</item>
<item name="cornerSizeTopRight">4dp</item>
<item name="cornerSizeBottomRight">4dp</item>
<item name="cornerSizeTopLeft">16dp</item>
<item name="cornerSizeBottomLeft">16dp</item>
</style>
You can obtain the same result using a style.
Short question:
Suppose I use this style on a button:
<Button
style="#style/Widget.AppCompat.Button.Borderless.Colored" ...
(or without the "Colored" part).
How do I set the background of it to be a selector that has a different color when being pressed? The default one is quite a bold color on pre-Lollipop...
I want to have all of the style working as the default (including padding), except for the color of the selector.
I think the easiest would be to create a typical multi-state drawable and replicate Android's default button drawable properties for padding, etc...
From the Android SDK AppCompat theme:
<!-- Colored bordered ink button -->
<style name="Base.Widget.AppCompat.Button.Colored">
<item name="android:background">#drawable/abc_btn_colored_material</item>
<item name="android:textAppearance">#style/TextAppearance.AppCompat.Widget.Button.Inverse</item>
</style>
They use the drawable abc_btn_colored_material, whose source is like this: https://github.com/dandar3/android-support-v7-appcompat/blob/master/res/drawable/abc_btn_borderless_material.xml
You can see it's just a layer list with the following multi-state drawable:
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_focused="true" android:drawable="#drawable/abc_btn_default_mtrl_shape"/>
<item android:state_pressed="true" android:drawable="#drawable/abc_btn_default_mtrl_shape"/>
<item android:drawable="#android:color/transparent"/>
</selector>
https://github.com/dandar3/android-support-v7-appcompat/blob/master/res/drawable/abc_btn_default_mtrl_shape.xml
<inset xmlns:android="http://schemas.android.com/apk/res/android"
android:insetLeft="#dimen/abc_button_inset_horizontal_material"
android:insetTop="#dimen/abc_button_inset_vertical_material"
android:insetRight="#dimen/abc_button_inset_horizontal_material"
android:insetBottom="#dimen/abc_button_inset_vertical_material">
<shape android:shape="rectangle">
<corners android:radius="#dimen/abc_control_corner_material" />
<solid android:color="#android:color/white" />
<padding android:left="#dimen/abc_button_padding_horizontal_material"
android:top="#dimen/abc_button_padding_vertical_material"
android:right="#dimen/abc_button_padding_horizontal_material"
android:bottom="#dimen/abc_button_padding_vertical_material" />
</shape>
</inset>
So just make it yours :) Copy the drawable xml into your app and customize it.
I don't know if you will be able to access the #dimen/... from outside the SDK package, those values are defined in the SDK Values.xml, take a look at https://github.com/dandar3/android-support-v7-appcompat/blob/master/res/values/values.xml . If it's not possible, just create your own version of the dimens in your values.xml
If there's a cleaner solution any android guru can point out, I'll be glad to know!
Requested Update
Another trick is to apply a ColorFilter to the whole Button View. It's a very simple thing but not useful for all situations, as the color filter will blindly change the view as a whole (including Font color, borders, etc...). However, smartly selecting Alpha on the colors, could work for specific situations.
I'd suggest a LightningColorFilter. To apply a color filter to the Button, or to any view, you can do something like
myButton.getBackground().setColorFilter(new LightingColorFilter(Color.WHITE, Color.RED));
Check out what every color means here: http://developer.android.com/reference/android/graphics/LightingColorFilter.html
i am using AppCompat 22.1.1.
For some reasons, my app can change its theme on the fly, during the user navigation. (i.e. move to an another part of the app, like in the google play store app, when you're moved from the "my apps" part, to the "movie" part for example)
To avoid creating one drawable background per theme, i tried to create a background like this :
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<solid android:color="?attr/colorPrimary" /></shape>
When changing the theme programmatically, i suppose the colorPrimary will change too, and a button inflated after that, will be tinted with the colour of the new theme.
But i have an inflate exception on pre-lollipop (but works on lollipop). The drawable cannot find the attribute attr/colorPrimary, why ?
Here is the simple theme.xml i'm using :
<style name="MyTheme" parent="Theme.AppCompat.Light.DarkActionBar">
<item name="colorPrimary">#color/my_blue</item>
<item name="colorPrimaryDark">#color/my_blue_dark</item>
<item name="colorAccent">#color/my_blue_light</item>
</style>
The colors are in values/colors.xml, just hexa colors.. And all resources are in the "values" dir, and NOT in values-r21 dir.
Create a color_primary.xml color resource in res/color/:
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:color="?attr/colorPrimary"/>
</selector>
Then reference this in your drawable:
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<solid android:color="#color/color_primary" />
</shape>
From my experience, I don't know, but I'm pretty sure there's nothing you can do via XML. Lots of framework resources are provided twice, in the form some_drawable_dark.xml and some_drawable_light.xml; it looks like you can't reference theme values from drawable and color folders.
So you should either:
Create x static resources, where x is the number of themes you are putting in;
Operate at runtime, using setColorFilter() and such. Depending on the case, it can be hard.
I have this in res/values/styles.xml
<style name="ListViewRowBorder" >
<item name="android:width">1dp</item>
<item name="android:color">#color/listview_row_stroke_color</item>
</style>
I am able to access color resources.How do i access this style resource in res/drawable/file.xml (shown below)
The way i have tried below does not seem to work.
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<solid android:color="#color/list_item_dark_background"/>
<stroke style="#style/ListViewRowBorder" />
</shape>
</item>
i don't think you can use a style for a stroke, please have a look at:
http://developer.android.com/guide/topics/resources/drawable-resource.html#stroke-element
I'm not sure if you can use Styles with shapes
As per the Style dev page:
A style can be applied to an individual View (from within a layout
file) or to an entire Activity or application (from within the
manifest file).