Before the AppCompat
update came out today I was able to change the color of buttons in Android L but not on older versions. After including the new AppCompat update I am unable to change the color for either version, when I do try the button just disappears. Does anyone know how to change the button color?
The following pictures shows what I want to achieve:
The white button is default, the red one is what I want.
This is what I was doing previously to change the color of the buttons in the styles.xml
:
<item name="android:colorButtonNormal">insert color here</item>
and to do it dynamically:
button.getBackground().setColorFilter(getResources().getColor(insert color here), PorterDuff.Mode.MULTIPLY);
Also I did change the theme parent from @android:style/Theme.Material.Light.DarkActionBar
to Theme.AppCompat.Light.DarkActionBar
This question is related to
android
button
android-view
android-button
material-design
if you want to do this via code in any color use this:
DrawableCompat.setTintList(button.getBackground(), ColorStateList.valueOf(yourColor));
I've just created an android library, that allows you to easily modify the button color and the ripple color
https://github.com/xgc1986/RippleButton
<com.xgc1986.ripplebutton.widget.RippleButton
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/btn"
android:text="Android button modified in layout"
android:textColor="@android:color/white"
app:buttonColor="@android:color/black"
app:rippleColor="@android:color/white"/>
You don't need to create an style for every button you want wit a different color, allowing you to customize the colors randomly
One way to pull this off allows you to just point to a style and NOT theme ALL the buttons in your app the same.
In themes.xml add a theme
<style name="Theme.MyApp.Button.Primary.Blue" parent="Widget.AppCompat.Button">
<item name="colorButtonNormal">@color/someColor</item>
<item name="android:textColorPrimary">@android:color/white</item>
</style>
Now in styles.xml add
<style name="MyApp.Button.Primary.Blue" parent="">
<item name="android:theme">@style/Theme.MyApp.Button.Primary.Blue</item>
</style>
Now in your layout simply point to the STYLE in your Button
<Button
...
style="@style/MyApp.Button.Primary.Blue"
... />
This SO answer helped me arrive at an answer https://stackoverflow.com/a/30277424/3075340
I use this utility method to set the background tint of a button. It works with pre-lollipop devices:
// Set button background tint programmatically so it is compatible with pre-lollipop devices.
public static void setButtonBackgroundTintAppCompat(Button button, ColorStateList colorStateList){
Drawable d = button.getBackground();
if (button instanceof AppCompatButton) {
// appcompat button replaces tint of its drawable background
((AppCompatButton)button).setSupportBackgroundTintList(colorStateList);
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
// Lollipop button replaces tint of its drawable background
// however it is not equal to d.setTintList(c)
button.setBackgroundTintList(colorStateList);
} else {
// this should only happen if
// * manually creating a Button instead of AppCompatButton
// * LayoutInflater did not translate a Button to AppCompatButton
d = DrawableCompat.wrap(d);
DrawableCompat.setTintList(d, colorStateList);
button.setBackgroundDrawable(d);
}
}
How to use in code:
Utility.setButtonBackgroundTintAppCompat(myButton,
ContextCompat.getColorStateList(mContext, R.color.your_custom_color));
This way, you do not have to specify a ColorStateList if you just want to change the background tint and nothing more but maintain the pretty button effects and what not.
I use this. Ripple effect and button click shadow working.
style.xml
<style name="Button.Red" parent="Widget.AppCompat.Button.Colored">
<item name="android:textColor">@color/material_white</item>
<item name="android:backgroundTint">@color/red</item>
</style>
Button on layout:
<Button
style="@style/Button.Red"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/close"/>
If you want to use AppCompat style like Widget.AppCompat.Button , Base.Widget.AppCompat.Button.Colored, etc., you need to use these styles with compatible views from support library.
Code below does not work for pre-lolipop devices:
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:theme="@style/Widget.AppCompat.Button" />
You need to use AppCompatButton to enabled AppCompat styles:
<android.support.v7.widget.AppCompatButton
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:theme="@style/Widget.AppCompat.Button" />
With latest Support Library you could just inherit you activity from AppCompatActivity
, so it will inflate your Button
as AppCompatButton
and give you an opportunity to style color of every single button on the layout with using of android:theme="@style/SomeButtonStyle"
, where SomeButtonStyle
is:
<style name="SomeButtonStyle" parent="@android:style/Widget.Button">
<item name="colorButtonNormal">@color/example_color</item>
</style>
Worked for me in 2.3.7, 4.4.1, 5.0.2
In Android Support Library 22.1.0, Google made the Button
tint aware.
So, another way to customise the background color of button is to use the backgroundTint
attribute.
For example,
<Button
android:id="@+id/add_remove_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:backgroundTint="@color/bg_remove_btn_default"
android:textColor="@android:color/white"
tools:text="Remove" />
This has been enhanced in v23.0.0 of AppCompat library with the addition of more themes including
Widget.AppCompat.Button.Colored
First of all include appCompat dependency if you haven't already
compile('com.android.support:appcompat-v7:23.0.0') {
exclude group: 'com.google.android', module: 'support-v4'
}
now since you need to use v23 of the app compat, you'll need to target SDK-v23 as well!
compileSdkVersion = 23
targetSdkVersion = 23
In your values/theme
<item name="android:buttonStyle">@style/BrandButtonStyle</item>
In your values/style
<style name="BrandButtonStyle" parent="Widget.AppCompat.Button.Colored">
<item name="colorButtonNormal">@color/yourButtonColor</item>
<item name="android:textColor">@color/White</item>
</style>
In your values-v21/style
<style name="BrandButtonStyle" parent="Widget.AppCompat.Button.Colored">
<item name="android:colorButtonNormal">@color/yourButtonColor</item>
<item name="android:textColor">@color/White</item>
</style>
Since your button theme is based on Widget.AppCompat.Button.Colored
The text color on the button is by default white!
but it seems there is an issue when you disable the button, the button will change its color to light grey, but the text color will remain white!
a workaround for this is to specifically set the text color on the button to white! as I have done in the style shown above.
now you can simply define your button and let AppCompat do the rest :)
<Button
android:layout_width="200dp"
android:layout_height="48dp" />
Edit:
To add <Button android:theme="@style/BrandButtonStyle"/>
I actually didn't want a change to my custom button styles but unfortunately they weren't working anymore.
My App has a minSdkVersion of 9 and everything worked before.
I don't know why but since I removed the android: before the buttonStyle it seems to work again
now = working:
<item name="buttonStyle">@style/ButtonmyTime</item>
before = just gray material buttons:
<item name="android:buttonStyle">@style/ButtonmyTime</item>
I have no spezial folder for newver android version since my buttons are quite flat and they should look the same across all android versions.
Maybe someone can tell me why I had to remove the "android:" The ImageButton is still working with "android:"
<item name="android:imageButtonStyle">@style/ImageButtonmyTimeGreen</item>
Appcompat library started to support material buttons after I posted the original response. In this post you can see the easiest implementation of raised and flat buttons.
Since that AppCompat doesn't support the button yet you can use xml as backgrounds. For doing that I had a look at the source code of the Android and found the related files for styling material buttons.
Have a look at the btn_default_material.xml on android source code.
You can copy the file into your projects drawable-v21 folder. But don't touch the color attr here. The file you need to change is the second file.
drawable-v21/custom_btn.xml
<ripple xmlns:android="http://schemas.android.com/apk/res/android"
android:color="?attr/colorControlHighlight">
<item android:drawable="@drawable/btn_default_mtrl_shape" />
</ripple>
As you realise there is a shape used inside this drawable which you can find in this file of the source code.
<inset xmlns:android="http://schemas.android.com/apk/res/android"
android:insetLeft="@dimen/button_inset_horizontal_material"
android:insetTop="@dimen/button_inset_vertical_material"
android:insetRight="@dimen/button_inset_horizontal_material"
android:insetBottom="@dimen/button_inset_vertical_material">
<shape android:shape="rectangle">
<corners android:radius="@dimen/control_corner_material" />
<solid android:color="?attr/colorButtonNormal" />
<padding android:left="@dimen/button_padding_horizontal_material"
android:top="@dimen/button_padding_vertical_material"
android:right="@dimen/button_padding_horizontal_material"
android:bottom="@dimen/button_padding_vertical_material" />
</shape>
And in this file you there are some dimensions used from the file that you can find here. You can copy the whole file and put into your values folder. This is important for applying the same size (that is used in material buttons) to all buttons
For older versions you should have another drawable with the same name. I am directly putting the items inline instead of referencing. You may want to reference them. But again, the most important thing is the original dimensions of the material button.
drawable/custom_btn.xml
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<!-- pressed state -->
<item android:state_pressed="true">
<inset xmlns:android="http://schemas.android.com/apk/res/android"
android:insetLeft="@dimen/button_inset_horizontal_material"
android:insetTop="@dimen/button_inset_vertical_material"
android:insetRight="@dimen/button_inset_horizontal_material"
android:insetBottom="@dimen/button_inset_vertical_material">
<shape android:shape="rectangle">
<corners android:radius="@dimen/control_corner_material" />
<solid android:color="@color/PRESSED_STATE_COLOR" />
<padding android:left="@dimen/button_padding_horizontal_material"
android:top="@dimen/button_padding_vertical_material"
android:right="@dimen/button_padding_horizontal_material"
android:bottom="@dimen/button_padding_vertical_material" />
</shape>
</inset>
</item>
<!-- focused state -->
<item android:state_focused="true">
<inset xmlns:android="http://schemas.android.com/apk/res/android"
android:insetLeft="@dimen/button_inset_horizontal_material"
android:insetTop="@dimen/button_inset_vertical_material"
android:insetRight="@dimen/button_inset_horizontal_material"
android:insetBottom="@dimen/button_inset_vertical_material">
<shape android:shape="rectangle">
<corners android:radius="@dimen/control_corner_material" />
<solid android:color="@color/FOCUSED_STATE_COLOR" />
<padding android:left="@dimen/button_padding_horizontal_material"
android:top="@dimen/button_padding_vertical_material"
android:right="@dimen/button_padding_horizontal_material"
android:bottom="@dimen/button_padding_vertical_material" />
</shape>
</inset>
</item>
<!-- normal state -->
<item>
<inset xmlns:android="http://schemas.android.com/apk/res/android"
android:insetLeft="@dimen/button_inset_horizontal_material"
android:insetTop="@dimen/button_inset_vertical_material"
android:insetRight="@dimen/button_inset_horizontal_material"
android:insetBottom="@dimen/button_inset_vertical_material">
<shape android:shape="rectangle">
<corners android:radius="@dimen/control_corner_material" />
<solid android:color="@color/NORMAL_STATE_COLOR" />
<padding android:left="@dimen/button_padding_horizontal_material"
android:top="@dimen/button_padding_vertical_material"
android:right="@dimen/button_padding_horizontal_material"
android:bottom="@dimen/button_padding_vertical_material" />
</shape>
</inset>
</item>
</selector>
Your button will have ripple effect on Lollipop devices. The old versions will have exactly same button except the ripple effect. But since that you provide drawables for different states, they'll also respond to touch events (as the old way).
I set android:textColor
to @null
in my button theme and it helps.
styles.xml
<style name="Button.Base.Borderless" parent="Widget.AppCompat.Button.Borderless.Colored">
<item name="android:textColor">@null</item>
</style>
some_layout.xml
<Button
style="@style/Button.Base.Borderless"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/hint" />
Now button text color is colorAccent
defined in AppTheme
<style name="AppTheme" parent="@style/Theme.AppCompat.Light.NoActionBar">
<item name="colorAccent">@color/colorAccent</item>
<item name="borderlessButtonStyle">@style/Button.Base.Borderless</item>
<item name="alertDialogTheme">@style/AlertDialog</item>
</style>
If you use style solution with colorButtonNormal, don't forget to inherit from Widget.AppCompat.Button.Colored so the ripple effect is working ;)
Like
<style name="CustomButtonStyle" parent="Widget.AppCompat.Button.Colored">
<item name="colorButtonNormal">@android:color/white</item>
</style>
this work for me with appcompat-v7:22.2.0 in android + 4.0
in your styles.xml
<style name="Button.Tinted" parent="Widget.AppCompat.Button">
<item name="colorButtonNormal">YOUR_TINT_COLOR</item>
<item name="colorControlHighlight">@color/colorAccent</item>
<item name="android:textColor">@android:color/white</item>
</style>
in your layout file
<Button
android:id="@+id/but_next"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/but_continue"
android:theme="@style/Button.Tinted" />
After 2 days looking for answers, the button theming didn't work for me in API < 21.
My only solution is to override AppCompatButton tinting not only with the base app theme "colorButtonNormal" but also the view backgroundTint like this :
public class AppCompatColorButton extends AppCompatButton {
public AppCompatColorButton(Context context) {
this(context, null);
}
public AppCompatColorButton(Context context, AttributeSet attrs) {
this(context, attrs, android.support.v7.appcompat.R.attr.buttonStyle);
}
public AppCompatColorButton(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
if (TintManager.SHOULD_BE_USED) {
setSupportBackgroundTintList(createButtonColorStateList(getContext(), attrs, defStyleAttr));
}
}
static final int[] DISABLED_STATE_SET = new int[]{-android.R.attr.state_enabled};
static final int[] FOCUSED_STATE_SET = new int[]{android.R.attr.state_focused};
static final int[] PRESSED_STATE_SET = new int[]{android.R.attr.state_pressed};
static final int[] EMPTY_STATE_SET = new int[0];
private ColorStateList createButtonColorStateList(Context context, AttributeSet attrs, int defStyleAttr) {
final int[][] states = new int[4][];
final int[] colors = new int[4];
int i = 0;
final int themeColorButtonNormal = ThemeUtils.getThemeAttrColor(context, android.support.v7.appcompat.R.attr.colorButtonNormal);
/*TypedArray a = context.obtainStyledAttributes(attrs, new int[] { android.R.attr.backgroundTint }, defStyleAttr, 0);
final int colorButtonNormal = a.getColor(0, themeColorButtonNormal);*/
TypedArray a = context.obtainStyledAttributes(attrs, android.support.v7.appcompat.R.styleable.View, defStyleAttr, 0);
final int colorButtonNormal = a.getColor(android.support.v7.appcompat.R.styleable.View_backgroundTint, themeColorButtonNormal);
a.recycle();
final int colorControlHighlight = ThemeUtils.getThemeAttrColor(context, android.support.v7.appcompat.R.attr.colorControlHighlight);
// Disabled state
states[i] = DISABLED_STATE_SET;
colors[i] = ThemeUtils.getDisabledThemeAttrColor(context, android.support.v7.appcompat.R.attr.colorButtonNormal);
i++;
states[i] = PRESSED_STATE_SET;
colors[i] = ColorUtils.compositeColors(colorControlHighlight, colorButtonNormal);
i++;
states[i] = FOCUSED_STATE_SET;
colors[i] = ColorUtils.compositeColors(colorControlHighlight, colorButtonNormal);
i++;
// Default enabled state
states[i] = EMPTY_STATE_SET;
colors[i] = colorButtonNormal;
i++;
return new ColorStateList(states, colors);
}
}
You can then define your Button color like this :
<com.example.views.AppCompatColorButton
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:backgroundTint="#ffff0000"
app:backgroundTint="#ffff0000"
android:text="Button"
android:textColor="@android:color/white" />
For me, the problem was that in Android 5.0 the android:colorButtonNormal
had no effect, and actually, no item from the theme (like android:colorAccent
), but in Android 4.4.3, for example, did. The project was configured with compileSdkVersion
and targetSdkVersion
to 22, so I've made all the changes as @Muhammad Alfaifi sugessted, but in the end, I've noticed that the problem was the buildToolsVersion, that wasn't updated. Once I changed to 23.0.1, everything start working almost as normal. Now, the android:colorButtonNormal
still has no effect, but at least the button reacts to android:colorAccent
, which for me is acceptable.
I hope that this hint could help someone.
Note: I've applied the style directly to the button, since the button's android:theme=[...]
also didn't have effect.
For those using an ImageButton
here is how you do it:
In style.xml:
<style name="BlueImageButton" parent="Base.Widget.AppCompat.ImageButton">
<item name="colorButtonNormal">@color/primary</item>
<item name="android:tint">@color/white</item>
</style>
in v21/style.xml:
<style name="BlueImageButton" parent="Widget.AppCompat.ImageButton">
<item name="android:colorButtonNormal">@color/primary</item>
<item name="android:tint">@color/white</item>
</style>
Then in your layout file:
<android.support.v7.widget.AppCompatImageButton
android:id="@+id/my_button"
android:theme="@style/BlueImageButton"
android:layout_width="42dp"
android:layout_height="42dp"
android:layout_gravity="center_vertical"
android:src="@drawable/ic_check_black_24dp"
/>
Layout:
<android.support.v7.widget.AppCompatButton
style="@style/MyButton"
...
/>
styles.xml:
<style name="MyButton" parent="Widget.AppCompat.Button.Colored">
<item name="backgroundTint">@color/button_background_selector</item>
<item name="android:textColor">@color/button_text_selector</item>
</style>
color/button_background_selector.xml:
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_enabled="false" android:color="#555555"/>
<item android:color="#00ff00"/>
</selector>
color/button_text_selector.xml:
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_enabled="false" android:color="#888888"/>
<item android:color="#ffffff"/>
</selector>
The answer is in THEME not style
The problem is that the Button color is stick to the colorButtonNormal of the theme. I have tried to change the Style in many different ways with no luck. So I changed the button theme.
Create a theme with colorButtonNormal and colorPrimary:
<style name="ThemeAwesomeButtonColor" parent="AppTheme">
<item name="colorPrimary">@color/awesomePrimaryColor</item>
<item name="colorButtonNormal">@color/awesomeButtonColor</item>
</style>
Use this theme in button
<Button
android:id="@+id/btn_awesome"
style="@style/AppTheme.Button"
android:theme="@style/ThemeAwesomeButtonColor"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/btn_awesome"/>
The "AppTheme.Button" can be any thing extends Button style like here I use primary color for text color:
<style name="AppTheme.Button" parent="Base.Widget.AppCompat.Button">
...
<item name="android:textColor">?attr/colorPrimary</item>
...
</style>
And you get the button in any color you want that compatible to material design.
To change the color of a single button
ViewCompat.setBackgroundTintList(button, getResources().getColorStateList(R.color.colorId));
if you use kotlin, can you see All attributes from MaterialButton are supported. Do not use the android:background attribute. MaterialButton manages its own background drawable, and setting a new background means Material button disabled color can no longer guarantee that the new attributes it introduces will function properly. If the default background is changed, Material Button cannot guarantee well-defined behavior. At MainActivity.kt
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
buttonClick.setOnClickListener {
(it as MaterialButton).apply {
backgroundTintList = ColorStateList.valueOf(Color.YELLOW)
backgroundTintMode = PorterDuff.Mode.SRC_ATOP
}
}
}
}
At activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@+id/constraintLayout"
tools:context=".MainActivity">
<com.google.android.material.button.MaterialButton
android:id="@+id/buttonNormal"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="24dp"
android:text="Material Button Normal"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<com.google.android.material.button.MaterialButton
android:id="@+id/buttonTint"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:backgroundTint="#D3212D"
android:text="Material Button Background Red"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/buttonNormal" />
<com.google.android.material.button.MaterialButton
android:id="@+id/buttonTintMode"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:backgroundTint="#D3212D"
android:backgroundTintMode="multiply"
android:text="Tint Red + Mode Multiply"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/buttonTint" />
<com.google.android.material.button.MaterialButton
android:id="@+id/buttonClick"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="32dp"
android:text="Click To Change Background"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.498"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/buttonTintMode" />
</androidx.constraintlayout.widget.ConstraintLayout>
resource : Material button change color example
Another simple solution using the AppCompatButton
<android.support.v7.widget.AppCompatButton
android:layout_width="wrap_content"
android:layout_height="wrap_content"
style="@style/Widget.AppCompat.Button.Colored"
app:backgroundTint="@color/red"
android:text="UNINSTALL" />
Use:
android:backgroundTint="@color/customColor"
Or even :
android:background="@color/customColor"
and that will give custom color to the button.
Use design support library(23.2.0) and appcompatwidgets as below
In Android Support Library 22.1 :
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.
The full list of tint aware widgets:
AppCompatAutoCompleteTextView
AppCompatButton
AppCompatCheckBox
AppCompatCheckedTextView
AppCompatEditText
AppCompatMultiAutoCompleteTextView
AppCompatRadioButton
AppCompatRatingBar
AppCompatSpinner
AppCompatTextView
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
In my case, instead of using Button, I use androidx.appcompat.widget.AppCompatButton and it worked for me.
If you only want "Flat" material button, you can customize their background using selectableItemBackground attribute as explained here.
To support colored buttons use the latest AppCompat library (>23.2.1) with:
inflate - XML
AppCompat Widget:
android.support.v7.widget.AppCompatButton
AppCompat Style:
style="@style/Widget.AppCompat.Button.Colored"
NB! To set a custom color in xml: use the attr: app
instead of android
(use alt+enter
or declare xmlns:app="http://schemas.android.com/apk/res-auto"
to use app
)
app:backgroundTint="@color/your_custom_color"
Example:
<android.support.v7.widget.AppCompatButton
style="@style/Widget.AppCompat.Button.Colored"
app:backgroundTint="@color/your_custom_color"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Colored Button"/>
or set it programmatically - JAVA
ViewCompat.setBackgroundTintList(your_colored_button,
ContextCompat.getColorStateList(getContext(),R.color.your_custom_color));
Source: Stackoverflow.com