In my layout, I am using falling for predefined values for android:textAppearence. But, there are, as I found, at least 2 ways to define them, which yields a very different result (like, different font size and alpha and weight value):
android:textAppearance:="#style/TextAppearance.MaterialComponents.Caption". This #style/... values are defined in (in my case) in a file
~/.gradle/caches/transforms-2/files-2.1/11ab83d2971a1126be493aa33fdd0f6e/material-1.1.0/res/values. This, for example, is defined as:
<style name="TextAppearance.MaterialComponents.Caption" parent="TextAppearance.AppCompat.Caption">
<item name="fontFamily">sans-serif</item>
<item name="android:fontFamily">sans-serif</item>
<item name="android:textStyle">normal</item>
<item name="android:textAllCaps">false</item>
<item name="android:textSize">12sp</item>
<item name="android:letterSpacing">0.0333333333</item>
</style>
android:textAppearance="#android:style/TextAppearance.Material.Menu". This is defined in
/home/rudra/.Android/Sdk/platforms/android-29/data/res/values/styles_material.xml. This is defined as:
<style name="TextAppearance.Material.Menu">
<item name="textSize">#dimen/text_size_menu_material</item>
<item name="fontFamily">#string/font_family_menu_material</item>
</style>
The main problem to me is, I am not sure which one to use for more consistent user experience. I am sure, the file for case 2 will not be shipped with my app, but maybe the user will have styles_material for his android-xx. But by realizing how values have changed with material-1.0.0 and 1.1.0, I am a bit sKeptic to depend on these values, but I am not sure.
On the other hand, this is a considerable work to redefine textAppearance for all possible scenarios.
So, I am looking for suggestions on how to define the textAppearence, and among case 1 and case 2, which should I use for more consistent result across devices
If you want to define a custom textAppearance, you can define as follow. I use material-components-android.
In style.xml
<!-- Base application theme. -->
<style name="AppTheme" parent="Theme.MaterialComponents.Light">
<!-- Customize your theme here. -->
<item name="colorPrimary">#color/primaryColor</item>
<item name="colorPrimaryDark">#color/primaryDarkColor</item>
<item name="colorPrimaryVariant">#color/primaryLightColor</item>
<item name="colorOnPrimary">#color/primaryTextColor</item>
<item name="colorSecondary">#color/secondaryColor</item>
<item name="colorSecondaryVariant">#color/secondaryDarkColor</item>
<item name="colorOnSecondary">#color/secondaryTextColor</item>
<!-- This line is important -->
<item name="textAppearanceHeadline3">#style/TextAppearance.Headline3</item>
</style>
<style name="TextAppearance.Headline3" parent="TextAppearance.MaterialComponents.Headline3">
<!-- Define your custom appearance here. -->
<item name="fontFamily">#font/work_sans_black</item>
<item name="android:textAllCaps">false</item>
<item name="android:textSize">#dimen/text_size</item>
<item name="android:textStyle">bold</item>
<item name="android:textColor">#color/primaryTextColor</item>
</style>
You can use this-TextAppearance.Headline3 in layout as follow.
<TextView
android:id="#+id/tvAppName"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="#string/app_name"
android:textAppearance="?attr/textAppearanceHeadline3"
/>
This is my usage for custom text appearance. You can define as you like.
If you want to customize theme or style, you have to know common attributes. You can see it at this article.
I hope this will be helpful for you.
Looking at TextView.java, there don't appear to be any attributes that are defined by android:textAppearance that are not able to be set on a TextView directly. So there doesn't appear to be any technical reason that one needs to use them over defining attributes in styles directly, at least in one's ability to configure how a TextView looks.
This means that you can avoid using it in your layouts as well as your styles without any adverse effects, provided that you override all the associated attributes. The base theme defines many different standard android:textAppearances for the various widgets, so you should check that all the widgets are properly overridden.
Based on my experience, android:textAppearance is most useful if you wish your app to appear to integrate into the rest of the device. So if you need big text, you can use android:textAppearance="?android:attr/textAppearanceLarge" and now your text is large! No need to know how many sp that means for the given device/screen size/etc..
However if your app is highly styled and you'd otherwise be overriding all the text sizes anyhow, the value-add of android:textAppearance diminishes. You can certainly use it as referenced in the posts above, but if that doesn't fit into your styling system, then feel free to omit it. It's just another tool to help you get a good-looking app across all devices.
I would also recommend making extra-sure that you try your app across a variety of devices if you choose not to use it, just to make sure that you didn't neglect to override a default android:textAppearance.
Related
I've been with a dilemma for a while that I don't know how to solve it properly. I want to use DRY (Don't Repeat Yourself), but not apply bad practices in styles (such as set the layout attributes inside them).
This is my case...
To have the text styles encapsulated in my projects, I usually use the following:
I have a style called Wrap_Content
<style name="WrapContent">
<item name="android:layout_width">wrap_content</item>
<item name="android:layout_height">wrap_content</item>
</style>
On the one hand, I have a style called Tv that inherits from WrapContent:
<style name="Tv" parent="WrapContent">
<item name="android:fontFamily">#font/font_foo</item>
<item name="android:textColor">#color/color_foo</item>
</style>
As you can see, apart, the Tv style has a default font and text color
If for example I want to use a font size of 15sp, I apply this style:
<style name="Tv.15">
<item name="android:textSize">15sp</item>
</style>
And so on...
Well, the issue is that all the TextView of my project I set wrap_content both width and height.
Therefore, doing things like this simplifies the layouts XML a lot and it increases the readability and grouping common behaviors.
Example:
<TextView
style="#style/Tv.15"
android:text="#string/foo"/>
And if in any case, I want to change any attribute, I have only to overwrite it from where I call it.
The dilemma is that I am mixing textAppearance styles with layout ones. I have thought about separating this ... but I have not just resolved the main issue, that I am setting layout attributes on it, something that I should know nothing more than its own view, and not its container.
But what does not convince me at all is to do something like this:
<style name="Tv">
<item name="android:fontFamily">#font/font_foo</item>
<item name="android:textColor">#color/color_foo</item>
</style>
<style name="Tv.15">
<item name="android:textSize">15sp</item>
</style>
<TextView
style="#style/Tv.15"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="#string/foo"/>
I don't want to repeat a million times with the same attributes if these are common. Or yes I see what it brings ... technical debt. Therefore, it does not seem like a valid option.
I have searched quite a lot and the truth is that I have not found anything that convinces me and I would like to reach something elegant, since it is something that I use at all times and I don't like it.
Well... what do you think about it?
Thank you so much!!!
EDITED 2019-11-08
I have thought a new approach adding a new layer of styles, the #style/TextAppearance. It is like this:
<style name="WrapContent">
<item name="android:layout_width">wrap_content</item>
<item name="android:layout_height">wrap_content</item>
</style>
<style name="TextAppearance">
<item name="android:fontFamily">#font/font_foo</item>
<item name="android:textColor">#color/color_foo</item>
</style>
<style name="TextAppearance.15">
<item name="android:textSize">15sp</item>
</style>
<style name="Tv" parent="WrapContent">
<item name="android:textAppearance">#style/TextAppearance</item>
</style>
<style name="Tv.15">
<item name="android:textAppearance">#style/TextAppearance.15</item>
</style>
This add a little bit of complexity to the system, but it splits the layout and the textAppearance attributes. Moreover, it allows use the TextAppearance style for buttons, editTexts an so on.
In our most recent Android Dev Summit, two of my colleagues gave a talk on how to use Theme & Style. We advice that you use Themes for View groups and their children and styles for simpler views. Perhaps your layout needs can be met by using Themes and then reserving styles for text appearances and such. Beyond that, efficacy should guide how you structure your style objects.
I know I can add different XMLs for different API levels, for example having different styles for values-v21 and values-v19. What I'm trying to understand is how the build system actually works with those different values? So for example if I have the bulk of my styles common across all APIs and one item of one style changes between 21 and the rest, do I:
1) Copy the whole styles.xml into v21 and change the one value I need to change
2) Only add that one style that changed to styles.xml under v21
3) Only add that one item of that one style that changed under 21
It's confusing and I couldn't find any documentation how the built process handles merging styles.
Rules are quite clear:
While running Android selects the best-matching style
If selected style is a child style, Android merges its items with parent best-matching style
If you provide your mutable item via a reference, just define its value to match selected api version.
<style name="SomeStyle">
<item name="someColor">#color/some_color</item>
</style>
You can have some_color.xml in color-v21 folder for API 21 and a common version of this file in a color folder for all other api levels.
Example:
You want to have the following style for non-v21 API
<style name="FinalStyle">
<item name="commonText">It\'s a common text</item>
<item name="specificDrawable">#drawable/icon</item>
<item name="specificColor">#color/primary_color</item>
<item name="specificText">non-v21</item>
</style>
And the following style for v21 API
<style name="FinalStyle">
<item name="commonText">It\'s a common text</item>
<item name="specificDrawable">#drawable/icon</item>
<item name="specificColor">#color/secondary_color</item>
<item name="specificText">v21</item>
</style>
Specific-parameters differ between v21/non-v21 API, common parameters are common.
How to do it?
res/values/styles.xml
<style name="BaseStyle">
<item name="commonText">It\'s a common text</item>
<item name="specificDrawable">#drawable/icon</item>
</style>
<style name="FinalStyle" parent="BaseStyle">
<item name="specificColor">#color/primary_color</item>
<item name="specificText">non-v21</item>
</style>
res/values-v21/styles.xml
<style name="FinalStyle" parent="BaseStyle">
<item name="specificColor">#color/secondary_color</item>
<item name="specificText">v21</item>
</style>
res/drawable/icon.png
Common icon
res/drawable-v21/icon.png
v21 icon
When Android searches FinalStyle for v21, it selects FinalStyle definition from res/values-v21 as best-matching style, and merges with BaseStyle. In this example there is also another best-matching resource search, when Android searches #drawable/icon.
This is for anyone who comes across this and is still just as confused as I was, even after a lot of reading and trial & error. Hopefully this helps.
The folder structure is like #Dmitry stated.
res/values/styles.xml
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:tools="http://schemas.android.com/tools">
<style name="AppBase" parent="Theme.MaterialComponents.NoActionBar">
<!-- simple: overrides colorPrimary in parent theme -->
<item name="colorPrimary">#color/brand_blue</item>
<item name="colorSecondary">#color/brand_grey</item>
<!-- sets the attributes in materialButtonStyle with style: myMaterialButton -->
<!-- the materialButtonStyle attribute is what actually changes the button settings -->
<item name="materialButtonStyle">#style/myMaterialButton</item>
</style>
<!-- this style consists of common 'attributes' among all API versions -->
<!-- you can choose to add a parent to inherit an additional style -->
<!-- unlike the materialButtonStyle attribute, this parent is not necessary to change the button settings -->
<style name="myMaterialButton" parent="Widget.MaterialComponents.Button">
<item name="cornerRadius">60dp</item>
<item name="android:paddingVertical" tools:targetApi="26">20dp</item>
</style>
<!-- this will add on and override AppBase and should include any changes that differ from other API versions -->
<style name="AppBaseChanges" parent="AppBase">
<!-- to inherit myMaterialButton, you don't have to include it in here, since it's in AppBase -->
<!-- however, if you want to extend myMaterialButton, create a new style as its child -->
<item name="materialButtonStyle">#style/myMaterialButtonAPI_All</item>
</style>
<!-- make sure the parent is myMaterialButton to inherit/override its settings -->
<!-- this will be picked for all APIs lower than other styles like this -->
<style name="myMaterialButtonAPI_All" parent="myMaterialButton">
<item name="backgroundTint">?attr/colorPrimary</item>
</style>
</resources>
res/values-v2/styles.xml
<?xml version="1.0" encoding="utf-8"?>
<resources>
<!-- restate the same declaration as the other xml file-->
<style name="AppBaseChanges" parent="AppBase">
<!-- use a different name (...API_2) for the overriding style -->
<item name="materialButtonStyle">#style/myMaterialButtonAPI_2</item>
</style>
<style name="myMaterialButtonAPI_2" parent="myMaterialButton">
<item name="backgroundTint">?attr/colorSecondary</item>
</style>
</resources>
Set the manifest theme to AppBaseChanges. The app will pick only one AppBaseChanges style to apply changes, so be sure to carefully override the right styles to ensure you are inheriting from lower level versions.
For some reason, AndroidStudio doesn't do a good job at all previewing themes, so before you think it's not working, relaunch the app to see the changes. There are also situations where I have no idea why it wasn't updating the setting and couldn't find where it was overriding the theme. In those cases you can dig further, or avoid the hassle and just apply the relevant style directly to the view.
Here's the order of precedence for the sample themes described above. The higher the style, the higher precedence it has and will override the lower style.
either myMaterialButtonAPI_All or myMaterialButtonAPI_2
AppBaseChanges (only one is chosen)
myMaterialButton
Widget.MaterialComponents.Button
AppBase
Theme.MaterialComponents.NoActionBar
You may maintain only one styles.xml(default) file for all the device version.
Checkout my answer to How to remove repeating of similar styles in v19/styles.xml and v21/styles.xml files
https://stackoverflow.com/a/53445541/5745574
According to Android Developer Design Style for Typography, there are 4 TextAppearances (Micro, Small, Medium, Large).
But there is no predefined style for Micro:
?android:attr/textAppearanceMicro
android:style/TextAppearance.Micro
None of them can be found in sources of Android. What am I missing?
Good question! After researching a little bit, I didn't find any results for "TextAppearance.Micro".
On the other hand, I found the official Android source for style resource related to TextAppearances.
<style name="TextAppearance.Small">
<item name="android:textSize">14sp</item>
<item name="android:textColor">?textColorSecondary</item>
</style>
<style name="TextAppearance.Medium">
<item name="android:textSize">18sp</item>
</style>
<style name="TextAppearance.Large">
<item name="android:textSize">22sp</item>
</style>
As you can see, all of them only affect the text size (except TextAppearance.Small, which also affect the text color). So, I can safely say that the site displays the text sizes as a guideline only. On the other hand, you can always add a custom style to support TextAppearance.Micro! Just insert this inside styles.xml.
<style name="TextAppearance.Micro" parent="#android:style/TextAppearance.Small">
<item name="android:textSize">12sp</item>
</style>
and use it like:
<TextView
...
android:textAppearance="#style/TextAppearance.Micro" />
On a bit related note, when I searched "textAppearanceMicro", I found 3 projects on GitHub. All of them adding some custom attributes, which one of them is textAppearanceMicro. Also, all of them are using ActionBarSherlock. I don't know whether there is a connection between textAppearanceMicro and ActionBarSherlock, but I didn't find that attribute was used anywhere in the code.
If you happen to use the Google AppCompat Library, you can use the following:
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textAppearance="#style/TextAppearance.AppCompat.Caption" />
I am developing a android app and i believe using the official text style can give user the best experience across all kinds of platforms and devices. But i found that TextAppearance.Large has the bigger text size but TextAppearance and TextAppearance.Small has the same size but only different in color, i don't know why.
They are not the same size. If you look at the styles.xml in the sdk\platforms\android-4.2\data\res\values\ you will see how they are defined.
As to why they are the same text size (which they are not), you will want to ask Google. ;-)
<style name="TextAppearance">
<item name="android:textColor">?textColorPrimary</item>
<item name="android:textColorHighlight">?textColorHighlight</item>
<item name="android:textColorHint">?textColorHint</item>
<item name="android:textColorLink">?textColorLink</item>
<item name="android:textSize">16sp</item>
<item name="android:textStyle">normal</item>
</style>
<style name="TextAppearance.Small">
<item name="android:textSize">14sp</item>
<item name="android:textColor">?textColorSecondary</item>
</style>
I believe using the official text style can give user the best
experience across all kinds of platforms and devices
The best you can do is define your own styles in styles.xml. That, in my view is better than relying solely on the default themes. They do change from manufacturer to manufacturer. Your idea is true only in cases of devices using the stock Android OS.
I have the following code
<TextView
android:text="#string/hello"
style="?android:attr/listSeparatorTextViewStyle" />
and I will get the following effect.
However, I am not happy with the color line. I would like to have something like
I would like it to have blue color line as in holo. I try the following custom style.
<style name="MyOwnListSeperatorTextViewStyle">
<item name="android:background">#android:drawable/list_section_divider_holo_light</item>
<item name="android:textAllCaps">true</item>
<!-- Copy from Widget.TextView.ListSeparator -->
<item name="android:background">#android:drawable/dark_header_dither</item>
<item name="android:layout_width">match_parent</item>
<item name="android:layout_height">wrap_content</item>
<item name="android:textStyle">bold</item>
<item name="android:textColor">?textColorSecondary</item>
<item name="android:textSize">14sp</item>
<item name="android:gravity">center_vertical</item>
<item name="android:paddingLeft">8dip</item>
</style>
But it won't work, as I get the following error.
error: Error: Resource is not public. (at 'android:background' with value '#android:drawable/dark_header_dither').
Have idea how can I change the line color used in listSeparatorTextViewStyle?
I needed to do this to override the typical Holo Spinner style (I didn't want the underlined item - i just wanted the arrow), and I think this can be overridden in precisely the same manner:
First off, you want to find the item you wish to override in the android styles source. There is an incredibly useful SO answer that contains all of the styles (and the names to override them) right here: Set Dialog theme to parent Theme in Android
I believe yours is the following line:
<item name="listSeparatorTextViewStyle">#android:style/Widget.Holo.Light.TextView.ListSeparator</item>
This takes us on a journey to find the style Widget.Holo.Light.TextView.ListSeparator which should live somewhere on your very own computer! But I'll make it easy and just c&p it:
<style name="Widget.Holo.Light.TextView.ListSeparator" parent="Widget.TextView.ListSeparator">
<item name="android:background">#android:drawable/list_section_divider_holo_light</item>
</style>
Now, you probably want to leave well enough alone, and just look at that background drawable. You will find it is a grey 9patch file that looks like the sinister grey line you seek to avoid.
We need to override this. I am sure there are a number of ways to do this, but I do so by customizing the theme of the application. Here is the themes.xml file:
<style name="AppTheme" parent="#android:style/Theme.Holo.Light.NoActionBar">
<item name="android:listSeparatorTextViewStyle">#style/MyOwnListSeperatorTextViewStyle</item>
</style>
<style name="MyOwnListSeperatorTextViewStyle" parent="Widget.TextView.ListSeparator">
<item name="android:background">#drawable/make_your_own_blue_9_patch_here</item>
</style>
Notice how we used the listSeparatorTextViewStyle from that previous SO post? And the parent of the custom style is the Widget.TextView.ListSeparator from android's style source? All very important.
Now you just need to apply this theme to your app, but I am assuming you have a theme already. If you haven't already, you will need to make your own 9patch but I would just look at the list_section_divider_holo_light.9.png file on your computer, and make the grey parts blue, and then make a copy and place it into your own drawables folder.
Hope this works and is helpful!