I'm Working on an android app that has a requirement to switch theme based on the themeCode given from server. I'm using sharePref to save the theme code and applying it with setTheme(R.style.themeName);. Its working fine till the basic theme attributes like
colorPrimary
colorPrimaryDark
colorAccent
windowActionBar
windowNoTitle
For this I has created different styles in styles.xml. But I have a limitation that some fields say EditText has variation as EditText
person name
email
phone
password etc.
And similarly TextView has variation as TextView
Heading
Single Line
Mutiline
Link etc.
Before multi-theme requirement I had created separate themes for all as
Apptheme.Edittext.email
Apptheme.Edittext.Password
Apptheme.Edittext.PersonName etc.
And was applying to specific view in xml like
style="#style/AppTheme.EditText.PersonName"
Now I have viewed many tutorials/posts but did not find solution to the variations in attribute. Please help to apply these variation, I'll be thankful for this.
Regards:
Inzimam Tariq
In my opinion changing app theme at runtime, will definitely need to reload activity; this in most cases will create issues at some point (if project is extended to a mid scale, having a user control like toggle or a switch and if user taps switch repeatedly app may easily crash)
I would suggest to use custom control classes (Textviews, Buttons..etc); wherein this properties are differentiated with current theme value from sharedPref.
This approach has a con; it will require to change all views manually of current screen and those in already rendered in memory(if any), rest all it will be much smoother transition in compare to our conventional approach
EDIT: Example for CustomTextView ##
This is an example for customtextview class
public class CustomTextView extends android.support.v7.widget.AppCompatTextView {
private static final String TAG = "TextView";
private Typeface tf = null;
private SharedPreferenceUtils preferenceUtils = SharedPreferenceUtils.getInstance();
/**
* #param context:This is an abstract class whose implementation is provided by Android Operating System.
* #param attrs:A collection of attributes, as found associated with a tag in an XML document.
* #param defStyle:
*/
public CustomTextView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
if (preferenceUtils.getBooleanValue(PrefsKeyValue.bTheme)) {
this.setTextColor(ResourceUtils.getColor(R.color.lightThemeTextColor));
} else {
this.setTextColor(ResourceUtils.getColor(R.color.colorWhite));
}
try {
TypedArray a = context.obtainStyledAttributes(attrs,
R.styleable.CustomEditText, defStyle, 0);
String str = a.getString(R.styleable.CustomTextView_FontEnum);
int original = a.getInt(R.styleable.CustomEditText_FontEnum, 0);
CustomEnum.CustomFontType customEnumValue = CustomEnum.CustomFontType.fromId(a.getInt(R.styleable.CustomEditText_FontEnum, 0));
a.recycle();
switch (customEnumValue) {
case BOLD:
setTypeface(HelveticaNeueBold.getInstance(context).getTypeFace());
break;
case LIGHT:
setTypeface(HelveticaNeueMedium.getInstance(context).getTypeFace());
break;
case REGULAR:
setTypeface(HelveticaNeue.getInstance(context).getTypeFace());
break;
default:
break;
}
} catch (Exception e) {
e.printStackTrace();
}
}
public CustomTextView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public boolean setCustomFont(Context ctx, String asset) {
try {
tf = Typeface.createFromAsset(ctx.getAssets(), asset);
} catch (Exception e) {
LogUtils.LogE(TAG, "Could not get typeface: " + e.getMessage());
return false;
}
setTypeface(tf);
return true;
}}
Herein I have changed textcolor in accordance to theme value from sharedPref
if (preferenceUtils.getBooleanValue(PrefsKeyValue.bTheme)) {
this.setTextColor(ResourceUtils.getColor(R.color.lightThemeTextColor));
} else {
this.setTextColor(ResourceUtils.getColor(R.color.colorWhite));
}
Then use this class as textview tag in xml file.
<com.mypkg.customview.CustomTextView
style="#style/signup_textViewStyle"
android:text="#string/activity_login_password" />
I believe, you can handle property variation with theme for controls in same manner.
What you can do is create custom attributes for your view types (e.g. TextView.Person, TextView.Date...), in your xml you can reference the attributes and then define the attributes in different themes. For instance, you style.xml could be
<resources>
<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar" >
<item name="TextView.Date">#style/DateTextViewDefault</item>
</style>
<style name="DateTextViewDefault">
<item name="android:textColor">#ff333333</item>
<item name="android:fontFamily">monospace</item>
</style>
<!-- Theme A -->
<style name="AppTheme.A">
<item name="colorPrimary">#3F51B5</item>
<item name="colorPrimaryDark">#303F9F</item>
<item name="colorAccent">#FF4081</item>
<item name="TextView.Person">#style/PersonTextViewA</item>
</style>
<style name="PersonTextViewA">
<item name="android:textSize">16sp</item>
<item name="android:fontFamily">serif</item>
<item name="android:textColor">#ff999999</item>
</style>
<!-- Theme B -->
<style name="AppTheme.B">
<item name="colorPrimary">#888888</item>
<item name="colorPrimaryDark">#555555</item>
<item name="colorAccent">#000000</item>
<item name="TextView.Person">#style/PersonTextViewB</item>
<item name="TextView.Date">#style/DateTextViewB</item>
</style>
<style name="PersonTextViewB">
<item name="android:textSize">20sp</item>
<item name="android:fontFamily">monospace</item>
<item name="android:textColor">#ff55aa</item>
</style>
<style name="DateTextViewB">
<item name="android:textColor">#ff0000BB</item>
<item name="android:fontFamily">sans-serif</item>
</style>
<attr name="TextView.Person" format="reference" />
<attr name="TextView.Date" format="reference" />
</resources>
then your activity xml layout
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<TextView
style="?attr/TextView.Person"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="John Doe" />
<TextView
style="?attr/TextView.Date"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="31/12/1999" />
<Button
android:id="#+id/buttonA"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="THEME A" />
<Button
android:id="#+id/buttonB"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="THEME B" />
</LinearLayout>
notice that the styles of the TextView are
style="?attr/TextView.Person"
and
style="?attr/TextView.Date"
AppTheme.A and AppTheme.B have 2 different resolutions for those attributes
In this example the attributes are entire styles for your views but you could easily have one style per view type (TextView.person) and then define generic attributes for single items of that style, e.g.
<attr name="TextView.Person.TextColor" format="color" />
and only change that single attribute in your themes.
Then in your Activity you just need to set the theme in onCreate with setTheme(int), the value could be in this case either R.style.AppTheme_A or R.style.AppTheme_B.
With this method you can add as many styles as you want without touching the layouts. Also, you can always define some default styles in your base theme and then only override that value in some of the custom themes, while others use the default one as for TextView.Date in the sample above.
If you want to give it a quick try, here's the code of the Activity I used to test style.xml and activity_main.xml above
class MainActivity : AppCompatActivity() {
private val prefs by lazy { getSharedPreferences("SharedPrefs", Context.MODE_PRIVATE) }
private var customTheme: Int
get() = prefs.getInt("theme", R.style.AppTheme_A)
set(value) = prefs.edit()
.putInt("theme", value)
.apply()
.also { recreate() }
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setTheme(customTheme)
setContentView(R.layout.activity_main)
buttonA.setOnClickListener { customTheme = R.style.AppTheme_A }
buttonB.setOnClickListener { customTheme = R.style.AppTheme_B }
}
}
Related
In the above picture GAIN 3 is selected but its not visible properly , so how can i change that color to darker color.
basically i want to change the selected text background in darker color.
I'm using com.jaredrummler.materialspinner.MaterialSpinner Spinner.
Here's the java implementation.
spinner.setOnItemSelectedListener(new MaterialSpinner.OnItemSelectedListener<String>() {
#Override public void onItemSelected(MaterialSpinner view, int position, long id, String item) {
text = spinner.getText().toString();
Log.e("Spinner Listener",text);
if(text.contains("GAIN 0")){
sendToDevice("F");
} else if(text.contains("GAIN 1")){
sendToDevice("G");
} else if(text.contains("GAIN 2")){
sendToDevice("H");
} else if(text.contains("GAIN 3")){
sendToDevice("I");
}
}
});
And the layout item looks like the following.
<com.jaredrummler.materialspinner.MaterialSpinner
android:id="#+id/spinner"
app:ms_dropdown_max_height="350dp"
app:ms_dropdown_height="wrap_content"
android:textColorHighlight="#000000"
android:layout_width="130dp"
style="#style/spinner_style"
android:popupTheme="#android:style/ThemeOverlay.Material"
android:textColor="#color/blue"
android:layout_below="#+id/testmodetitle"
android:layout_height="wrap_content"
android:layout_marginTop="55dp"
android:layout_alignBaseline="#+id/button1"
android:layout_alignBottom="#+id/button1"
android:layout_toEndOf="#+id/button1"
android:layout_marginStart="30dp" />
To change background color and other color this library has provided some attributes. To change background color of selected item use below code.
<com.jaredrummler.materialspinner.MaterialSpinner
android:id="#+id/spinner"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:ms_background_selector="#drawable/selector_gray_white_spinner"
app:ms_dropdown_height="wrap_content"
app:ms_dropdown_max_height="350dp" />
create one selector in drawable having name selector_gray_white_spinner.xml
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_focused="true" android:state_pressed="true" android:drawable="#color/darkGray"/>
<item android:state_focused="false" android:state_pressed="true" android:drawable="#color/darkGray"/>
<item android:state_focused="true" android:drawable="#android:color/white"/>
<item android:state_focused="false" android:state_pressed="false" android:drawable="#android:color/white"/>
</selector>
Add dark color in your color.xml file
<color name="darkGray">#acacac</color>
Use this way it will help you:
ArrayAdapter<String> dataAdapter = new ArrayAdapter<String>(this,
android.R.layout.simple_spinner_item, list) {
#Override
public View getDropDownView(int position, View convertView, ViewGroup parent)
{
View v = null;
v = super.getDropDownView(position, null, parent);
// If this is the selected item position
if (position == selectedItem) {
v.setBackgroundColor(Color.BLUE);
}
else {
// for other views
v.setBackgroundColor(Color.WHITE);
}
return v;
}
};
There are some attributes available along with the implementation of that specific library. Please have a look in the readme.md section where the attributes are listed.
I think you might consider using ms_background_selector attribute in your layout where you have declared the spinner.
So the layout declaration will look like this.
<com.jaredrummler.materialspinner.MaterialSpinner
android:id="#+id/spinner"
app:ms_background_selector="#drawable/your_darker_selector"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
Add a file named your_darker_selector.xml and put the following code inside the file.
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_checked="true" android:drawable="#android:color/darker_gray"/>
<item android:state_checked="false" android:drawable="#android:color/white" />
</selector>
Modify the color from the selector file as per your necessity.
Give html colour code for the first item of spinner.
String styledText = "This is <font color='red'>simple</font>.";
textView.setText(Html.fromHtml(styledText),
TextView.BufferType.SPANNABLE);
What I have:
I have a custom class inheriting AppCompatTextView.
I have defined a custom attribute textformat in attires.xml and
i am passing what font I need to set from the xml
Stylefile
<style name="HeaderFilterName">
<item name="android:src">#drawable/back_button</item>
<item name="android:text">#string/str_filter_edit</item>
<item name="android:gravity">center</item>
<item name="android:textSize">#dimen/Header_Filter_Name_Text_size</item>
<item name="android:layout_weight">1</item>
</style>
XML
<customViews.CustomTftTextView
android:id="#+id/txtScreenNameId"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:textformat="fonts/sf_san_fransisco.ttf"
style="#style/HeaderFilterName"/>
attr.xml
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="customfont">
<attr name="textformat" format="string"/>
</declare-styleable>
</resources>
CustomTftTextView.java
public class CustomTftTextView extends AppCompatTextView {
private String text;
public CustomTftTextView(final Context context) {
this(context, null);
Initialize(text,context);
}
public CustomTftTextView(final Context context, final AttributeSet attrs) {
this(context, attrs, 0);
text = context.getResources().obtainAttributes(attrs, R.styleable.customfont).getString(R.styleable.customfont_textformat);
Initialize(text,context);
}
public CustomTftTextView(final Context context, final AttributeSet attrs, final int defStyle) {
super(context, attrs, defStyle);
text = context.getResources().obtainAttributes(attrs, R.styleable.customfont).getString(R.styleable.customfont_textformat);
Initialize(text,context);
}
private void Initialize(String format, Context context) {
Typeface mTypeface;
if (format != null)
{
mTypeface = Typeface.createFromAsset(context.getAssets(), format);
}
else
{
mTypeface = Typeface.createFromAsset(context.getAssets(), "fonts/sf_san_fransisco.ttf");
}
setTypeface(mTypeface, Typeface.NORMAL);
setLineSpacing(0.0f, 1.4f);
}
}
While above code works perfect, If I move app:textformat inside the style file, The font is not setting.
<style name="HeaderFilterName">
<item name="android:src">#drawable/back_button</item>
<item name="android:text">#string/str_filter_edit</item>
<item name="textformat">#string/custom_font_medium </item>
<item name="android:gravity">center</item>
<item name="android:textSize">#dimen/Header_Filter_Name_Text_size</item>
<item name="android:layout_weight">1</item>
</style>
strings.xml
<string name="custom_font_medium">fonts/sf_san_fransisco.ttf</string>
How properly achieve this
If you check the docs for the Resources#obtainAttributes() method, it says:
Retrieve a set of basic attribute values from an AttributeSet, not performing styling of them using a theme and/or style resources.
To get the attributes with your style applied, use a Context#obtainStyledAttributes() method instead. It would also be advisable to keep a reference to that return, so you can recycle() it when done. For example:
TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.customfont);
text = a.getString(R.styleable.customfont_textformat);
a.recycle();
Make sure in styles.xml you used namespace like below
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app='http://schemas.android.com/apk/res-auto'>
I have an android.support.v7.widget Toolbar in my Android app. The background color of this is bright orange and the best looking color on top of this would be white instead of black.
I have the default color on black and not white. Since it would conflict with other stuff, that is almost impossible to override. I cannot change the primary text color to white!
I've managed to change the title color.
What I'm looking for right now is how I can change the action button color as well (to white).
My code:
Main activity:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
xmlns:app="http://schemas.android.com/apk/res-auto"
tools:context=".UI.activities.MainActivity">
<android.support.v7.widget.Toolbar
android:id="#+id/r2_toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="?attr/colorPrimary"
android:elevation="4dp"
app:titleTextColor="#color/primary_text_material_light"
app:subtitleTextColor="#color/primary_text_material_light"
android:theme="#style/R2Theme.Toolbar"/>
<fragment android:name="com.r2retail.r2retailapp.UI.fragments.SetupFragment"
android:layout_below="#+id/r2_toolbar"
android:id="#+id/list"
android:layout_weight="1"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</RelativeLayout>
Menu bar:
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<item android:id="#+id/about"
android:icon="#drawable/ic_menu"
android:title="About"
app:showAsAction="never"/>
</menu>
Styles:
<resources>
<style name="R2Theme" parent="Theme.AppCompat.Light.NoActionBar">=
<item name="colorPrimary">#color/colorPrimary</item>
<item name="colorPrimaryDark">#color/colorPrimaryDark</item>
<item name="colorAccent">#color/colorPrimary</item>
<item name="android:textColorPrimary">#color/secondary_text_material_dark</item>
<item name="android:textColorSecondaryInverse">#color/primary_text_material_light</item>
</style>
<style name="R2Theme.Toolbar" parent="R2Theme">
<item name="actionMenuTextColor">#color/primary_text_material_light</item>
</style>
</resources>
In styles:
<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
...
<item name="actionOverflowButtonStyle">#style/MyOverflowButtonStyle</item>
</style>
<style name="MyOverflowButtonStyle" parent="Widget.AppCompat.ActionButton.Overflow">
<item name="android:tint">#62ff00</item>
</style>
Result:
The solution is to replace icon itself.
1st
Go to values/styles and in your styles.xml file add:
<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
<!-- Customize your theme here. -->
<item name="android:actionOverflowButtonStyle">#style/MyActionButtonOverflow</item>
</style>
<style name="MyActionButtonOverflow" parent="android:style/Widget.Holo.Light.ActionButton.Overflow">
<!--Here you need to put name of drawable you will create during the next step-->
<item name="android:src">#drawable/your_white_icon</item>
</style>
2nd
Then go to drawable folder. Right mouse click -> new -> vector asset.
Then press on Icon image and choose from suggested icon named ic_more_vert_black_24dp.
Customize it -> press next -> finish.
Then open newly created icon file. Code looks like this.
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24.0"
android:viewportHeight="24.0">
<path
android:fillColor="#FFFFFFFF" <!-- Here u can change color-->
android:pathData="M12,8c1.1,0 2,-0.9 2,-2s-0.9,-2 -2,-2 -2,0.9 -2,2 0.9,2 2,2zM12,10c-1.1,0 -2,0.9 -2,2s0.9,2 2,2 2,-0.9 2,-2 -0.9,-2 -2,-2zM12,16c-1.1,0 -2,0.9 -2,2s0.9,2 2,2 2,-0.9 2,-2 -0.9,-2 -2,-2z"/>
</vector>
Change fillColor attribute to the color you need. Put this file to the styles how described in 1st step.
Voila! Color of our three dots changed not depending on base app styles (result for #FF2012 color).
An alternative way, in code instead of XML:
public static boolean colorizeToolbarOverflowButton(#NonNull Toolbar toolbar, #ColorInt int color) {
final Drawable overflowIcon = toolbar.getOverflowIcon();
if (overflowIcon == null)
return false;
toolbar.setOverflowIcon(getTintedDrawable(toolbar.getContext(), overflowIcon, toolbarIconsColor));
return true;
}
public static Drawable getTintedDrawable(#NonNull Context context, #NonNull Drawable inputDrawable, #ColorInt int color) {
Drawable wrapDrawable = DrawableCompat.wrap(inputDrawable);
DrawableCompat.setTint(wrapDrawable, color);
DrawableCompat.setTintMode(wrapDrawable, Mode.SRC_IN);
return wrapDrawable;
}
The function will return true if succeeded to colorize the overflow icon.
And another alternative, in case you prefer not to use a tinted drawable:
public static boolean colorizeToolbarOverflowButton(#NonNull Toolbar toolbar, #ColorInt Integer color) {
final Drawable overflowIcon = toolbar.getOverflowIcon();
if (overflowIcon == null)
return false;
final PorterDuffColorFilter colorFilter = toolbarIconsColor == null ? null : new PorterDuffColorFilter(toolbarIconsColor, PorterDuff.Mode.MULTIPLY);
overflowIcon.setColorFilter(colorFilter);
return true;
}
In addition, if you wish to colorize the icons of the action items and the nav item, you can try this (based on here):
/**
* 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
*/
#JvmStatic
#UiThread
fun colorizeToolbarActionItemsAndNavButton(toolbarView: Toolbar, #ColorInt toolbarIconsColor: Int?) {
//https://snow.dog/blog/how-to-dynamicaly-change-android-toolbar-icons-color/
val colorFilter = if (toolbarIconsColor == null) null else PorterDuffColorFilter(toolbarIconsColor, Mode.MULTIPLY)
for (i in 0 until toolbarView.childCount) {
val v = toolbarView.getChildAt(i)
//Step 1 : Changing the color of back button (or open drawer button).
if (v is ImageButton) {
//Action Bar back button
v.drawable.mutate().colorFilter = colorFilter
}
if (v is ActionMenuView) {
for (j in 0 until v.childCount) {
//Step 2: Changing the color of any ActionMenuViews - icons that
//are not back button, nor text, nor overflow menu icon.
val innerView = v.getChildAt(j)
if (innerView is ActionMenuItemView) {
val drawablesCount = innerView.compoundDrawables.size
for (k in 0 until drawablesCount) {
if (innerView.compoundDrawables[k] != null) {
innerView.post { innerView.compoundDrawables[k].mutate().colorFilter = colorFilter }
}
}
}
}
}
}
}
Usage:
override fun onCreateOptionsMenu(menu: Menu): Boolean {
menuInflater.inflate(R.menu.menu_main, menu)
toolbar.doOnPreDraw {
colorizeToolbarActionItemsAndNavButton(toolbar,0xffff0000.toInt())
}
return true
}
For AndroidX users (really don't know if it works using old Support Library):
TL;DR:
<style name="MyToolbarTheme">
<item name="colorControlNormal">#color/white</item>
</style>
Apply MyToolbarTheme to your Toolbar view.
Long explanation:
Widget.AppCompat.ActionButton.Overflow extends Base.Widget.AppCompat.ActionButton.Overflow. We'll be talking about the latter:
On the default implementation:
<style name="Base.Widget.AppCompat.ActionButton.Overflow" parent="RtlUnderlay.Widget.AppCompat.ActionButton.Overflow">
<item name="srcCompat">#drawable/abc_ic_menu_overflow_material</item>
...
</style>
On API 21 implementation:
<style name="Base.Widget.AppCompat.ActionButton.Overflow" parent="android:Widget.Material.ActionButton.Overflow">
<item name="android:src">#null</item>
<item name="srcCompat">#drawable/abc_ic_menu_overflow_material</item>
</style>
On API 23 and higher implementation:
It extends android:Widget.Material.ActionButton.Overflow.
<style name="Widget.Material.ActionButton.Overflow">
<item name="src">#drawable/ic_menu_moreoverflow_material</item>
...
</style>
We can notice that every implementation uses #drawable/ic_menu_moreoverflow_material.
Inside this drawable's implementation you can see the following:
android:tint="?attr/colorControlNormal"
If you want to change the color of icons (Navigation icon, menu item icons) in your toolbar, you can simply use the code below. I had saved problem and solved using this.
<!--Light Theme-->
<style name="AppThemeLight" parent="Theme.MaterialComponents.Light.NoActionBar">
<!--other colors and properties-->
<item name="iconTint">#color/colorBlack</item>
</style>
<!-- Dark/Night theme. -->
<style name="AppThemeDark" parent="Theme.MaterialComponents.NoActionBar">
<!--other colors and properties-->
<item name="iconTint">#color/colorWhite</item>
</style>
If anyone's looking to change it programmatically, and the following didn't work:
mBinding.toolbar.overflowIcon?.setTint(Color.WHITE)
OR
mBinding.toolbar.overflowIcon?.setColorFilter(Color.WHITE, PorterDuff.Mode.SRC_ATOP)
OR
mBinding.toolbar.overflowIcon?.colorFilter = BlendModeColorFilterCompat.createBlendModeColorFilterCompat(Color.WHITE, BlendModeCompat.SRC_ATOP)
OR
val overflowIcon = ContextCompat.getDrawable(this, R.drawable.dots_vertical_black)
overflowIcon?.setTint(Color.WHITE)
mBinding.toolbar.overflowIcon = overflowIcon
TRY THIS.
Finally the below line worked for me (after 2 days of trial and error -_-)
mBinding.toolbar.menu?.findItem(R.id.menu)?.icon?.setTint(Color.WHITE)
I would like to define what color should be used when painting to a canvas depending on a custom state. This is how far I got:
In res/layout/content.xml:
<com.example.package.MyView
app:primary_color="#drawable/my_selector"
/>
primary_color is a custom attribute defined in res/values/attrs.xml:
<resource>
<declare-styleable name="MyView">
<attr name="primary_color" format="reference"/>
</declare-styleable>
</resource>
my_selector is defined in res/drawable/my_selector.xml
<selector xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res/com.example.package">
<item
app:state_a="true"
android:drawable="#drawable/red" />
<item
app:state_b="true"
android:drawable="#drawable/orange" />
<item
app:state_c="true"
android:drawable="#drawable/red" />
</selector>
red, orange and red are defined in res/values/colordrawable.xml:
<resources>
<drawable name="red">#f00</drawable>
<drawable name="orange">#fb0</drawable>
<drawable name="green">#0f0</drawable>
</resources>
In MyView I can get this drawable:
StateListDrawable primaryColor;
public MyView(Context context, AttributeSet attrs) {
super(context, attrs);
try{
primaryColor = (StateListDrawable) a.getDrawable(
R.styleable.MyView_primary_color);
}finally {
a.recycle();
}
}
primaryColor updates correctly with the different states, I can test this by calling:
setBackground(primaryColor);
But I want to use this color with Paint, like this:
paint.setColor(primaryColor);
But this is obviously not allowed. I've tried converting the primaryColor to a ColorDrawable which has the method getColor(), but I can't figure out how to do this, if it is possible.
Any suggestions on how to get the color that can be used in the the view from a selector would be amazing.
I found ColorStateList which turned out to be exactly what I needed. The following is a simplified version of my current implementation, in case someone else gets in the same rut as I did.
In res/color/my_selector.xml
<?xml version="1.0" encoding="utf-8" ?>
<selector xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"">
<item app:state_weak="true" android:color="#F00" />
<item app:state_average="true" android:color="#0F0" />
<item app:state_strong="true" android:color="#00F" />
<item android:color="#FA0" />
</selector>
In res/layout/content.xml (This has another layout wrapped around it, but that is not relevant)
<com.example.package.MyView
android:id="#+id/strMeter"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:primary_color="#color/my_selector"
/>
primary_color is defines as a reference in res/values/attrs.xml
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="MyView">
<attr name="primary_color" format="reference"/>
</declare-styleable>
</resources>
I get the reference to the ColorStateList in the constructor of MyView:
ColorStateList primaryColor;
public PasswordStrengthBar(Context context, AttributeSet attrs) {
super(context, attrs);
try{
primaryColor = a.getColorStateList(
R.styleable.MyView_primary_color);
}finally {
a.recycle();
}
}
When I want to get the color for the current state:
int color = secondaryColor.getColorForState(
getDrawableState(), primaryColor.getDefaultColor());
If you implement a custom state, like I did, then you will also have to override onCreateDrawableState for the states to actually update, but there is plenty of documentation/posts that cover that.
I am using appcompat v7 to get the look consistent on Android 5 and less. It works rather well. However I cannot figure out how to change the bottom line color and the accent color for EditTexts. Is it possible?
I have tried to define a custom android:editTextStyle (cf. below) but I only succeeded to change the full background color or text color but not the bottom line nor the accent color. Is there a specific property value to use? do I have to use a custom drawable image through the android:background property? is it not possible to specify a color in hexa?
<style name="Theme.App.Base" parent="Theme.AppCompat.Light.DarkActionBar">
<item name="android:editTextStyle">#style/Widget.App.EditText</item>
</style>
<style name="Widget.App.EditText" parent="Widget.AppCompat.EditText">
???
</style>
According to android API 21 sources, EditTexts with material design seem to use colorControlActivated and colorControlNormal. Therefore, I have tried to override these properties in the previous style definition but it has no effect. Probably appcompat does not use it. Unfortunately, I cannot find the sources for the last version of appcompat with material design.
Finally, I have found a solution. It simply consists of overriding the value for colorControlActivated, colorControlHighlight and colorControlNormal in your app theme definition and not your edittext style. Then, think to use this theme for whatever activity you desire. Below is an example:
<style name="Theme.App.Base" parent="Theme.AppCompat.Light.DarkActionBar">
<item name="colorControlNormal">#c5c5c5</item>
<item name="colorControlActivated">#color/accent</item>
<item name="colorControlHighlight">#color/accent</item>
</style>
I felt like this needed an answer in case somebody wanted to change just a single edittext. I do it like this:
editText.getBackground().mutate().setColorFilter(ContextCompat.getColor(context, R.color.your_color), PorterDuff.Mode.SRC_ATOP);
While Laurents solution is correct, it comes with some drawbacks as described in the comments since not only the bottom line of the EditText gets tinted but the Back Button of the Toolbar, CheckBoxes etc. as well.
Luckily v22.1 of appcompat-v7 introduced some new possibilities. Now it's possible to assign a specific theme only to one view. Straight from the Changelog:
Deprecated use of app:theme for styling Toolbar. You can now use android:theme for toolbars on all API level 7 and higher devices and android:theme support for all widgets on API level 11 and higher devices.
So instead of setting the desired color in a global theme, we create a new one and assign it only to the EditText.
Example:
<style name="MyEditTextTheme">
<!-- Used for the bottom line when not selected / focused -->
<item name="colorControlNormal">#9e9e9e</item>
<!-- colorControlActivated & colorControlHighlight use the colorAccent color by default -->
</style>
<EditText
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:theme="#style/MyEditTextTheme"/>
This can be changed in XML by using:
For Reference API >= 21 compatibility use:
android:backgroundTint="#color/blue"
For backward API < 21 compatibility use:
app:backgroundTint="#color/blue"
Here is the solution for API < 21 and above
Drawable drawable = yourEditText.getBackground(); // get current EditText drawable
drawable.setColorFilter(Color.GREEN, PorterDuff.Mode.SRC_ATOP); // change the drawable color
if(Build.VERSION.SDK_INT > 16) {
yourEditText.setBackground(drawable); // set the new drawable to EditText
}else{
yourEditText.setBackgroundDrawable(drawable); // use setBackgroundDrawable because setBackground required API 16
}
Hope it help
The accepted answer is a bit more per style basis thing, but the most efficient thing to do is to add the colorAccent attribute in your AppTheme style like this:
<style name="AppTheme.Base" parent="Theme.AppCompat.Light.NoActionBar">
<item name="colorAccent">#color/colorAccent</item>
<item name="android:editTextStyle">#style/EditTextStyle</item>
</style>
<style name="EditTextStyle" parent="Widget.AppCompat.EditText"/>
The colorAccent attribute is used for widget tinting throughout the app and thus should be used for consistency
If you are using appcompat-v7:22.1.0+ you can use the DrawableCompat to tint your widgets
public static void tintWidget(View view, int color) {
Drawable wrappedDrawable = DrawableCompat.wrap(view.getBackground());
DrawableCompat.setTint(wrappedDrawable.mutate(), getResources().getColor(color));
view.setBackgroundDrawable(wrappedDrawable);
}
<!-- Base application theme. -->
<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
<!-- Customize your theme here. -->
<item name="colorPrimary">#color/colorPrimary</item>
<item name="colorPrimaryDark">#color/colorPrimaryDark</item>
<item name="colorAccent">#color/colorAccent</item>
<item name="colorControlNormal">#color/colorAccent</item>
<item name="colorControlActivated">#color/colorAccent</item>
<item name="colorControlHighlight">#color/colorAccent</item>
</style>
Use:
<EditText
app:backgroundTint="#color/blue"/>
This will support pre-Lollipop devices not only +21
One quick solution for your problem is to look in yourappspackage/build/intermediates/exploded-aar/com.android.support/appcompat-v7/res/drawable/ for abc_edit_text_material.xml and copy that xml file in your drawable folder. Then you can change the colour of the 9 patch files from inside this selector, in order to match your preferences.
It's very easy just add android:backgroundTint attribute in your EditText.
android:backgroundTint="#color/blue"
android:backgroundTint="#ffffff"
android:backgroundTint="#color/red"
<EditText
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:backgroundTint="#ffffff"/>
Here is a part of source code of TextInputLayout in support design library(UPDATED for version 23.2.0), which changes EditText's bottom line color in a simpler way:
private void updateEditTextBackground() {
ensureBackgroundDrawableStateWorkaround();
final Drawable editTextBackground = mEditText.getBackground();
if (editTextBackground == null) {
return;
}
if (mErrorShown && mErrorView != null) {
// Set a color filter of the error color
editTextBackground.setColorFilter(
AppCompatDrawableManager.getPorterDuffColorFilter(
mErrorView.getCurrentTextColor(), PorterDuff.Mode.SRC_IN));
}
...
}
It seems that all of above code become useless right now in 23.2.0 if you want to change the color programatically.
And if you want to support all platforms, here is my method:
/**
* Set backgroundTint to {#link View} across all targeting platform level.
* #param view the {#link View} to tint.
* #param color color used to tint.
*/
public static void tintView(View view, int color) {
final Drawable d = view.getBackground();
final Drawable nd = d.getConstantState().newDrawable();
nd.setColorFilter(AppCompatDrawableManager.getPorterDuffColorFilter(
color, PorterDuff.Mode.SRC_IN));
view.setBackground(nd);
}
I too was stuck on this problem for too long.
I required a solution that worked for versions both above and below v21.
I finally discovered a very simple perhaps not ideal but effective solution: Simply set the background colour to transparent in the EditText properties.
<EditText
android:background="#android:color/transparent"/>
I hope this saves someone some time.
For me I modified both the AppTheme and a value colors.xml Both the colorControlNormal and the colorAccent helped me change the EditText border color. As well as the cursor, and the "|" when inside an EditText.
<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
<!-- Customize your theme here. -->
<item name="colorControlNormal">#color/yellow</item>
<item name="colorAccent">#color/yellow</item>
</style>
Here is the colors.xml
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="yellow">#B7EC2A</color>
</resources>
I took out the android:textCursorDrawable attribute to #null that I placed inside the editText style. When I tried using this, the colors would not change.
You can set background of edittext to a rectangle with minus padding on left, right and top to achieve this. Here is the xml example:
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:top="-1dp"
android:left="-1dp"
android:right="-1dp"
android:bottom="1dp"
>
<shape android:shape="rectangle">
<stroke android:width="1dp" android:color="#6A9A3A"/>
</shape>
</item>
</layer-list>
Replace the shape with a selector if you want to provide different width and color for focused edittext.
I worked out a working solution to this problem after 2 days of struggle, below solution is perfect for them who want to change few edit text only, change/toggle color through java code, and want to overcome the problems of different behavior on OS versions due to use setColorFilter() method.
import android.content.Context;
import android.graphics.PorterDuff;
import android.graphics.drawable.Drawable;
import android.support.v4.content.ContextCompat;
import android.support.v7.widget.AppCompatDrawableManager;
import android.support.v7.widget.AppCompatEditText;
import android.util.AttributeSet;
import com.newco.cooltv.R;
public class RqubeErrorEditText extends AppCompatEditText {
private int errorUnderlineColor;
private boolean isErrorStateEnabled;
private boolean mHasReconstructedEditTextBackground;
public RqubeErrorEditText(Context context) {
super(context);
initColors();
}
public RqubeErrorEditText(Context context, AttributeSet attrs) {
super(context, attrs);
initColors();
}
public RqubeErrorEditText(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
initColors();
}
private void initColors() {
errorUnderlineColor = R.color.et_error_color_rule;
}
public void setErrorColor() {
ensureBackgroundDrawableStateWorkaround();
getBackground().setColorFilter(AppCompatDrawableManager.getPorterDuffColorFilter(
ContextCompat.getColor(getContext(), errorUnderlineColor), PorterDuff.Mode.SRC_IN));
}
private void ensureBackgroundDrawableStateWorkaround() {
final Drawable bg = getBackground();
if (bg == null) {
return;
}
if (!mHasReconstructedEditTextBackground) {
// This is gross. There is an issue in the platform which affects container Drawables
// where the first drawable retrieved from resources will propogate any changes
// (like color filter) to all instances from the cache. We'll try to workaround it...
final Drawable newBg = bg.getConstantState().newDrawable();
//if (bg instanceof DrawableContainer) {
// // If we have a Drawable container, we can try and set it's constant state via
// // reflection from the new Drawable
// mHasReconstructedEditTextBackground =
// DrawableUtils.setContainerConstantState(
// (DrawableContainer) bg, newBg.getConstantState());
//}
if (!mHasReconstructedEditTextBackground) {
// If we reach here then we just need to set a brand new instance of the Drawable
// as the background. This has the unfortunate side-effect of wiping out any
// user set padding, but I'd hope that use of custom padding on an EditText
// is limited.
setBackgroundDrawable(newBg);
mHasReconstructedEditTextBackground = true;
}
}
}
public boolean isErrorStateEnabled() {
return isErrorStateEnabled;
}
public void setErrorState(boolean isErrorStateEnabled) {
this.isErrorStateEnabled = isErrorStateEnabled;
if (isErrorStateEnabled) {
setErrorColor();
invalidate();
} else {
getBackground().mutate().clearColorFilter();
invalidate();
}
}
}
Uses in xml
<com.rqube.ui.widget.RqubeErrorEditText
android:id="#+id/f_signup_et_referral_code"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentTop="true"
android:layout_toEndOf="#+id/referral_iv"
android:layout_toRightOf="#+id/referral_iv"
android:ems="10"
android:hint="#string/lbl_referral_code"
android:imeOptions="actionNext"
android:inputType="textEmailAddress"
android:textSize="#dimen/text_size_sp_16"
android:theme="#style/EditTextStyle"/>
Add lines in style
<style name="EditTextStyle" parent="android:Widget.EditText">
<item name="android:textColor">#color/txt_color_change</item>
<item name="android:textColorHint">#color/et_default_color_text</item>
<item name="colorControlNormal">#color/et_default_color_rule</item>
<item name="colorControlActivated">#color/et_engagged_color_rule</item>
</style>
java code to toggle color
myRqubeEditText.setErrorState(true);
myRqubeEditText.setErrorState(false);
In Activit.XML add the code
<EditText
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:inputType="textPersonName"
android:ems="10"
android:id="#+id/editText"
android:hint="Informe o usuário"
android:backgroundTint="#android:color/transparent"/>
Where BackgroundTint=color for your desired colour
I use this method to change the color of the line with PorterDuff, with no other drawable.
public void changeBottomColorSearchView(int color) {
int searchPlateId = mSearchView.getContext().getResources().getIdentifier("android:id/search_plate", null, null);
View searchPlate = mSearchView.findViewById(searchPlateId);
searchPlate.getBackground().setColorFilter(color, PorterDuff.Mode.SRC_IN);
}
If you want change bottom line without using app colors, use these lines in your theme:
<item name="android:editTextStyle">#android:style/Widget.EditText</item>
<item name="editTextStyle">#android:style/Widget.EditText</item>
I don't know another solution.
I was absolutely baffled by this problem. I had tried everything in this thread, and in others, but no matter what I did I could not change the color of the underline to anything other than the default blue.
I finally figured out what was going on. I was (incorrectly) using android.widget.EditText when making a new instance (but the rest of my components were from the appcompat library). I should have used android.support.v7.widget.AppCompatEditText. I replaced new EditText(this) with new AppCompatEditText(this)
and the problem was instantly solved. It turns out, if you are actually using AppCompatEditText, it will just respect the accentColor from your theme (as mentioned in several comments above) and no additional configuration is necessary.
This is the easiest and most efficient/reusable/works on all APIs
Create a custom EditText class like so:
public class EditText extends android.widget.EditText {
public EditText(Context context) {
super(context);
init();
}
public EditText(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
public EditText(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}
private void init() {
getBackground().mutate().setColorFilter(ContextCompat.getColor(getContext(), R.color.colorAccent), PorterDuff.Mode.SRC_ATOP);
}
}
Then use it like this:
<company.com.app.EditText
android:layout_width="200dp"
android:layout_height="wrap_content"/>
To change the EditText background dynamically, you can use ColorStateList.
int[][] states = new int[][] {
new int[] { android.R.attr.state_enabled}, // enabled
new int[] {-android.R.attr.state_enabled}, // disabled
new int[] {-android.R.attr.state_checked}, // unchecked
new int[] { android.R.attr.state_pressed} // pressed
};
int[] colors = new int[] {
Color.BLACK,
Color.RED,
Color.GREEN,
Color.BLUE
};
ColorStateList colorStateList = new ColorStateList(states, colors);
Credits: This SO answer about ColorStateList is awesome.
You can use just backgroundTint for change bottom line color of edit text
android:backgroundTint="#000000"
example :
<EditText
android:id="#+id/title1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:backgroundTint="#000000" />
Add app:backgroundTint for below api level 21. Otherwise use android:backgroundTint.
For below api level 21.
<EditText
android:id="#+id/edt_name"
android:layout_width="300dp"
android:layout_height="wrap_content"
android:textColor="#0012ff"
app:backgroundTint="#0012ff"/>
For higher than api level 21.
<EditText
android:id="#+id/edt_name"
android:layout_width="300dp"
android:layout_height="wrap_content"
android:textColor="#0012ff"
android:backgroundTint="#0012ff"/>
Please modify this method according to your need. This worked for me!
private boolean validateMobilenumber() {
if (mobilenumber.getText().toString().trim().isEmpty() || mobilenumber.getText().toString().length() < 10) {
input_layout_mobilenumber.setErrorEnabled(true);
input_layout_mobilenumber.setError(getString(R.string.err_msg_mobilenumber));
// requestFocus(mobilenumber);
return false;
} else {
input_layout_mobilenumber.setError(null);
input_layout_mobilenumber.setErrorEnabled(false);
mobilenumber.setBackground(mobilenumber.getBackground().getConstantState().newDrawable());
}
}