Theme attributes not working correctly with VectorDrawable - android

I work on application which supports multi themes, dark and light, with min sdk version 21.
I found out that it's possible to use theme attribute (e.g. ?attr/logo_color) inside VectorDrawable.
So for example, If I set theme attribute to fill color of desired path
<vector ...>
<path
android:pathData="..."
android:fillColor="?attr/logo_color"/>
</vector>
or set theme attribute to tint whole vector
<vector android:tint="?attr/logo_color">
...
</vector>
I run the app (light theme), it sets color correctly, but when I change theme Activity.setTheme() (light to dark), color is not changed. Color is always 'cached' to previous theme's color. Interesting is that this doesn't work on lollipop and marshmallow, however on Android 10 it changes correctly.
On the other hand hand if I set android:tint="" color inside ImageView
<ImageView
...
app:srcCompat="#drawable/ic_logo"
android:tint="?attr/logo_colo"/>
It works with all versions but it of course change color of whole drawable.
Is it any bug or is it possible to use theme attributes inside VectorDrawable on lower apis with run time theme change?

Was facing the same issue, what I did was the following things
In the build.gradle file (app level), set the following (Suggesting this point since you have not mentioned what is the minSdk you are supporting)
android {
defaultConfig {
vectorDrawables.useSupportLibrary = true
}
}
If you are setting such drawables to any kind of views during run time or dynamically then
Instead of this (which is now deprecated)
context.getResources().getDrawable(/*Your resource id*/)
Use this
ContextCompat.getDrawable(context, /*Your resource id*/)
Explanation: What the ContextCompat class does is make sure that the drawable you get is complying to whatever theme is currently being used in your app (Android 5.0+)
If you support even below that, then you can use a ContextThemeWrapper to wrap your current context and apply a specific theme to the drawable and then use it whenever you like
Good things to read
Jorge Castillo's Article about ContextThemeWrapper

Related

light icons in darkmode

In Android Studio, I've added a bunch of icons to my app via res -> New -> Vector Asset. When I tested my app on a device with darkmode, I noticed, that some icons changed their color with the text color, while some remained black.
Upon further investigation I found the ones that changed their color had this attribute:
<vector
android:tint="?attr/colorControlNormal"
while the ones that did not change their color had this one:
<vector
android:tint="#000000"
The fix was easy: I just had to change the tint attribute accordingly.
My question is this: where is the tint attribute controlled in Android Studio? The dialog does have a color attribute, which is set to 000000. But I don't recall doing anything special before to end up with tint="?attr/colorControlNormal".
How can I make Android Studio to always set tint to ?attr/colorControlNormal?

Can we have Android Dark Mode Support for App Icons?

I am trying out with Dark Mode Theme Support for Android 10 for my App.
I am able to work with all other things in Dark Mode except App Launcher Icon.
For reference, I was using below link
https://developer.android.com/guide/topics/ui/look-and-feel/darktheme
I know there is no such mention of App Icon change as per Day/Night theme changes.
Just for confirmation, need all your inputs on will it be possible to change the app icon as per change in theme from normal to dark and vice versa.
Thanks in advance.
Have you checked the Themes and styles section in documentation?
Your themes and styles should avoid hard-coded colors or icons
intended for use under a light theme. You should use theme attributes
(preferred) or night-qualified resources instead.
Here are the two most important theme attributes to know about:
?android:attr/textColorPrimary This is a general purpose text color. It is near-black in Light theme and near-white on Dark themes. It contains a disabled state.
?attr/colorControlNormal A general-purpose icon color. It contains a disabled state.
So the ?android:attr/textColorPrimary and ?attr/colorControlNormal will change based on the theme (black -> white & white -> black). I'm assuming we can set those colors as android:tint property to achieve the dark/white theme for vector icons. The con is your icons need to be black and white only.
To achieve Dark Mode for Icons in Android:
Create the separate resource folder named values-night
Inside values-night folder define your night theme (ex. theme.xml)
Define all the desired colours you want in Dark mode inside theme.xml
Now, inside your icon drawable define the icon tint attribute as follows-
<vector android:height="24dp" android:tint="?attr/colorPrimaryDark" android:viewportHeight="24" android:viewportWidth="24" android:width="24dp"
Using the above code, the icon colour will change based on colours defined in Dark and Light mode theme in your project
Pictures:
res folder values-night
define your Dark mode theme
change icon tint attribute of your Icons
light mode
Dark mode
No, the app icon does not support dark mode.
Apart from the app icon, other images colour can be modified :
Try using
android:drawableTint="#color/black"
OR
app:tint="#color/black"
[
Add xml import by presing ALT+Enter or use following:
xmlns:app="http://schemas.android.com/apk/res-auto"
]
with your desired image.
UPDATE:
or Just use:
ivMyImageView.setColorFilter(ActivityCompat.getColor(context, android.R.color.holo_green_light))
PS:
(Attribute drawableTint is only used in API level 23 and higher)
I think it possible, just launcher doesn't support display it.
BTW, I created new color resource into values-night and values, eg:
<!-- values-night/colors.xml -->
<color name="icon_background">#000000</color>
<!-- values/colors.xml -->
<color name="icon_background">#FFFFFF</color>
Then set background color into app icon:
<!-- mipmap/ic_launcher.xml -->
...
<background android:drawable="#color/icon_background" />
...
<!-- mipmap/ic_launcher_round.xml -->
...
<background android:drawable="#color/icon_background" />
...
App's icon now change when toggle dark mode..., but only for app swicher (icon display on top of window), but lancher does't update...
I've tested on Android 11 on Pixel 4XL phone (using Google Launcher).
Anyone else?
Dark mode is not supported for app icons (launcher icons).
Reason for this:
Some resource qualifiers like locale/density/version code do not
change in day-to-day use and changing icons on those are supported.
But we do not recommend changing app icons and labels based on
frequently changing parameters and it is not supported at most places
in system UI. Users create a memory map between apps and their
corresponding icons/labels. Changing these frequently is disruptive to
the user.
There is an issue filed with google for this and the decision is:
Won't Fix (Intended Behavior)
Issue tracker: https://issuetracker.google.com/issues/147521650?pli=1
Well, you can use ure resource colors. Add night mode variation (right click values, New -> Values resource file, set file name "colors" and qualifier "Night mode". You can do variation of drawable specifically if you want.
The main drawback - it doesn't really stable. I don't know if it's just me, but I'm getting weird behaviour in emulator (sorry, can't test on device right now). Right after install icon is set with correct mode, but when you change to other it doesn't get updated. When you try to move icon it using current theme variation however.
Try to add mipmap-anydpi-v26 & mipmap-night-anydpi-v26 icons in your source code. I tried to add but is a little bit buggy. I theory icons support dark theme XD
Here is an example

Tint Navigation Icon in Toolbar

How to tint menu icons is already covered a few times, like here:
Toolbar icon tinting on Android
Additionally to this solution there is still the problem of the navigation icon.
Applying a Theme(Overlay) to your Toolbar just tints the text and the whitelisted icons (see: https://stackoverflow.com/a/26817918/2417724)
If you set a custom icon (which happens to be quite easy the case, as you need to change it if you don't want to display the default back arrow) then this custom icon does not get tinted.
How do you handle your Icons then?
All my icons are per default black and I don't want to have special white versions just to use them in the Toolbar then.
The appcompat navigation button - which is simply an AppCompatImageButton - can be styled through the toolbarNavigationButtonStyle attribute. The default style for that in the AppCompat themes is Widget.AppCompat.Toolbar.Button.Navigation, and we can extend that style to add a tint attribute value. For example:
<style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">
...
<item name="toolbarNavigationButtonStyle">#style/Toolbar.Button.Navigation.Tinted</item>
</style>
<style name="Toolbar.Button.Navigation.Tinted" parent="Widget.AppCompat.Toolbar.Button.Navigation">
<item name="tint">#color/nav_button_tint</item>
</style>
There are a couple of caveats to be aware of when using this method.
Prior to support library version 25.4.0, AppCompatImageButton did not offer its own tint attribute, and therefore a tint attribute in the app's namespace will not apply (and just won't exist, unless defined elsewhere). It is necessary to use the platform android:tint attribute if using support library version 25.3.0 or earlier.
Unfortunately, this leads to another catch, in that the platform tint prior to Lollipop (API level 21) can handle only simple, single color values, and using a ColorStateList (<selector>) resource value will cause an Exception to be thrown. This poses no problems if the android:tint value is a simple color, but it is often desired to tint the navigation icon to match another theme color attribute, which may very well be a ColorStateList. In this case, it would be necessary to create separate styles in res/values/ and res/values-21/, specifying a simple color value for android:tint in res/values/.
For example, if tinting to match the theme's primary text color:
res/values/styles.xml
<item name="android:tint">#color/normal_text_color</item>
res/values-v21/styles.xml
<item name="android:tint">?android:textColorPrimary</item>
You need only concern yourself with the notes above if you're stuck using a support library version less than 25.4.0.
To effectively set the tint color of the navigation icon programmatically you need to set the drawable first and apply the tint afterwards.
toolbar.setNavigationIcon(R.drawable.ic_back)
toolbar.children.forEach {
(it as? AppCompatImageButton)?.imageTintList =
ColorStateList.valueOf(Color.GREEN)
it.refreshDrawableState()
}

SupportActionMode background color with material design

I'm using Toolbar as setSupportActionBar(toolbar). How i can change action mode background color. I try in my App Theme change attribute:
<item name="android:actionModeBackground">#color/color</item>
But it is not working. How can I solve this problem?
The AppCompat library supports versions of Android that don't have ActionBars, so it needs to define attributes to use that wouldn't exist on those devices. You want to use this:
<item name="actionModeBackground">#color/color</item>
The same goes for any other style/theme attributes you want to use that have been backported. But you'd use platform ones that exist, like android:textColor or android:background.

Lollipop's backgroundTint has no effect on a Button

I have a Button in my Activity, and I'd like it to have my theme's accent color.
Instead of making my own drawables like we had to do pre-Lollipop, naturally I'd like to use the new backgroundTint attribute.
<Button
android:id="#+id/btnAddCode"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:backgroundTint="#color/accent"
android:text="#string/addressInfo_edit_addCode" />
Unfortunately it has no effect, the button stays gray.
I tried different values for backgroundTintMode, which didn't change anything.
I also tried doing it programmatically in my Activity, which didn't change anything.
addCodeView.findViewById(R.id.btnAddCode).setBackgroundTintList(
getResources().getColorStateList(R.color.accent));
Why is my tint ignored?
EDIT:
Just to clarify, I am indeed testing on a Lollipop device.
Other widgets (e.g. EditText) are correctly and automatically tinted.
The bad news
Like BoD says, it's meaningless to tint a Button's background in Lollipop 5.0 (API level 21).
The good news
Lollipop 5.1 (API level 22) seems to have fixed this by changing btn_mtrl_default_shape.xml (among other files): https://android.googlesource.com/platform/frameworks/base/+/6dfa60f33ca6018959ebff1efde82db7d2aed1e3%5E!/#F0
The great news
The new support library (version 22.1+) adds backward-compatible tinting support to lots of components, including AppCompatButton!
Unfortunately, the android:backgroundTint property still doesn't work (maybe I'm doing something wrong) -- so you have to set the ColorStateList in code, using setSupportBackgroundTintList(). It'd be really nice to see android:backgroundTint supported in the future. Update: Marcio Granzotto commented that app:backgroundTint works on AppCompatButton! Note that it's app:, not android:, because it's in the app/library.
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<AppCompatButton
android:id="#+id/mybutton"
android:layout_width="wrap_content" android:layout_height="wrap_content"
android:text="Testing, testing"
app:backgroundTint="#ff00ff"/>
</LinearLayout>
Your activity will automatically inflate an AppCompatButton instead of the normal Button if you let it inherit from AppCompatActivity.
public class MainActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
AppCompatButton v = (AppCompatButton) findViewById(R.id.mybutton);
ColorStateList csl = new ColorStateList(new int[][]{new int[0]}, new int[]{0xffffcc00});
v.setSupportBackgroundTintList(csl);
}
}
You should of course get the ColorStateList from a color resource, but I was lazy, so...
Oh, and don't forget to base your app theme on one of the Theme.AppCompat themes, or the compat views will be very, very sad... ;)
This worked on both 2.3.7 (Gingerbread MR1) and 5.0 (Lollipop 'Classic').
It seems that tinting a ripple drawable is meaningless (and the default background of a button is a ripple drawable).
In fact, after looking at the platform's default button drawable, I found the "correct" way to do this:. You have to define this in your theme:
<item name="android:colorButtonNormal">#color/accent</item>
(Of course this is only for level 21+.)
Warning: since this is defined in a theme, this will use the given color for all the buttons (at least all of the buttons in activities using that theme.)
As a bonus, you can also change the ripple color by defining this:
<item name="android:colorControlHighlight">#color/accent_ripple</item>
To resolve issues related to tinting on Android 5.0.x I use something like this:
public static void setButtonTint(Button button, ColorStateList tint) {
if (Build.VERSION.SDK_INT == Build.VERSION_CODES.LOLLIPOP && button instanceof AppCompatButton) {
((AppCompatButton) button).setSupportBackgroundTintList(tint);
} else {
ViewCompat.setBackgroundTintList(button, tint);
}
}
It uses the support method only for API 21 and the ViewCompat one for all other cases.
I usually do it dynamically by using PorterDuff:
mbutton = (Button) findViewById(R.id.mybutton);
mbutton.getBackground().setColorFilter(anycolor, PorterDuff.Mode.MULTIPLY);
You can check different blending modes here and nice examples here.
Just use app:backgroundTint instead of android:backgroundTint, the tint will take effect below Lollipop. The reason is AppCompatActivity use AppCompatViewInflater to auto change Button or TextView to AppCompatButton or AppCompatTextView, then app:backgroundTint take effect.
In my project I used it, it worked.
Tested on API 19 through API 27
<?xml version="1.0" encoding="utf-8"?>
<android.support.v7.widget.AppCompatButton
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
style="#style/Widget.AppCompat.Button.Colored"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="#string/retry"
android:textColor="#android:color/white"
app:backgroundTint="#android:color/holo_red_dark" />
produces output as -
I think you need to have android:background set to make android:backgroundTint work.
To be more accurate, my guess is that you can't backgroundTint the default button background from Material themes, which is defined as a RippleDrawable.
Just use app:backgroundTint instead of android:backgroundTint
Similar issue was reported on google https://code.google.com/p/android/issues/detail?id=201873
But after release of Android Support Library, revision 23.2.1 (March 2016) This bug is solved.
Issue : FloatingActionButton.setBackgroundTintList(#Nullable ColorStateList tint) no longer changes background color
update Support Library to Android Support Library to 23.2.1
Use design support library(23.2.1) and appcompatwidgets as below
Material Design for Pre-Lollipop Devices :
AppCompat (aka ActionBarCompat) started out as a backport of the
Android 4.0 ActionBar API for devices running on Gingerbread,
providing a common API layer on top of the backported implementation
and the framework implementation. AppCompat v21 delivers an API and
feature-set that is up-to-date with Android 5.0
Android Support Library 22.1 :
The ability to tint widgets automatically when using AppCompat is
incredibly helpful in keeping strong branding and consistency
throughout your app. This is done automatically when inflating layouts
- replacing Button with AppCompatButton, TextView with AppCompatTextView, etc. to ensure that each could support tinting. In
this release, those tint aware widgets are now publicly available,
allowing you to keep tinting support even if you need to subclass one
of the supported widgets.
If we look into the source code of Support Library, we see that it tints normally it's known buttons, but if we change the shape of our button (I have round button) tint doesn't work ok in api<=21.
We can also see that TintManager became public class (appcompat-v7:23.1.1), so we can take ColorStateList from default button shape (which is tinted ok in 5.0) for current theme (so we don't have to create the array of colors):
Context c = ...; // activity
AppCompatButton ab = ...; // your button
// works ok in 22+:
if (Build.VERSION.SDK_INT <= 21) {
// default appcompat button, that is tinted ok with current theme colors "abc_btn_default_mtrl_shape":
// ColorStateList tint = TintManager.get(c).getTintList(R.drawable.abc_btn_default_mtrl_shape);
// Appcompat 23.2 change:
ColorStateList tint = AppCompatDrawableManager.get().getTintList(c, R.drawable.abc_btn_default_mtrl_shape);
ab.setSupportBackgroundTintList(tint);
}
Because attribute backgroundTint is only used in API level 21 and higher
Be aware recyclerview most updated lib can cause this bug as well.
This command
sendBtnView.setBackgroundTintList(colorState)
worked perfectly in the past but stop working for me. after research it turns out the cause is the lib that was addeded to gradle dependencies:
compile 'com.android.support:recyclerview-v7:+'
So I tried to change it to 23.02.1 as it was recommended here in Amit Vaghela post.
I changed to
compile 'com.android.support:recyclerview-v7:23.02.1'
But gradle error said recyclerview lib does not have this version (23.02.1) (gradle could not find it in Jcenter raw.github or repo).
Then, becaouse I knew setBackgroundTintList command used to worke well in the past with version 22.02.0' in all the other libs I have in gradle dependencies.
so I change it to:
compile 'com.android.support:recyclerview-v7:22.02.0'
And now it works again.
I am not sure if this is recommended but you can try this:
Drawable newDrawable = mBtnAction.getBackground(); // obtain Bg drawable from the button as a new drawable
DrawableCompat.setTint(newDrawable, mActivity.getHomeTobBarBGColor()); //set it's tint
mBtnAction.setBackground(newDrawable); //apply back to button
In a general sense it works. Tried ViewCompat but it doesn't seem to work properly.
you can use backgroundTint <android.support.design.button.MaterialButton with "com.android.support:design:28.0.0-rc01" version
If you are using androidx then adding both prefixed and not prefixed version resolved issue on android 5.1:
<style name="Button_Primary">
<item name="android:layout_height">wrap_content</item>
<item name="android:layout_width">wrap_content</item>
<item name="android:backgroundTint">#color/button_primary_selector</item>
<item name="backgroundTint">#color/button_primary_selector</item><!--needed for android 5-->
</style>
button_primary selector is in color folder with following content:
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:local="http://schemas.android.com/apk/res-auto">
<item android:state_enabled="true" android:color="#android:color/holo_blue_dark" />
<item android:state_enabled="false" android:color="#android:color/darker_gray" />
</selector>
and apply it on regular button on AppCompatActivity
<Button style="#style/Button_Primary"/>
If you are developing an app that your target sdk is higher than api 21 and your minSdkVersion is 21(Lollipop) instead of
android:backgroundTint="#color/accent"
you can simply say..
android:background="#color/accent"

Categories

Resources