I have a theme class "Util" and I have 3 buttons to change the theme in my Activity.
When I apply the theme every TextView is working well with themes, EditText is not working which made me to change it to TextView and the Buttons also don't working.
The thing that i discovered is That the field of text color have to be empty like in TextView.
I Changed my Button to TextView and its worked! But is there any way to make these button work as well??
This is my Util class:
public static void changeToTheme(Activity activity, int theme){
sTheme = theme;
activity.finish();
activity.startActivity(new Intent(activity, activity.getClass()));
}
/** Set the theme of the activity, according to the configuration. */
public static void onActivityCreateSetTheme(Activity activity){
switch (sTheme)
{
default:
case THEME_DEFAULT:
activity.setTheme(R.style.FirstTheme);
break;
case THEME_WHITE:
activity.setTheme(R.style.SecondTheme);
break;
case THEME_BLUE:
activity.setTheme(R.style.ThirdTheme);
break;
}
}
//Shared Preferences
public static int getTheme()
{
return sTheme;
}
public static boolean canSetThemeFromPrefs( final Activity activity )
{
boolean result = false;
SharedPreferences prefMngr = PreferenceManager.getDefaultSharedPreferences( activity );
if ( prefMngr.contains( "Theme_Preferences" ) )
{
result = true;
}
return result;
}
public static int getThemeFromPrefs( final Activity activity )
{
SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences( activity );
final String themeFromPrefs = preferences.getString( "Theme_Preferences", "THEME_DEFAULT" );
if ( themeFromPrefs.equals( "THEME_BLUE" ) )
{
sTheme = THEME_BLUE;
}
else if ( themeFromPrefs.equals( "THEME_WHITE" ) )
{
sTheme = THEME_WHITE;
}
else
{
sTheme = THEME_DEFAULT;
}
return getTheme();
}
public static int getThemeFromPrefs( final String key )
{
if ( key.equals( "THEME_BLUE" ) )
{
sTheme = THEME_BLUE;
}
else if ( key.equals( "THEME_WHITE" ) )
{
sTheme = THEME_WHITE;
}
else
{
sTheme = THEME_DEFAULT;
}
return getTheme();
}
Here is my Button:
<Button
android:id="#+id/button3"
android:layout_width="#dimen/layout_width_numbers"
android:layout_height="#dimen/layout_width_numbers"
android:background="#drawable/numbers"
android:text="#string/button3"
android:textColor="#color/button_text_color"
android:textSize="#dimen/button_textsize"
android:textStyle="bold" />
Update:
And here is my Styles.xml
<!-- Change Layout theme. -->
<!-- Red. -->
<style name="FirstTheme" >
<item name="android:textColor">#color/first_theme_button</item>
</style>
<!-- Green. Violet -->
<style name="SecondTheme" >
<item name="android:textColor">#color/second_theme_button</item>
</style>
<!-- Blue. -->
<style name="ThirdTheme" >
<item name="android:textColor">#color/third_theme_button</item>
</style>
And also Shared Preference doesn't work and it doesn't save my last Theme!!
Thanks :)
I tested this out, and I think I know how to make it work:
<style name="SecondTheme" parent="android:Theme.Light">
<item name="android:editTextStyle">#style/SecondTheme</item>
<item name="android:buttonStyle">#style/SecondTheme</item>
<item name="android:textColor">#color/second_theme_button</item>
</style>
When I did this, I got the edit text and the button in the second_theme_button color.
Related
I need to change the colorPrimary and colorAccent programmatically. The color values are fetched from a web service once we login to the application. These values will be any, because it is setting from a web application. So the same theme should be displayed in the mobile application too.
This is my theme
<style name="AppTheme" parent="Theme.MaterialComponents.Light.DarkActionBar">
<item name="colorPrimary">#color/colorPrimary</item>
<item name="colorPrimaryDark">#color/colorPrimaryDark</item>
<item name="colorAccent">#color/colorAccent</item>
</style>
I searched everywhere and the answers are like, I need to create a number of different themes and switch between. But in my scenario, it is perfectly dynamic.
first create "res/values-v9/" and put there this file: styles.xml and regular "res/values" folder will be used with your styles.
put this code in your res/values/styles.xml:
<resources>
<style name="AppTheme" parent="Theme.AppCompat.Light">
<item name="colorPrimary">#000</item>
<item name="colorPrimaryDark">#000</item>
<item name="colorAccent">#000</item>
<item name="android:windowAnimationStyle">#style/WindowAnimationTransition</item>
</style>
<style name="AppThemeDarkActionBar" parent="Theme.AppCompat.Light.DarkActionBar">
<item name="colorPrimary">#000</item>
<item name="colorPrimaryDark">#000</item>
<item name="colorAccent">#000</item>
<item name="android:windowAnimationStyle">#style/WindowAnimationTransition</item>
</style>
<style name="WindowAnimationTransition">
<item name="android:windowEnterAnimation">#android:anim/fade_in</item>
<item name="android:windowExitAnimation">#android:anim/fade_out</item>
</style>
</resources>
in to AndroidManifest:
<application android:theme="#style/AppThemeDarkActionBar">
create a new class with name "ThemeColors.java"
public class ThemeColors {
private static final String NAME = "ThemeColors", KEY = "color";
#ColorInt
public int color;
public ThemeColors(Context context) {
SharedPreferences sharedPreferences = context.getSharedPreferences(NAME, Context.MODE_PRIVATE);
String stringColor = sharedPreferences.getString(KEY, "004bff");
color = Color.parseColor("#" + stringColor);
if (isLightActionBar()) context.setTheme(R.style.AppTheme);
context.setTheme(context.getResources().getIdentifier("T_" + stringColor, "style", context.getPackageName()));
}
public static void setNewThemeColor(Activity activity, int red, int green, int blue) {
int colorStep = 15;
red = Math.round(red / colorStep) * colorStep;
green = Math.round(green / colorStep) * colorStep;
blue = Math.round(blue / colorStep) * colorStep;
String stringColor = Integer.toHexString(Color.rgb(red, green, blue)).substring(2);
SharedPreferences.Editor editor = activity.getSharedPreferences(NAME, Context.MODE_PRIVATE).edit();
editor.putString(KEY, stringColor);
editor.apply();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) activity.recreate();
else {
Intent i = activity.getPackageManager().getLaunchIntentForPackage(activity.getPackageName());
i.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
activity.startActivity(i);
}
}
private boolean isLightActionBar() {// Checking if title text color will be black
int rgb = (Color.red(color) + Color.green(color) + Color.blue(color)) / 3;
return rgb > 210;
}
}
MainActivity:
public class MainActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
new ThemeColors(this);
setContentView(R.layout.activity_main);
}
public void buttonClick(View view){
int red= new Random().nextInt(255);
int green= new Random().nextInt(255);
int blue= new Random().nextInt(255);
ThemeColors.setNewThemeColor(MainActivity.this, red, green, blue);
}
}
To change color, just replace Random with your RGB, Hope this helps.
enter image description [here]
There is a complete example: ColorTest.zip
You can have a look at this GitHub project from Rumit Patel.
I am trying change theme when the app device is offline. But to achieve changing the background color is not gonna help and i needed to change the whole view and the text colors. But for that getting all the view with FindViewById is not an effective method to achieve that as I got lots of views to the Activity and as i tried using Themes
<item name="android:textColor">#android:color/black</item>
<item name="android:windowBackground">#android:color/white</item>
But as it shows only one colored TextView I cant use this method to activity as it has multiple colors and changing theme has to be done before you create the activity.
Please provide a solution which supports changing theme with multiple colored View
public class Utils {
private static int sTheme;
public final static int THEME_MATERIAL_LIGHT = 0;
public final static int THEME_YOUR_CUSTOM_THEME = 1;
public static void changeToTheme(Activity activity, int theme) {
sTheme = theme;
activity.finish();
activity.startActivity(new Intent(activity, activity.getClass()));
activity.overridePendingTransition(android.R.anim.fade_in,
android.R.anim.fade_out);
}
public static void onActivityCreateSetTheme(Activity activity) {
switch (sTheme) {
default:
case THEME_MATERIAL_LIGHT:
activity.setTheme(R.style.Theme_Material_Light);
break;
case THEME_YOUR_CUSTOM_THEME:
activity.setTheme(R.style.Theme_YOUR_CUSTOM_THEME);
break;
}
}
}
In Your Activity:
#Override // Any Activity
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// MUST BE SET BEFORE setContentView
Utils.onActivityCreateSetTheme(this);
// AFTER SETTING THEME
setContentView(R.layout.activity_theme);
}
I am using themes (dynamically) in my android app, like this:
my_layout.xml (extract):
<TextView
android:id="#+id/myItem"
style="?my_item_style" />
attrs.xml (extract):
<attr name="my_item_style" format="reference" />
themes.xml (extract):
<style name="MainTheme.Blue">
<item name="my_item_style">#style/my_item_style_blue</item>
</style>
<style name="MainTheme.Green">
<item name="my_item_style">#style/my_item_style_green<item>
</style>
styles.xml (extract):
<style name="my_item_style_blue">
<item name="android:textColor">#color/my_blue</item>
</style>
<style name="my_item_style_green">
<item name="android:textColor">#color/my_blue</item>
</style>
So, as you can see, I am setting themes dynamically. I am using this class:
public class ThemeUtils {
private static int sTheme;
public final static int THEME_BLUE = 1;
public final static int THEME_GREEN = 2;
public static void changeToTheme(MainActivity activity, int theme) {
sTheme = theme;
activity.startActivity(new Intent(activity, MyActivity.class));
}
public static void onActivityCreateSetTheme(Activity activity)
{
switch (sTheme)
{
default:
case THEME_DEFAULT:
case THEME_BLUE:
activity.setTheme(R.style.MainTheme_Blue);
break;
case THEME_GREEN:
activity.setTheme(R.style.MainTheme_Green);
break;
}
}
}
What I want to know, is there a way how to do this (change theme color) in code? For example, I have following code (extract):
((TextView) findViewById(R.id.myItem)).setTextColor(R.color.blue);
It can be done by some helper method, which would use switch command for available themes and return correct color for a theme. But I would like to know if there is some better, nicer and faster way.
Thanks!
I have finally done it using following method:
public static int getColor(String colorName) {
Context ctx = getContext();
switch (sTheme) {
default:
case THEME_DEFAULT:
return ctx.getResources().getIdentifier("BLUE_" + colorName, "color", ctx.getPackageName());
case THEME_BLUE:
return ctx.getResources().getIdentifier("BLUE_" + colorName, "color", ctx.getPackageName());
case THEME_GREEN:
return ctx.getResources().getIdentifier("GREEN_" + colorName, "color", ctx.getPackageName());
}
}
This returns color according to my theme (I used prefixes).
If I understand corectly you're looking for a way to
extract a style from a theme,
extract a value (text color) from said style.
Let's get to it.
// Extract ?my_item_style from a context/activity.
final TypedArray a = context.obtainStyledAttributes(new int[] { R.attr.my_item_style });
#StyleRes final int styleResId = a.getResourceId(0, 0);
a.recycle();
// Extract values from ?my_item_style.
final TypedArray b = context.obtainStyledAttributes(styleResId, new int[] { android.R.attr.textColor });
final ColorStateList textColors = b.getColorStateList(0);
b.recycle();
// Apply extracted values.
if (textColors != null) {
textView.setTextColor(textColors);
}
A couple of notes:
TypedArray does not support getting support vector drawables and theme references in color state lists on older API levels. If you're willing to use AppCompat internal API you may want to try TintTypedArray.
Allocating int[] all the time is costly, make it a static final.
If you want to resolve multiple attributes at once the array of attributes has to be sorted! Else it crashes sometimes. <declare-styleable> generates such array and corresponding indices for you.
Have you check this MultipleThemeMaterialDesign demo?
SettingActivity:
#Override
protected void onCreate(Bundle savedInstanceState) {
Preferences.applyTheme(this);
getDelegate().installViewFactory();
getDelegate().onCreate(savedInstanceState);
super.onCreate(savedInstanceState);
setToolbar();
addPreferencesFromResource(R.xml.preferences);
Preferences.sync(getPreferenceManager());
mListener = new SharedPreferences.OnSharedPreferenceChangeListener() {
#Override
public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
Preferences.sync(getPreferenceManager(), key);
if (key.equals(getString(R.string.pref_theme))) {
finish();
final Intent intent = IntentCompat.makeMainActivity(new ComponentName(
SettingsActivity.this, MainActivity.class));
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | IntentCompat.FLAG_ACTIVITY_CLEAR_TASK);
startActivity(intent);
}
}
};
}
See full example for demo.
What about passing theme id via Intent?
Intent intent = new Intent(activity, MyActivity.class);
intent.putExtra("theme", R.style.MainTheme_Green);
activity.startActivity(intent);
And then in onCreate:
// assuming that MainTheme_Blue is default theme
setTheme(getIntent().getIntExtra("theme", R.style.MainTheme_Blue));
Given the fact that every resource is a field into the R class, you can look for them using reflection. That's costly, but since you are going to get an int value, you can store them after you get them and avoid the performance drop. And since the methods that use resources take any int, you can use an int variable as placeholder, and then put the desired color into it.
for getting any resource:
String awesomeColor = "blue";
int color = getResourceId(R.color, awesomeColor, false);
if(blue>0) ((TextView) findViewById(R.id.myItem)).setTextColor(color);
The function:
public static int getResourceId(Class rClass, String resourceText, boolean showExceptions){
String key = rClass.getName()+"-"+resourceText;
if(FailedResourceMap.containsKey(key)) return 0;
if(ResourceMap.containsKey(key)) return ResourceMap.get(rClass.getName()+"-"+resourceText);
try {
String originalText = resourceText;
if(Build.VERSION.SDK_INT>=Build.VERSION_CODES.GINGERBREAD){
resourceText = ValidationFunctions.normalizeText(resourceText);
}
resourceText = resourceText.replace("?", "").replace(" ", " ").replace(" ", "_").replace("(", "").replace(")", "");
int resource = rClass.getDeclaredField(resourceText).getInt(null);
ResourceMap.put(rClass.getName()+"-"+originalText, resource);
return resource;
} catch (IllegalAccessException | NullPointerException e) {
FailedResourceMap.put(key, 0);
if(showExceptions) e.printStackTrace();
} catch (NoSuchFieldException e) {
FailedResourceMap.put(key, 0);
if(showExceptions) e.printStackTrace();
}
return 0;
}
Working version here: https://github.com/fcopardo/AndroidFunctions/blob/master/src/main/java/com/grizzly/functions/TextFunctions.java
This treatment is valid for any android resource. You can set the theme this way too instead of using intermediate variables:
public static void onActivityCreateSetTheme(Activity activity)
{
int theme = getResourceId(R.style, activity.getClass().getSimpleName(), false);
if(theme > 0) activity.setTheme(theme);
}
First of all, this question asks a very similar question. However, my question has a subtle difference.
What I'd like to know is whether it is possible to programmatically change the colorPrimary attribute of a theme to an arbitrary color?
So for example, we have:
<style name="AppTheme" parent="android:Theme.Material.Light">
<item name="android:colorPrimary">#ff0000</item>
<item name="android:colorAccent">#ff0000</item>
</style>
At runtime, the user decides he wants to use #ccffff as a primary color. Ofcourse there's no way I can create themes for all possible colors.
I don't mind if I have to do hacky stuff, like relying on Android's private internals, as long as it works using the public SDK.
My goal is to eventually have the ActionBar and all widgets like a CheckBox to use this primary color.
Themes are immutable, you can't.
I read the comments about contacts app and how it use a theme for each contact.
Probably, contacts app has some predefine themes (for each material primary color from here: http://www.google.com/design/spec/style/color.html).
You can apply a theme before a the setContentView method inside onCreate method.
Then the contacts app can apply a theme randomly to each user.
This method is:
setTheme(R.style.MyRandomTheme);
But this method has a problem, for example it can change the toolbar color, the scroll effect color, the ripple color, etc, but it cant change the status bar color and the navigation bar color (if you want to change it too).
Then for solve this problem, you can use the method before and:
if (Build.VERSION.SDK_INT >= 21) {
getWindow().setNavigationBarColor(getResources().getColor(R.color.md_red_500));
getWindow().setStatusBarColor(getResources().getColor(R.color.md_red_700));
}
This two method change the navigation and status bar color.
Remember, if you set your navigation bar translucent, you can't change its color.
This should be the final code:
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setTheme(R.style.MyRandomTheme);
if (Build.VERSION.SDK_INT >= 21) {
getWindow().setNavigationBarColor(getResources().getColor(R.color.myrandomcolor1));
getWindow().setStatusBarColor(getResources().getColor(R.color.myrandomcolor2));
}
setContentView(R.layout.activity_main);
}
You can use a switch and generate random number to use random themes, or, like in contacts app, each contact probably has a predefine number associated.
A sample of theme:
<style name="MyRandomTheme" parent="Theme.AppCompat.NoActionBar">
<!-- Customize your theme here. -->
<item name="colorPrimary">#color/myrandomcolor1</item>
<item name="colorPrimaryDark">#color/myrandomcolor2</item>
<item name="android:navigationBarColor">#color/myrandomcolor1</item>
</style>
You can use Theme.applyStyle to modify your theme at runtime by applying another style to it.
Let's say you have these style definitions:
<style name="DefaultTheme" parent="Theme.AppCompat.Light">
<item name="colorPrimary">#color/md_lime_500</item>
<item name="colorPrimaryDark">#color/md_lime_700</item>
<item name="colorAccent">#color/md_amber_A400</item>
</style>
<style name="OverlayPrimaryColorRed">
<item name="colorPrimary">#color/md_red_500</item>
<item name="colorPrimaryDark">#color/md_red_700</item>
</style>
<style name="OverlayPrimaryColorGreen">
<item name="colorPrimary">#color/md_green_500</item>
<item name="colorPrimaryDark">#color/md_green_700</item>
</style>
<style name="OverlayPrimaryColorBlue">
<item name="colorPrimary">#color/md_blue_500</item>
<item name="colorPrimaryDark">#color/md_blue_700</item>
</style>
Now you can patch your theme at runtime like so:
getTheme().applyStyle(R.style.OverlayPrimaryColorGreen, true);
The method applyStylehas to be called before the layout gets inflated! So unless you load the view manually you should apply styles to the theme before calling setContentView in your activity.
Of course this cannot be used to specify an arbitrary color, i.e. one out of 16 million (2563) colors. But if you write a small program that generates the style definitions and the Java code for you then something like one out of 512 (83) should be possible.
What makes this interesting is that you can use different style overlays for different aspects of your theme. Just add a few overlay definitions for colorAccent for example. Now you can combine different values for primary color and accent color almost arbitrarily.
You should make sure that your overlay theme definitions don't accidentally inherit a bunch of style definitions from a parent style definition. For example a style called AppTheme.OverlayRed implicitly inherits all styles defined in AppTheme and all these definitions will also be applied when you patch the master theme. So either avoid dots in the overlay theme names or use something like Overlay.Red and define Overlay as an empty style.
I've created some solution to make any-color themes, maybe this can be useful for somebody. API 9+
1. first create "res/values-v9/" and put there this file: styles.xml
and regular "res/values" folder will be used with your styles.
2. put this code in your res/values/styles.xml:
<resources>
<style name="AppTheme" parent="Theme.AppCompat.Light">
<item name="colorPrimary">#000</item>
<item name="colorPrimaryDark">#000</item>
<item name="colorAccent">#000</item>
<item name="android:windowAnimationStyle">#style/WindowAnimationTransition</item>
</style>
<style name="AppThemeDarkActionBar" parent="Theme.AppCompat.Light.DarkActionBar">
<item name="colorPrimary">#000</item>
<item name="colorPrimaryDark">#000</item>
<item name="colorAccent">#000</item>
<item name="android:windowAnimationStyle">#style/WindowAnimationTransition</item>
</style>
<style name="WindowAnimationTransition">
<item name="android:windowEnterAnimation">#android:anim/fade_in</item>
<item name="android:windowExitAnimation">#android:anim/fade_out</item>
</style>
</resources>
3. in to AndroidManifest:
<application android:theme="#style/AppThemeDarkActionBar">
4. create a new class with name "ThemeColors.java"
public class ThemeColors {
private static final String NAME = "ThemeColors", KEY = "color";
#ColorInt
public int color;
public ThemeColors(Context context) {
SharedPreferences sharedPreferences = context.getSharedPreferences(NAME, Context.MODE_PRIVATE);
String stringColor = sharedPreferences.getString(KEY, "004bff");
color = Color.parseColor("#" + stringColor);
if (isLightActionBar()) context.setTheme(R.style.AppTheme);
context.setTheme(context.getResources().getIdentifier("T_" + stringColor, "style", context.getPackageName()));
}
public static void setNewThemeColor(Activity activity, int red, int green, int blue) {
int colorStep = 15;
red = Math.round(red / colorStep) * colorStep;
green = Math.round(green / colorStep) * colorStep;
blue = Math.round(blue / colorStep) * colorStep;
String stringColor = Integer.toHexString(Color.rgb(red, green, blue)).substring(2);
SharedPreferences.Editor editor = activity.getSharedPreferences(NAME, Context.MODE_PRIVATE).edit();
editor.putString(KEY, stringColor);
editor.apply();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) activity.recreate();
else {
Intent i = activity.getPackageManager().getLaunchIntentForPackage(activity.getPackageName());
i.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
activity.startActivity(i);
}
}
private boolean isLightActionBar() {// Checking if title text color will be black
int rgb = (Color.red(color) + Color.green(color) + Color.blue(color)) / 3;
return rgb > 210;
}
}
5. MainActivity:
public class MainActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
new ThemeColors(this);
setContentView(R.layout.activity_main);
}
public void buttonClick(View view){
int red= new Random().nextInt(255);
int green= new Random().nextInt(255);
int blue= new Random().nextInt(255);
ThemeColors.setNewThemeColor(MainActivity.this, red, green, blue);
}
}
To change color, just replace Random with your RGB, Hope this helps.
There is a complete example: ColorTest.zip
You can have a look at this GitHub project from Rumit Patel.
I used the Dahnark's code but I also need to change the ToolBar background:
if (dark_ui) {
this.setTheme(R.style.Theme_Dark);
if (Build.VERSION.SDK_INT >= 21) {
getWindow().setNavigationBarColor(getResources().getColor(R.color.Theme_Dark_primary));
getWindow().setStatusBarColor(getResources().getColor(R.color.Theme_Dark_primary_dark));
}
} else {
this.setTheme(R.style.Theme_Light);
}
setContentView(R.layout.activity_main);
toolbar = (Toolbar) findViewById(R.id.app_bar);
if(dark_ui) {
toolbar.setBackgroundColor(getResources().getColor(R.color.Theme_Dark_primary));
}
You can change define your own themes, or customize existing android themes in res > values > themes, find where it says primary color and point it to the color defined in color.xml you want
<style name="Theme.HelloWorld" parent="Theme.MaterialComponents.DayNight.DarkActionBar">
<!-- Primary brand color. -->
<item name="colorPrimary">#color/my_color</item>
<item name="colorPrimaryVariant">#color/my_color</item>
<item name="colorOnPrimary">#color/white</item>
from an activity you can do:
getWindow().setStatusBarColor(i color);
You cannot change the color of colorPrimary, but you can change the theme of your application by adding a new style with a different colorPrimary color
<style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">
<!-- Customize your theme here. -->
<item name="colorPrimary">#color/colorPrimary</item>
<item name="colorPrimaryDark">#color/colorPrimaryDark</item>
</style>
<style name="AppTheme.NewTheme" parent="Theme.AppCompat.Light.NoActionBar">
<item name="colorPrimary">#color/colorOne</item>
<item name="colorPrimaryDark">#color/colorOneDark</item>
</style>
and inside the activity set theme
setTheme(R.style.AppTheme_NewTheme);
setContentView(R.layout.activity_main);
USE A TOOLBAR
You can set a custom toolbar item color dynamically by creating a custom toolbar class:
package view;
import android.app.Activity;
import android.content.Context;
import android.graphics.ColorFilter;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffColorFilter;
import android.support.v7.internal.view.menu.ActionMenuItemView;
import android.support.v7.widget.ActionMenuView;
import android.support.v7.widget.Toolbar;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AutoCompleteTextView;
import android.widget.EditText;
import android.widget.ImageButton;
import android.widget.ImageView;
import android.widget.TextView;
public class CustomToolbar extends Toolbar{
public CustomToolbar(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
// TODO Auto-generated constructor stub
}
public CustomToolbar(Context context, AttributeSet attrs) {
super(context, attrs);
// TODO Auto-generated constructor stub
}
public CustomToolbar(Context context) {
super(context);
// TODO Auto-generated constructor stub
ctxt = context;
}
int itemColor;
Context ctxt;
#Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
Log.d("LL", "onLayout");
super.onLayout(changed, l, t, r, b);
colorizeToolbar(this, itemColor, (Activity) ctxt);
}
public void setItemColor(int color){
itemColor = color;
colorizeToolbar(this, itemColor, (Activity) ctxt);
}
/**
* Use this method to colorize toolbar icons to the desired target color
* #param toolbarView toolbar view being colored
* #param toolbarIconsColor the target color of toolbar icons
* #param activity reference to activity needed to register observers
*/
public static void colorizeToolbar(Toolbar toolbarView, int toolbarIconsColor, Activity activity) {
final PorterDuffColorFilter colorFilter
= new PorterDuffColorFilter(toolbarIconsColor, PorterDuff.Mode.SRC_IN);
for(int i = 0; i < toolbarView.getChildCount(); i++) {
final View v = toolbarView.getChildAt(i);
doColorizing(v, colorFilter, toolbarIconsColor);
}
//Step 3: Changing the color of title and subtitle.
toolbarView.setTitleTextColor(toolbarIconsColor);
toolbarView.setSubtitleTextColor(toolbarIconsColor);
}
public static void doColorizing(View v, final ColorFilter colorFilter, int toolbarIconsColor){
if(v instanceof ImageButton) {
((ImageButton)v).getDrawable().setAlpha(255);
((ImageButton)v).getDrawable().setColorFilter(colorFilter);
}
if(v instanceof ImageView) {
((ImageView)v).getDrawable().setAlpha(255);
((ImageView)v).getDrawable().setColorFilter(colorFilter);
}
if(v instanceof AutoCompleteTextView) {
((AutoCompleteTextView)v).setTextColor(toolbarIconsColor);
}
if(v instanceof TextView) {
((TextView)v).setTextColor(toolbarIconsColor);
}
if(v instanceof EditText) {
((EditText)v).setTextColor(toolbarIconsColor);
}
if (v instanceof ViewGroup){
for (int lli =0; lli< ((ViewGroup)v).getChildCount(); lli ++){
doColorizing(((ViewGroup)v).getChildAt(lli), colorFilter, toolbarIconsColor);
}
}
if(v instanceof ActionMenuView) {
for(int j = 0; j < ((ActionMenuView)v).getChildCount(); j++) {
//Step 2: Changing the color of any ActionMenuViews - icons that
//are not back button, nor text, nor overflow menu icon.
final View innerView = ((ActionMenuView)v).getChildAt(j);
if(innerView instanceof ActionMenuItemView) {
int drawablesCount = ((ActionMenuItemView)innerView).getCompoundDrawables().length;
for(int k = 0; k < drawablesCount; k++) {
if(((ActionMenuItemView)innerView).getCompoundDrawables()[k] != null) {
final int finalK = k;
//Important to set the color filter in seperate thread,
//by adding it to the message queue
//Won't work otherwise.
//Works fine for my case but needs more testing
((ActionMenuItemView) innerView).getCompoundDrawables()[finalK].setColorFilter(colorFilter);
// innerView.post(new Runnable() {
// #Override
// public void run() {
// ((ActionMenuItemView) innerView).getCompoundDrawables()[finalK].setColorFilter(colorFilter);
// }
// });
}
}
}
}
}
}
}
then refer to it in your layout file. Now you can set a custom color using
toolbar.setItemColor(Color.Red);
Sources:
I found the information to do this here: How to dynamicaly change Android Toolbar icons color
and then I edited it, improved upon it, and posted it here: GitHub:AndroidDynamicToolbarItemColor
This is what you CAN do:
write a file in drawable folder, lets name it background.xml
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android" >
<solid android:color="?attr/colorPrimary"/>
</shape>
then set your Layout's (or what so ever the case is) android:background="#drawable/background"
on setting your theme this color would represent the same.
I have an activity which is shown in a dialog:
In order to remove border and rounded corners, i tried this:
<resources>
<style name="ActivityDialog" parent="#android:style/Theme.Dialog">
<item name="android:windowBackground">#null</item>
<item name="android:windowFrame">#null</item>
</style>
The border is gone, but sadly also the margin around the dialog.
Without creating a custom background drawable and adding a special style just add one line to your code:
dialog.getWindow().setBackgroundDrawableResource(android.R.color.transparent);
The border, round corners and margin are all defined by android:windowBackground. (Parameter android:windowFrame is already set to #null in Theme.Dialog style, therefore setting it to #null again has no effect.)
In order to remove the border and round corners you have to change the android:windowBackground appropriately. The Theme.Dialog style sets it to #android:drawable/panel_background. Which is a 9-patch drawable that looks like this (this one is the hdpi version):
As you can see the 9-patch png defines the margin, border and round corners of the dialog theme. To remove the border and round corners you have to create an appropriate drawable. If you want to keep the shadow gradient you have to create set of new 9-patch drawables (one drawable for each dpi). If you don't need the shadow gradient you can create a shape drawable.
The required style is then:
<style name="ActivityDialog" parent="#android:style/Theme.Dialog">
<item name="android:windowBackground">#drawable/my_custom_dialog_background</item>
</style>
I played around a bit with other possibilities but using a 9 patch with fixed margins and found out that the layer-list drawable is allowing to define offsets, hence margins around its enclosed drawables, so this worked for me:
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android" >
<item
android:drawable="#drawable/my_custom_background"
android:top="5dp" android:bottom="5dp" android:left="5dp" android:right="5dp">
</item>
</layer-list>
and then you can use this as the "android:windowBackground":
<style name="ActivityDialog" parent="#android:style/Theme.Dialog">
<item name="android:windowBackground">#drawable/my_custom_layer_background</item>
</style>
Another option
Resources\Values\styles.xml
<style name="MessageDialog" parent="android:Theme.Holo.Light.Dialog">
<item name="android:windowBackground">#android:color/transparent</item>
</style>
where
AlertDialog.Builder builder = new AlertDialog.Builder(Activity, Resource.Style.MessageDialog);
These statements are excerpted from the following snippet:
public class MessageAlertDialog : DialogFragment, IDialogInterfaceOnClickListener
{
private const string DIALOG_TITLE = "dialogTitle";
private const string MESSAGE_TEXT = "messageText";
private const string MESSAGE_RESOURCE_ID = "messageResourceId";
private string _dialogTitle;
private string _messageText;
private int _messageResourceId;
public EventHandler OkClickEventHandler { get; set; }
public static MessageAlertDialog NewInstance(string messageText)
{
MessageAlertDialog dialogFragment = new MessageAlertDialog();
Bundle args = new Bundle();
args.PutString(MESSAGE_TEXT, messageText);
dialogFragment.Arguments = args;
return dialogFragment;
}
public static MessageAlertDialog NewInstance(string dialogTitle, string messageText)
{
MessageAlertDialog dialogFragment = new MessageAlertDialog();
Bundle args = new Bundle();
args.PutString(DIALOG_TITLE, dialogTitle);
args.PutString(MESSAGE_TEXT, messageText);
dialogFragment.Arguments = args;
return dialogFragment;
}
public static MessageAlertDialog NewInstance(int messageResourceId)
{
MessageAlertDialog dialogFragment = new MessageAlertDialog();
Bundle args = new Bundle();
args.PutInt(MESSAGE_RESOURCE_ID, messageResourceId);
dialogFragment.Arguments = args;
return dialogFragment;
}
public override void OnCreate(Bundle savedInstanceState)
{
base.OnCreate(savedInstanceState);
_dialogTitle = Arguments.GetString(DIALOG_TITLE);
_messageText = Arguments.GetString(MESSAGE_TEXT);
_messageResourceId = Arguments.GetInt(MESSAGE_RESOURCE_ID);
}
public override Dialog OnCreateDialog(Bundle savedInstanceState)
{
base.OnCreateDialog(savedInstanceState);
AlertDialog.Builder builder = new AlertDialog.Builder(Activity, Resource.Style.MessageDialog);
if (_dialogTitle != null)
{
builder.SetTitle(_dialogTitle);
}
if (_messageText != null)
{
builder.SetMessage(_messageText);
}
else
{
View messageView = GetMessageView();
if (messageView != null)
{
builder.SetView(messageView);
}
}
builder.SetPositiveButton("OK", this);
//.SetCancelable(false);
this.Cancelable = false;
AlertDialog dialog = builder.Create();
dialog.SetCanceledOnTouchOutside(false);
//dialog.Window.SetBackgroundDrawableResource(Android.Resource.Color.Transparent);
return dialog;
}
private View GetMessageView()
{
if (_messageResourceId != 0)
{
View messageView = Activity.LayoutInflater.Inflate(_messageResourceId, null);
return messageView;
}
return null;
}
void IDialogInterfaceOnClickListener.OnClick(IDialogInterface di, int i)
{
OkClickEventHandler?.Invoke(this, null);
}
}
Usage
public static void ShowMessageAlertDialog(FragmentManager fragmentManager, string dialogTitle, string messageText, EventHandler okClickEventHandler)
{
MessageAlertDialog msgAlertDialog = MessageAlertDialog.NewInstance(dialogTitle, messageText);
msgAlertDialog.OkClickEventHandler += okClickEventHandler;
msgAlertDialog.Show(fragmentManager, "message_alert_dialog");
}
Another solution to this issue is to use android.support.v7.app.AlerDialog instead of android.app.AlertDialog. It's the most easiest and time effective solution. Design you custom view in the layout and then use it with your support package's AlertDialogBuilderclass and it will work like charm.