Create style that extends from default style set in application theme - android

I have set the default button style in my application theme, so that it is applied to every button in the app.
There are few buttons that need to have their text capitalized in addition to default style. So what I'm trying to do is something like this
(the following code snippet doesn't work as I cannot set parent="?attr/materialButtonStyle"):
<style name="capitalizedButton" parent="?attr/materialButtonStyle">
<item name="android:textAllCaps">true</item>
</style>
Is it possible to extend a style defined in theme.xml? I don't want to refer to the local style that I'm setting in theme as that can change later, instead I intend to access it using theme attribute.

If the capitalization happens in addition to your custom default button style, there is no need to make the capitalization style a child of ?attr/materialButtonStyle (This cannot be done anyhow, as ?attr references an attribute in the currently applied theme). You can read more about it here.
styles.xml
<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
<item name="buttonStyle">#style/DefaultButton</item>
</style>
<style name="DefaultButton" parent="Widget.AppCompat.Button">
<item name="android:textColor">#color/white</item>
</style>
<style name="AllCapsButton">
<item name="android:textAllCaps">true</item>
</style>
layout.xml
<Button
android:id="#+id/button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Button"
android:theme="#style/AllCapsButton"/>
In the code above, button gets the DefaultButton style (white text) applied by the theme, and the AllCapsButton style (capitalized text) applied in the layout file.

You can directly pass style to Button widget in you XML like:
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
style="capitalizedButton"/>
Hope you get the answer. If not please explain your question.

Related

Overriding editTextStyle doesn't work with latest Material Components base style

In an app of mine, I'm using the Theme.MaterialComponents.Light.NoActionBar as a base style. In this style, which I call AppTheme, I'm trying to override editTextStyle to provide a custom style for com.google.android.material.textfield.TextInputEditText (according to the source code, it uses R.attr.editTextStyle as a default style).
This is my current theme, related to the TIEditText and TILayout:
<style name="AppTheme" parent="Theme.MaterialComponents.Light.NoActionBar">
[ primary and secondary colors, OnColors, etc.]
<item name="editTextStyle">#style/AppTheme.TextInputEditText</item>
<item name="textInputStyle">#style/AppTheme.TextInputLayout</item>
[ Custom attribute for testing, defined in attrs.xml ]
<item name="textInputEditTextStyle">#style/AppTheme.TextInputEditText</item>
</style>
For some reason, even though I set editTextStyle, if I use it in code, it does not get applied:
<com.google.android.material.textfield.TextInputLayout
android:id="#+id/tilFirstName"
style="?attr/textInputStyle"
android:hint="#string/label_firstname"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
<com.google.android.material.textfield.TextInputEditText
android:id="#+id/firstName"
style="?attr/editTextStyle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:inputType="textPersonName"
android:text="#={viewModel.firstName}" />
</com.google.android.material.textfield.TextInputLayout>
However if I replace the style of firstName with ?attr/textInputEditTextStyle, it works.
Why can't I override editTextStyle in the default theme? What the hell is going on?
Target SDK is 28, minSDK is 21, Material library version is 1.1.0-alpha06
Let's just move past the part where we all recognize that Android themes and styles are singularly the most absurd wasteland of hackery and guesswork ever devised by human beings.
This is an expansion on the previous answer. Same silly 'hack'. I was able to style the TextInputEditText by setting editTextStyle, but not where it intuitively belongs, but rather inside a custom materialThemeOverlay nested within the style defined for textInputStyle. Witness:
<style name="AppTheme" parent="Theme.MaterialComponents.DayNight.DarkActionBar">
<!-- works fine -->
<item name="textInputStyle">#style/AppTheme.TextInputLayoutStyle</item>
<!-- should work fine, doesn't work, happily ignored -->
<!-- <item name="editTextStyle">#style/AppTheme.TextInputEditTextStyle</item> -->
</style>
<style name="AppTheme.TextInputLayoutStyle" parent="Widget.MaterialComponents.TextInputLayout.OutlinedBox">
<!-- other props (boxBackgroundMode, boxBackgroundColor, boxStrokeColor, etc) -->
<!-- can we set editTextStyle from here? Of course not! We should magically know we need a material theme overlay-->
<item name="materialThemeOverlay">#style/AppTheme.MaterialThemeOverlay</item>
</style>
<!-- style inception! a style, child of another style, whose only purpose is to refer to yet another style -->
<style name="AppTheme.MaterialThemeOverlay">
<item name="editTextStyle">#style/AppTheme.TextInputEditTextStyle</item>
</style>
<!-- finally, the style we SHOULD have been able to set from the theme -->
<style name="AppTheme.TextInputEditTextStyle" parent="#style/Widget.MaterialComponents.TextInputEditText.OutlinedBox">
<item name="android:textColor">#color/white</item>
</style>
All of the above ridiculousness and ANOTHER day of my life thrown in the trash, just to change the color of text. Thaaaaanks Aaaaaandroid.
For some reason, even though I set editTextStyle, if I use it in code, it does not get applied
It happens because the default styles of the TextInputLayout override the editTextStyle using the materialThemeOverlay attribute.
For example the Widget.MaterialComponents.TextInputLayout.FilledBox has this default style:
<style name="Widget.MaterialComponents.TextInputLayout.FilledBox" parent="Base.Widget.MaterialComponents.TextInputLayout">
<item name="materialThemeOverlay">
#style/ThemeOverlay.MaterialComponents.TextInputEditText.FilledBox
</item>
....
</style>
<style name="ThemeOverlay.MaterialComponents.TextInputEditText.FilledBox">
<item name="editTextStyle">#style/Widget.MaterialComponents.TextInputEditText.FilledBox</item>
</style>

Custom item in theme

I want to create style that I will use in android:textAppearance of my TextView. But this style differs on different API levels, so I want to have:
one common style (for all API levels),
some styles that inherits after that common style in values-v16, values-v21 dirs,
one directive (style / attr ?) I will put in TextView's android:textAppearance parameter (layout files are common for all API levels).
I tried multiple combinations from Google and Stack and finally ended with something like this (which is of course not working):
values/attrs.xml
<attr name="myTextAppearance" format="reference" />
values/styles.xml
<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
<item name="myTextAppearance">#style/CommonTextAppearance</item>
</style>
<style name="CommonTextAppearance">
<item name="android:textColor">#00f</item>
</style>
values-v16/styles.xml
<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
<item name="myTextAppearance">#style/V16TextAppearance</item>
</style>
<style name="V16TextAppearance" parent="CommonTextAppearance">
<item name="android:textSize">20sp</item>
</style>
layout/activity_main.xml
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="#android:id/text1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:text="Lorem"
android:textAppearance="?attr/myTextAppearance"/>
</RelativeLayout>
And currently I'm getting an error:
Couldn't find theme resource ?attr/myTextAppearance for the current theme
I spent a lot of time trying to do this on my own, but I failed. Can you help me please?
This is because the ?attr/[name] value is a reference to the owner's style parameter. If there's no paraneter with [name], the code falls back to the default parameter set which is defined in a theme.
Basically, the TextView class checks the inline style defined in layout xml, then the default theme and then the textViewStyle reference. None of them contains your myTextAppearance attribute.
You have to override the view and in constructor use obtainStyledAttributes. Pass your theme attribute as the default one. Then parse the value and use it as the new text appearance.
http://developer.android.com/reference/android/content/res/Resources.Theme.html#obtainStyledAttributes(android.util.AttributeSet,int[],int,int)
http://developer.android.com/reference/android/view/View.html#View(android.content.Context,android.util.AttributeSet,int)

Overriding text color for buttons not working

I've created a custom theme for a button bar using buttonBarStyle on each button and buttonBarButtonStyle on the layout.
It works fine, but i want to change the text color for the buttons, but it still takes the default color (#android:color/primary_text_holo_light)
My code is:
<style name="ButtonBarTheme" parent="AppTheme">
<item name="buttonBarStyle">?android:attr/buttonBarStyle</item>
<item name="buttonBarButtonStyle">?android:attr/buttonBarButtonStyle</item>
<item name="android:textColor">#ffffff</item>
</style>
And for the layout:
<LinearLayout
style="?android:attr/buttonBarStyle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#0090cc"
android:orientation="horizontal" >
<Button
android:id="#+id/bar_button1"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
style="#style/ButtonBarStyleButton"
android:text="Button 1" />
<Button
android:id="#+id/bar_button2"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
style="?android:attr/buttonBarButtonStyle"
android:text="Button 2" />
</LinearLayout>
AppTheme has parent AppBaseTheme, which has parent android:Theme.Holo.Light.DarkActionBar.
What do I have to do to change the buttons' text color?
EDIT:
I tried applying the color on each element and it works, but I want to change it by overriding the color in the style file (keep all design properties in one place, similar to CSS).
Also, I have tried to create a style that has the properties of ?android:attr/buttonBarStyle"
<style name="ButtonBarStyleButtonBase">
<item name="buttonBarButtonStyle">?android:attr/buttonBarButtonStyle</item>
</style>
<style name="ButtonBarStyleButton" parent="ButtonBarStyleButtonBase">
<item name="android:textColor">#ffFFff</item>
</style>
but it behaves strangely: the color is updated, but no properties form the parent style are visible.
I'd like to preface this answer by saying that I don't fully understand what a button bar is. The android developer docs don't seem to have any mention of them, besides defining some style attributes for them, therefore I'm uncertain as to how they map to actual Views/Widgets.
I believe what's going on is that you're missing the distinction between a Style and a Theme. A Style is applied to a View to define it's attributes. A Theme is applied to either the entire Application or an individual Activity and provides the ability to define default styles for the Views displayed within the Application/Activity on which the Theme is applied (among other things).
More to the point though, attributes such as buttonBarStyle and buttonBarButtonStyle will only be used by the Activity to resolve default styles for the button bars and Buttons within button bars. Because these attributes are only resolved by the Activity, they will be ignored if applied directly to your Views.
Therefore if you want to use Themes to apply your Styles, you'll need to create a new Style definition which extends the desired base Style, then assign that custom Style back to the appropriate View Style attribute in your Theme definition.
<style name="CustomButtonStyle" parent="#android:attr/buttonStyle">
<item name="android:textColor">#ffffff</item>
</style>
<style name="AppTheme" parent="#android:style/Theme.Holo.Light.DarkActionBar">
<item name="buttonStyle">#style/CustomButtonStyle</item>
</style>
<application
...
android:theme="#style/AppTheme" >
All Buttons without an explicit style attribute will now appear with white text.
However, for your special case of wanting to display Buttons with a ButtonBar Style, you should note that the ButtonBar Style isn't automatically applied to your Buttons. You have to manually define the Buttons that you want to appear with that Style. As such, the example above will now look more like this:
<style name="CustomButtonBarStyle" parent="#android:attr/buttonBarStyle">
<item name="android:textColor">#ffffff</item>
</style>
<style name="AppTheme" parent="#android:style/Theme.Holo.Light.DarkActionBar">
<item name="buttonBarStyle">#style/CustomButtonBarStyle</item>
</style>
<application
...
android:theme="#style/AppTheme" >
Now apply the the ButtonBar style you have defined for the current Theme:
<Button
...
style="?android:attr/buttonBarStyle"/>
Note that this will apply the ButtonBar style defined for the current Theme (which unless it is overridden by an Activity, it will be the same throughout your application). Also note that because you are referencing the Style defined for the current Theme, it allows you to define the current theme differently depending on desired resource qualifiers, without having to change your layout code.
I tried the other solution given here, with no luck. Attempting to use a private style (android:attr/buttonBarStyle, in this case) as a parent gives errors.
A solution to the problem of changing the color of buttons under buttonBarStyle is to recreate android:attr/buttonBarStyle for yourself by assigning your buttons a background:
android:background="?android:attr/selectableItemBackground"
and then specifying:
android:textColor="#color/your_color"
which will correctly change the text color of the buttons while preserving buttonBarButtonStyle's appearance.

Set a consistent theme for all the editTexts in Android

I have finished making my app. Now, I want to reset all my editTexts to have the layout width as fill parent instead of wrap content.
android:layout_width="fill_parent"
while currently all my editTexts are
android:layout_width="wrap_content"
Is there any way i can do this in a style xml file, instead of individually in each layout?
I currently have this as my styles.xml
<style name="AppTheme" parent="android:Theme.Light">
<item name="android:fontFamily">Verdana</item>
<item name="android:editTextStyle">#style/EditTextStyle</item>
</style>
<style name="EditTextStyle" parent="#android:style/Widget.EditText">
<item name="android:layout_width">fill_parent</item>
<item name="android:textColor">#808080</item>
</style>
But i'm getting an exception saying that layout_width must be specified.
This is my exception:
07-15 11:13:34.872: E/AndroidRuntime(1195): java.lang.RuntimeException: Unable to start activity ComponentInfo{com.passwordkeeper.ui/com.passwordkeeper.ui.ActivityLogin}: java.lang.RuntimeException: Binary XML file line #29: You must supply a layout_width attribute.
Any easy way out or do i have to change the attribute in all my editText's individually?
You can try this one.
Here is the part of the manifest file you need to change to call your custom theme (the custom theme called here is AppTheme:
<application android:name="YourApplication"
android:theme="#style/AppTheme" >
Then in your file styles.xml, create and customize this custom theme:
<style name="AppTheme" parent="#android:style/Theme.Holo.Light">
<item name="android:typeface">YourTypeFace</item>
</style>
You can add the parameters you need inside the style. This will apply the style to all your textviews.
One solution to your problem is to apply a custom theme to all of your activities. In order to do that, you can inherit properties from an existing theme and override the properties that you want to change.
In AndroidManifest.xml, locate the <application> element.
Add the attribute to it:
android:theme="#style/"
Locate styles.xml file in the values folder.
Use the following template:
<style name="ApplicationStyle" parent="android:Theme.Light">
<item name="#android:editTextStyle">#style/customEditText</item>"
</style>
Names used in the above are just examples, you may use your own. As to the parent theme, that is also up to you.
All that is left is the definition of editTextStyle (or whatever name you have chosen for the style). You should inherit properties from Widget.EditText and override the properties that you want to change, like the following:
<style name="customEditText" parent="#android:style/Widget.EditText">
<item name="android:textColor" >#ffffff</item>
</style>
To quote the official android guide:
The parent attribute in the element is optional and specifies
the resource ID of another style from which this style should inherit
properties. You can then override the inherited style properties if
you want to.
I tried to make it easy to understand and follow. I'm a junior dev so while the above solution works for me, it may not be the best one out there. As I said though, it solves the problem rather efficiently.
Unfortunately it is not possible to set layout attributes (layout_*) from a theme (see Layout Parameters documentation and this answer from an Android framework engineer). You must set them on each element or set the layout attributes in a style and let each element reference the style like this:
<Button style="#style/ButtonBig" android:text="my text" />
where ButtonBig is defined like this:
<style name="ButtonBig" parent="android:Widget.Holo.Light.Button">
<item name="android:textSize">20sp</item>
<item name="android:layout_width">match_parent</item>
<item name="android:layout_height">100dp</item>
</style>
I was having this exact issue. For some reason it helped me to drop the android: part in the AppTheme definition, and leave it only as editTextStyle (as mentioned in this answer):
<style name="AppTheme" parent="android:Theme.Light">
<item name="android:fontFamily">Verdana</item>
<item name="editTextStyle">#style/EditTextStyle</item>
</style>
You'll have to set the property style="#styles/EditTextStyle" to all of your EditText components in your application.
define the style attribute for all EditText like below:
<EditText
android:id="#+id/editText1"
android:layout_height="wrap_content"
style="#style/EditTextStyle">
You have to specify layout_width individually to each and every View. There is no way To escape. You can create a LayoutParams object and set its width and height in it and set in it every EditText like
textView1.setLayoutParams(lParams);
textView2.setLayoutParams(lParams);
...

What theme attributes do I need to override to change the blue highlight color of my dialogs?

I have a theme called "greenhighlight" — this theme was generated using the Android Action Bar Style Generator, and inherits from the default ActionBarSherlock theme. The theme does nothing except change the highlight at the bottom of the ActionBar from blue to green.
To theme all my activities, I just do:
<application android:theme="#style/Theme.greenhighlight"...
This works pretty well for activities (note the green highlight on the bottom of the ActionBar):
However, I'm having difficulty theming my dialogs to match my activities:
My "greenhighlight_Dialog" theme is defined as:
<style name="greenhighlight_Dialog" parent="#style/Theme.Sherlock.Dialog">
<item name="android:progressBarStyleHorizontal">
#style/greenhighlight_ProgressBar
</item>
</style>
I'm inheriting from the default Sherlock dialog theme, and overriding the progress bar using the progress bar style as defined by my generated "greenhighlight" theme — you can see that the progress bar is the correct shade of green in the screenshot above.
To use the theme, I'm running the following code:
ContextThemeWrapper ctw =
new ContextThemeWrapper(this, R.style.greenhighlight_Dialog);
AlertDialog.Builder builder = new AlertDialog.Builder(ctw);
...
ProgressDialog pd = new ProgressDialog(this, R.style.greenhighlight_Dialog);
...
My problem is that I have no idea what attributes I need to override. I've been looking through styles.xml and themes.xml as recommended by the Styles and Themes doco (which notes that "the R.style reference, however, is not well documented and does not thoroughly describe the styles") — but there are a lot of styles defined on Theme.Dialog and I'm unsure as to which ones I need to override to get the change I want.
What attributes to I need to override for my dialogs to have green title text, a green highlight bar underneath the title and green check marks for checked list items?
I went digging around the source and came across the alert_dialog_holo.xml layout file. This is the divider view:
<View android:id="#+id/titleDivider"
android:layout_width="match_parent"
android:layout_height="2dip"
android:visibility="gone"
android:background="#android:color/holo_blue_light" />
In the AlertController class, it's visibility is set to VISIBLE if there is a title present. Since the color is hardcoded, there's not going to be a way to overwrite this with a style attribute.
This is the title view:
<com.android.internal.widget.DialogTitle android:id="#+id/alertTitle"
style="?android:attr/windowTitleStyle"
android:singleLine="true"
android:ellipsize="end"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
So it uses the windowTitleStyle. It took a lot of chasing, but I eventually found the style that uses:
<style name="TextAppearance.Holo.DialogWindowTitle">
<item name="android:textSize">22sp</item>
<item name="android:textColor">#android:color/holo_blue_light</item>
</style>
Following the parent styles back, I was able to change the text color only via styles:
<style name="AppTheme" parent="#android:style/Theme.Holo">
<item name="android:alertDialogTheme">#style/AlertDialogStyle</item>
</style>
<style name="AlertDialogStyle" parent="#android:style/Theme.Holo.Dialog">
<item name="android:windowBackground">#android:color/transparent</item>
<item name="android:windowContentOverlay">#null</item>
<item name="android:windowMinWidthMajor">#android:dimen/dialog_min_width_major</item>
<item name="android:windowMinWidthMinor">#android:dimen/dialog_min_width_minor</item>
<item name="android:windowTitleStyle">#style/DialogWindowTitle</item>
</style>
<style name="DialogWindowTitle">
<item name="android:maxLines">1</item>
<item name="android:scrollHorizontally">true</item>
<item name="android:textAppearance">#style/DialogWindowTitleAppearance</item>
</style>
<style name="DialogWindowTitleAppearance" parent="#android:style/TextAppearance.Holo.DialogWindowTitle">
<item name="android:textColor">#00ff00</item>
</style>
Now for the divider, you can't change it via styles, but since you know the id, you can extend the AlertDialog class and intercept it when it creates its layout (onCreate) and change it's color. Though this is handled in the private AlertController class, so I'm unsure how much luck you'll have. I'll look into this more and come back if I come up with anything.
I found a solution to remove the divider: just add the following line in your custom alert dialog style (please refer to AlertDialogStyle in Jason Robinson's answer):
<item name="android:divider">#null</item>"
Then the divider will gone.
If you still want to add a custom divider, you can add it in your custom content layout.

Categories

Resources