I need a UI element that will allow the user to expand/collapse (show/hide) a view that contains optional/advanced information.
I envision this as a text label (e.g. 'advanced options', 'more/less') plus an icon that toggles between open and close. I've got some icons that could serve the purpose, and the functionality of the element closely matches that of a ToggleButton, so I attempted to solve this problem with a styled ToggleButton.
This solution turned out to be super ugly (from the maintenance perspective). I'll add it as an answer below.
I'm hoping someone could suggest a better solution, or point out how my solution could be simplified.
BTW, animating the expanding/collapsing of the view is addressed well in this SO Q/A.
Thankfully, I've found a much simpler way.
Instead of using a ToggleButton (which involved 11 resource files to style it) I'm just using a TextView with a CompoundDrawable, and then managing the state myself.
This way involves a little more code, but saves a huge mess of resource files. It also makes it easier to handle multiple themes, e.g. here I set the resource ID's for the needed icons depending on the theme:
final MainActivity ma = (MainActivity) getActivity();
if (ma.isDarkTheme()) {
icon_expand = getResources().getIdentifier( "ic_action_expand","drawable", ma.getPackageName() );
icon_collapse = getResources().getIdentifier( "ic_action_collapse","drawable", ma.getPackageName() );
} else {
icon_expand = getResources().getIdentifier( "ic_action_expand_light","drawable", ma.getPackageName() );
icon_collapse = getResources().getIdentifier( "ic_action_collapse_light","drawable", ma.getPackageName() );
}
I do this in my fragment's onCreate. It is unfortunate that I have to resolve the icon ID's at runtime, but that seems to be how it is done.
I have no idea why my icons are displayed at normal size in this case and at double size when I specified them as a layer of the background of the ToggleButton (which necessitating ScaledDrawable resources for all of my icons).
Here is my onClickListener (for the TextView):
protected OnClickListener expandCtrlListener = new OnClickListener() {
public void onClick(View v) {
assertTrue( v == expandCtrl );
isExpanded = !isExpanded;
advancedView.setVisibility( isExpanded ? View.VISIBLE : View.GONE );
expandCtrl.setCompoundDrawablesWithIntrinsicBounds( isExpanded ? icon_collapse : icon_expand, 0, 0, 0 );
}
};
Note that none of this shows the animation of the expand/collapse. That is addressed in the article I mention at the end of the question.
As I wrote in the question, the functionality of the button closely matches ToggleButton so I started with a layout that shows a styled ToggleButton next to some text:
<RelativeLayout
android:id="#+id/expandLayout"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="right"
android:layout_marginRight="12dp" >
<ToggleButton
android:id="#+id/expcollButton"
style="#style/expCollToggleBtn"
android:layout_centerVertical="true"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:checked="false"/>
<TextView
android:id="#+id/expandText"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:layout_marginLeft="8dp"
android:layout_toRightOf="#id/expcollButton"
android:text="Show Options" />
</RelativeLayout>
The style (in styles.xml file) looks like this. This nulls out the button text and points to the background drawable.
<style name="expCollToggleBtn">
<item name="android:background">#drawable/expcoll_btn_toggle_bg_light</item>
<item name="android:textOn"></item>
<item name="android:textOff"></item>
<item name="android:disabledAlpha">?android:attr/disabledAlpha</item>
</style>
The background drawable is a layer-list xml file that points to another drawable (perhaps there is some way to get rid of this file and just point directly to the selector below?)
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item android:id="#+android:id/background" android:drawable="#android:color/transparent" />
<item android:id="#+android:id/toggle" android:drawable="#drawable/expcoll_btn_toggle" />
</layer-list>
The selector lists the images for open/closed:
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_checked="false" android:drawable="#drawable/ic_action_expand" />
<item android:state_checked="true" android:drawable="#drawable/ic_action_collapse" />
</selector>
Now, the first complication is that this results in a very oversized button/image. I've provided suitable images for all pixel densities, so I'm not sure why this is happening, but the solution is to scale the images.
Unfortunately, this seems to require a ScaledDrawable for every image. I actually have four images because I foolishly decided to support light and dark themes in my app. So I need four of these.
<scale xmlns:android="http://schemas.android.com/apk/res/android"
android:drawable="#drawable/ic_action_expand"
android:scaleGravity="center"
android:scaleHeight="50%"
android:scaleWidth="50%"
/>
Also, note that there is a bug in ScaledDrawables, described here, so that needs to be accounted for.
And finally, since I'm trying to support two themes I need multiple copies of the selector and the layer-list files too, and need to add attributes to the attrs.xml file. At this point, I've got 11 XML files involved (plus the 10 image files for all the densities) all for a pretty ordinary button.
This can't be right!
A simple Expand Collapse Code
//show/hide boxes panel
var expansion: Boolean = false
expand_up.setOnClickListener {
if (!expansion) {
expand_up.setImageResource(R.drawable.collapse)
boxes_panel.visibility = View.VISIBLE
expansion = true
}
else{
expand_up.setImageResource(R.drawable.expand)
boxes_panel.visibility = View.GONE
expansion = false
}
}
There is a library for this.
It is called SAExpandableButton, you can find it here
Related
How to create an ImageButton without border (just the image should be visible)? One could achieve this by setting imageButton.setBackgroundDrawable(null), but this also removes the focus and selection colors.
The goal is that initially only the image without borders is visible. But when the user focuses/touches/clicks the image this should be indicated by hightlighting the image like regular buttons.
Solution in Java-Code for API 14 is preferred. Thank you!
As has been mentioned, the borderlessButtonStyle built into the default themes on API11 and above is the simplest way to achieve this effect. You mentioned you are creating your buttons in Java code instead of XML, so there are two options depending on how you need to apply the style.
Option #1: Add it to the theme
If all the Button or ImageButton instances in your application (or at least within the Activity) need to have this style applied, add the styling to your theme instead:
<resources>
<style name="AppTheme" parent="android:Theme.Holo.Light">
<!-- Default style for ImageButtons -->
<item name="android:imageButtonStyle">?android:borderlessButtonStyle</item>
<!-- Default style for Buttons -->
<item name="android:buttonStyle">?android:borderlessButtonStyle</item>
</style>
</resources>
With this theme applied to your Application or Activity, you won't have to declare the style of each element, you can just declare them as
Button button = new Button(context);
ImageButton imageButton = new ImageButton(context);
And the styling will be pulled from the theme.
Option #2: Declare it in the constructor
If only a couple buttons need to be styled this way, you can pass the style attribute you want to apply directly to each view, like so:
Button button = new Button(context, null, android.R.attr.borderlessButtonStyle);
ImageButton imageButton = new ImageButton(context, null, android.R.attr.borderlessButtonStyle);
This version supplies a different default style attribute for the widget to use.
Use borderlessButtonStyle to ImageButton
<ImageButton
style="?android:borderlessButtonStyle"
android:layout_width="58dp"
android:layout_height="wrap_content"
android:contentDescription="Delete"
android:src="#android:drawable/ic_delete" />
Ref : Google I/O 2013 - Android Design for UI Developers
Use a selector for the background like this:
/res/drawable/my_selector.xml
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_pressed="true" android:drawable="#drawable/my_drawable" />
<item android:drawable="#android:color/transparent" />
</selector>
my_drawable is whatever drawable you want as your border.
Then your ImageButton
<ImageButton
android:layout_height="wrap_content"
android:layout_height="wrap_content"
android:background="#drawable/my_selector"
android:src="#drawable/your_bitmap" />
your_bitmap is your actual image.
Your answer is here in the Nick Butcher and Roman Nurik talk for Google I/O 2013 about android design for UI developers.
Min: 31:40:
https://www.youtube.com/watch?v=Jl3-lzlzOJI#t=31m40s
The only problem with this approach is that style="?android:borderlessButtonStyle" is available for API 11 and above so if you want the same behaviour on any API before the 11, then you will have to stick with selectors.
By the way I highly recommend you to watch the whole talk because it is really interesting.
You have to add
imageButton.setClickable(true);
imageButton.setFocusable(true);
And it will works...
That's the way in your xml file :
android:clickable="true"
android:focusable="true"
Hope this help
I hope this will help you. please give the background as transparent
<ImageButton
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="#drawable/facebookbuttonanimation"
android:background="#00000000"
/>
You can design different images for clicked/not clicked states and set them in the onTouchListener as shown in the selected answer for this SO post.
Then you can set the image back to the previous image on post longclick or click.
I have a list with items, and 2 colorliststates, one for the odds, one for the evens.
basically, it ressembles this :
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_pressed="false" android:color="#fff" />
<item android:state_pressed="true" android:color="#999" />
</selector>
How can I use apply the colorliststate to the items? in the layout, there is no notion of odd or even, so i can't. In the java code, I can't find a way of using the colorliststate once i load it :
getContext().getResources().getColorStateList(R.color.list_even);
Any idea?
You can use:
// you must place your file in the res/color folder
widget.setTextColor(getContext().getResources().getColor(R.color.list_even));
Or just put it in the textColor attribute if you are using XML:
<SomWidget
android:textColor="#color/list_even"/>
First define two different selector drawables. You can refer in the drawable xml to a color.xml or enter directly the color value. To use the color.xml is more fancy.
Second
I guess you have ListAdapter implementation. So in your getView(..) implementation you can set different background drawables for odd and even items. I guess you want to modify the background and not the text color because you posted an example referring to a pressed state.
Expressed in code, without having eclipse here and totally correct Syntax in mind:
if (position % 2 == 0) {
view.setBackGroundDrawable(R.drawable.selector1);
}
else {
view.setBackgroundDrawable(R.drawable.selector2);
}
Does my example match what you want?
I'm getting a view from the XML with the code below:
Button view = (Button) LayoutInflater.from(this).inflate(R.layout.section_button, null);
I would like to set a "style" for the button how can I do that in java since a want to use several style for each button I will use.
First of all, you don't need to use a layout inflater to create a simple Button. You can just use:
button = new Button(context);
If you want to style the button you have 2 choices: the simplest one is to just specify all the elements in code, like many of the other answers suggest:
button.setTextColor(Color.RED);
button.setTextSize(TypedValue.COMPLEX_UNIT_SP, 18);
The other option is to define the style in XML, and apply it to the button. In the general case, you can use a ContextThemeWrapper for this:
ContextThemeWrapper newContext = new ContextThemeWrapper(baseContext, R.style.MyStyle);
button = new Button(newContext);
To change the text-related attributes on a TextView (or its subclasses like Button) there is a special method:
button.setTextAppearance(R.style.MyTextStyle);
Or, if you need to support devices pre API-23 (Android 6.0)
button.setTextAppearance(context, R.style.MyTextStyle);
This method cannot be used to change all attributes; for example to change padding you need to use a ContextThemeWrapper. But for text color, size, etc. you can use setTextAppearance.
Generally you can't change styles programmatically; you can set the look of a screen, or part of a layout, or individual button in your XML layout using themes or styles. Themes can, however, be applied programmatically.
There is also such a thing as a StateListDrawable which lets you define different drawables for each state the your Button can be in, whether focused, selected, pressed, disabled and so on.
For example, to get your button to change colour when it's pressed, you could define an XML file called res/drawable/my_button.xml directory like this:
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:state_pressed="true"
android:drawable="#drawable/btn_pressed" />
<item
android:state_pressed="false"
android:drawable="#drawable/btn_normal" />
</selector>
You can then apply this selector to a Button by setting the property android:background="#drawable/my_button".
Yes, you can use for example in a button
Button b = new Button(this);
b.setBackgroundResource(R.drawable.selector_test);
You can do style attributes like so:
Button myButton = new Button(this, null,android.R.attr.buttonBarButtonStyle);
in place of:
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="#+id/btn"
style="?android:attr/buttonBarButtonStyle"
/>
If you are using the Support library, you could simply use
TextViewCompat.setTextAppearance(textView, R.style.AppTheme_TextStyle_ButtonDefault_Whatever);
for TextViews and Buttons. There are similar classes for the rest of Views :-)
Depending on what style attributes you'd like to change you may be able to use the Paris library:
Button view = (Button) LayoutInflater.from(this).inflate(R.layout.section_button, null);
Paris.style(view).apply(R.style.YourStyle);
Many attributes like background, padding, textSize, textColor, etc. are supported.
List of currently supported attributes
Installation instructions
Disclaimer: I authored the library.
The answer by #Dayerman and #h_rules is right.
To give an elaborated example with code,
In drawable folder, create an xml file called button_disabled.xml
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle" android:padding="10dp">
<solid android:color="#color/silver"/>
<corners
android:bottomRightRadius="20dp"
android:bottomLeftRadius="20dp"
android:topLeftRadius="20dp"
android:topRightRadius="20dp"/>
</shape>
Then in Java,
((Button) findViewById(R.id.my_button)).setEnabled(false);
((Button) findViewById(R.id.my_button)).setBackgroundResource(R.drawable.button_disabled);
This will set the button's property to disabled and sets the color to silver.
[The color is defined in color.xml as:
<resources>
<color name="silver">#C0C0C0</color>
</resources>
For anyone looking for a Material answer see this SO post: Coloring Buttons in Android with Material Design and AppCompat
I used a combination of this answer to set the default text color of the button to white for my button:
https://stackoverflow.com/a/32238489/3075340
Then this answer https://stackoverflow.com/a/34355919/3075340 to programmatically set the background color. The code for that is:
ViewCompat.setBackgroundTintList(your_colored_button,
ContextCompat.getColorStateList(getContext(),R.color.your_custom_color));
your_colored_button can be just a regular Button or a AppCompat button if you wish - I tested the above code with both types of buttons and it works.
EDIT: I found that pre-lollipop devices do not work with the above code. See this post on how to add support for pre-lollipop devices: https://stackoverflow.com/a/30277424/3075340
Basically do this:
Button b = (Button) findViewById(R.id.button);
ColorStateList c = ContextCompat.getColorStateList(mContext, R.color.your_custom_color;
Drawable d = b.getBackground();
if (b instanceof AppCompatButton) {
// appcompat button replaces tint of its drawable background
((AppCompatButton)b).setSupportBackgroundTintList(c);
} 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)
b.setBackgroundTintList(c);
} 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, c);
b.setBackgroundDrawable(d);
}
At runtime, you know what style you want your button to have. So beforehand, in xml in the layout folder, you can have all ready to go buttons with the styles you need. So in the layout folder, you might have a file named: button_style_1.xml. The contents of that file might look like:
<?xml version="1.0" encoding="utf-8"?>
<Button
android:id="#+id/styleOneButton"
style="#style/FirstStyle" />
If you are working with fragments, then in onCreateView you inflate that button, like:
Button firstStyleBtn = (Button) inflater.inflate(R.layout.button_style_1, container, false);
where container is the ViewGroup container associated with the onCreateView method you override when creating your fragment.
Need two more such buttons? You create them like this:
Button secondFirstStyleBtn = (Button) inflater.inflate(R.layout.button_style_1, container, false);
Button thirdFirstStyleBtn = (Button) inflater.inflate(R.layout.button_style_1, container, false);
You can customize those buttons:
secondFirstStyleBtn.setText("My Second");
thirdFirstStyleBtn.setText("My Third");
Then you add your customized, stylized buttons to the layout container you also inflated in the onCreateView method:
_stylizedButtonsContainer = (LinearLayout) rootView.findViewById(R.id.stylizedButtonsContainer);
_stylizedButtonsContainer.addView(firstStyleBtn);
_stylizedButtonsContainer.addView(secondFirstStyleBtn);
_stylizedButtonsContainer.addView(thirdFirstStyleBtn);
And that's how you can dynamically work with stylized buttons.
I made a helper interface for this using the holder pattern.
public interface StyleHolder<V extends View> {
void applyStyle(V view);
}
Now for every style you want to use pragmatically just implement the interface, for example:
public class ButtonStyleHolder implements StyleHolder<Button> {
private final Drawable background;
private final ColorStateList textColor;
private final int textSize;
public ButtonStyleHolder(Context context) {
TypedArray ta = context.obtainStyledAttributes(R.style.button, R.styleable.ButtonStyleHolder);
Resources resources = context.getResources();
background = ta.getDrawable(ta.getIndex(R.styleable.ButtonStyleHolder_android_background));
textColor = ta.getColorStateList(ta.getIndex(R.styleable.ButtonStyleHolder_android_textColor));
textSize = ta.getDimensionPixelSize(
ta.getIndex(R.styleable.ButtonStyleHolder_android_textSize),
resources.getDimensionPixelSize(R.dimen.standard_text_size)
);
// Don't forget to recycle!
ta.recycle();
}
#Override
public void applyStyle(Button btn) {
btn.setBackground(background);
btn.setTextColor(textColor);
btn.setTextSize(TypedValue.COMPLEX_UNIT_PX, textSize);
}
}
Declare a stylable in your attrs.xml, the styleable for this example is:
<declare-styleable name="ButtonStyleHolder">
<attr name="android:background" />
<attr name="android:textSize" />
<attr name="android:textColor" />
</declare-styleable>
Here is the style declared in styles.xml:
<style name="button">
<item name="android:background">#drawable/button</item>
<item name="android:textColor">#color/light_text_color</item>
<item name="android:textSize">#dimen/standard_text_size</item>
</style>
And finally the implementation of the style holder:
Button btn = new Button(context);
StyleHolder<Button> styleHolder = new ButtonStyleHolder(context);
styleHolder.applyStyle(btn);
I found this very helpful as it can be easily reused and keeps the code clean and verbose, i would recommend using this only as a local variable so we can allow the garbage collector to do its job once we're done with setting all the styles.
I faced the same problem recently. here is how i solved it.
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<!-- This is the special two colors background START , after this LinearLayout, you can add all view that have it for main background-->
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:weightSum="2"
android:background="#FFFFFF"
android:orientation="horizontal"
>
<View
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="1"
android:background="#0000FF" />
<View
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="1"
android:background="#F000F0" />
</LinearLayout>
<!-- This is the special two colors background END-->
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:gravity="center"
android:text="This Text is centered with a special backgound,
You can add as much elements as you want as child of this RelativeLayout"
android:textColor="#FFFFFF"
android:textSize="20sp" />
</RelativeLayout>
I used a LinearLayout with android:weightSum="2"
I gave to the two child elements android:layout_weight="1"
(I gave each 50% of the parent space(width & height))
And finally, i gave the two child element different background colors to have the final effect.
Thanks !
I'm getting a view from the XML with the code below:
Button view = (Button) LayoutInflater.from(this).inflate(R.layout.section_button, null);
I would like to set a "style" for the button how can I do that in java since a want to use several style for each button I will use.
First of all, you don't need to use a layout inflater to create a simple Button. You can just use:
button = new Button(context);
If you want to style the button you have 2 choices: the simplest one is to just specify all the elements in code, like many of the other answers suggest:
button.setTextColor(Color.RED);
button.setTextSize(TypedValue.COMPLEX_UNIT_SP, 18);
The other option is to define the style in XML, and apply it to the button. In the general case, you can use a ContextThemeWrapper for this:
ContextThemeWrapper newContext = new ContextThemeWrapper(baseContext, R.style.MyStyle);
button = new Button(newContext);
To change the text-related attributes on a TextView (or its subclasses like Button) there is a special method:
button.setTextAppearance(R.style.MyTextStyle);
Or, if you need to support devices pre API-23 (Android 6.0)
button.setTextAppearance(context, R.style.MyTextStyle);
This method cannot be used to change all attributes; for example to change padding you need to use a ContextThemeWrapper. But for text color, size, etc. you can use setTextAppearance.
Generally you can't change styles programmatically; you can set the look of a screen, or part of a layout, or individual button in your XML layout using themes or styles. Themes can, however, be applied programmatically.
There is also such a thing as a StateListDrawable which lets you define different drawables for each state the your Button can be in, whether focused, selected, pressed, disabled and so on.
For example, to get your button to change colour when it's pressed, you could define an XML file called res/drawable/my_button.xml directory like this:
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:state_pressed="true"
android:drawable="#drawable/btn_pressed" />
<item
android:state_pressed="false"
android:drawable="#drawable/btn_normal" />
</selector>
You can then apply this selector to a Button by setting the property android:background="#drawable/my_button".
Yes, you can use for example in a button
Button b = new Button(this);
b.setBackgroundResource(R.drawable.selector_test);
You can do style attributes like so:
Button myButton = new Button(this, null,android.R.attr.buttonBarButtonStyle);
in place of:
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="#+id/btn"
style="?android:attr/buttonBarButtonStyle"
/>
If you are using the Support library, you could simply use
TextViewCompat.setTextAppearance(textView, R.style.AppTheme_TextStyle_ButtonDefault_Whatever);
for TextViews and Buttons. There are similar classes for the rest of Views :-)
Depending on what style attributes you'd like to change you may be able to use the Paris library:
Button view = (Button) LayoutInflater.from(this).inflate(R.layout.section_button, null);
Paris.style(view).apply(R.style.YourStyle);
Many attributes like background, padding, textSize, textColor, etc. are supported.
List of currently supported attributes
Installation instructions
Disclaimer: I authored the library.
The answer by #Dayerman and #h_rules is right.
To give an elaborated example with code,
In drawable folder, create an xml file called button_disabled.xml
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle" android:padding="10dp">
<solid android:color="#color/silver"/>
<corners
android:bottomRightRadius="20dp"
android:bottomLeftRadius="20dp"
android:topLeftRadius="20dp"
android:topRightRadius="20dp"/>
</shape>
Then in Java,
((Button) findViewById(R.id.my_button)).setEnabled(false);
((Button) findViewById(R.id.my_button)).setBackgroundResource(R.drawable.button_disabled);
This will set the button's property to disabled and sets the color to silver.
[The color is defined in color.xml as:
<resources>
<color name="silver">#C0C0C0</color>
</resources>
For anyone looking for a Material answer see this SO post: Coloring Buttons in Android with Material Design and AppCompat
I used a combination of this answer to set the default text color of the button to white for my button:
https://stackoverflow.com/a/32238489/3075340
Then this answer https://stackoverflow.com/a/34355919/3075340 to programmatically set the background color. The code for that is:
ViewCompat.setBackgroundTintList(your_colored_button,
ContextCompat.getColorStateList(getContext(),R.color.your_custom_color));
your_colored_button can be just a regular Button or a AppCompat button if you wish - I tested the above code with both types of buttons and it works.
EDIT: I found that pre-lollipop devices do not work with the above code. See this post on how to add support for pre-lollipop devices: https://stackoverflow.com/a/30277424/3075340
Basically do this:
Button b = (Button) findViewById(R.id.button);
ColorStateList c = ContextCompat.getColorStateList(mContext, R.color.your_custom_color;
Drawable d = b.getBackground();
if (b instanceof AppCompatButton) {
// appcompat button replaces tint of its drawable background
((AppCompatButton)b).setSupportBackgroundTintList(c);
} 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)
b.setBackgroundTintList(c);
} 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, c);
b.setBackgroundDrawable(d);
}
At runtime, you know what style you want your button to have. So beforehand, in xml in the layout folder, you can have all ready to go buttons with the styles you need. So in the layout folder, you might have a file named: button_style_1.xml. The contents of that file might look like:
<?xml version="1.0" encoding="utf-8"?>
<Button
android:id="#+id/styleOneButton"
style="#style/FirstStyle" />
If you are working with fragments, then in onCreateView you inflate that button, like:
Button firstStyleBtn = (Button) inflater.inflate(R.layout.button_style_1, container, false);
where container is the ViewGroup container associated with the onCreateView method you override when creating your fragment.
Need two more such buttons? You create them like this:
Button secondFirstStyleBtn = (Button) inflater.inflate(R.layout.button_style_1, container, false);
Button thirdFirstStyleBtn = (Button) inflater.inflate(R.layout.button_style_1, container, false);
You can customize those buttons:
secondFirstStyleBtn.setText("My Second");
thirdFirstStyleBtn.setText("My Third");
Then you add your customized, stylized buttons to the layout container you also inflated in the onCreateView method:
_stylizedButtonsContainer = (LinearLayout) rootView.findViewById(R.id.stylizedButtonsContainer);
_stylizedButtonsContainer.addView(firstStyleBtn);
_stylizedButtonsContainer.addView(secondFirstStyleBtn);
_stylizedButtonsContainer.addView(thirdFirstStyleBtn);
And that's how you can dynamically work with stylized buttons.
I made a helper interface for this using the holder pattern.
public interface StyleHolder<V extends View> {
void applyStyle(V view);
}
Now for every style you want to use pragmatically just implement the interface, for example:
public class ButtonStyleHolder implements StyleHolder<Button> {
private final Drawable background;
private final ColorStateList textColor;
private final int textSize;
public ButtonStyleHolder(Context context) {
TypedArray ta = context.obtainStyledAttributes(R.style.button, R.styleable.ButtonStyleHolder);
Resources resources = context.getResources();
background = ta.getDrawable(ta.getIndex(R.styleable.ButtonStyleHolder_android_background));
textColor = ta.getColorStateList(ta.getIndex(R.styleable.ButtonStyleHolder_android_textColor));
textSize = ta.getDimensionPixelSize(
ta.getIndex(R.styleable.ButtonStyleHolder_android_textSize),
resources.getDimensionPixelSize(R.dimen.standard_text_size)
);
// Don't forget to recycle!
ta.recycle();
}
#Override
public void applyStyle(Button btn) {
btn.setBackground(background);
btn.setTextColor(textColor);
btn.setTextSize(TypedValue.COMPLEX_UNIT_PX, textSize);
}
}
Declare a stylable in your attrs.xml, the styleable for this example is:
<declare-styleable name="ButtonStyleHolder">
<attr name="android:background" />
<attr name="android:textSize" />
<attr name="android:textColor" />
</declare-styleable>
Here is the style declared in styles.xml:
<style name="button">
<item name="android:background">#drawable/button</item>
<item name="android:textColor">#color/light_text_color</item>
<item name="android:textSize">#dimen/standard_text_size</item>
</style>
And finally the implementation of the style holder:
Button btn = new Button(context);
StyleHolder<Button> styleHolder = new ButtonStyleHolder(context);
styleHolder.applyStyle(btn);
I found this very helpful as it can be easily reused and keeps the code clean and verbose, i would recommend using this only as a local variable so we can allow the garbage collector to do its job once we're done with setting all the styles.
I faced the same problem recently. here is how i solved it.
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<!-- This is the special two colors background START , after this LinearLayout, you can add all view that have it for main background-->
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:weightSum="2"
android:background="#FFFFFF"
android:orientation="horizontal"
>
<View
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="1"
android:background="#0000FF" />
<View
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="1"
android:background="#F000F0" />
</LinearLayout>
<!-- This is the special two colors background END-->
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:gravity="center"
android:text="This Text is centered with a special backgound,
You can add as much elements as you want as child of this RelativeLayout"
android:textColor="#FFFFFF"
android:textSize="20sp" />
</RelativeLayout>
I used a LinearLayout with android:weightSum="2"
I gave to the two child elements android:layout_weight="1"
(I gave each 50% of the parent space(width & height))
And finally, i gave the two child element different background colors to have the final effect.
Thanks !
I'm trying to detect the focus/pressed color for button and other elements.
This is needed because I'm developing new components and it's important that those look as part of platform.
Those colors are ORANGE on android sdk and GREEN on HTC SenseUI.
If I could detect that color my component will look as part of platform on both version.
Does anyone knows how to do this?
It's possible to create "selector" which uses custom image for default state and platform default for focus/selection.
To do this follow the steps:
1) create xml file with selector in "res/drawable" (e.g. "red_button.xml"):
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_pressed="true"
android:drawable="#android:drawable/btn_default" >
</item>
<item android:state_focused="true"
android:drawable="#android:drawable/btn_default" >
</item>
<item
android:drawable="#drawable/btn_default_red" >
</item>
</selector>
2) from folder ".../android-sdk-mac/platforms/android-1.5/data/res/drawable/" take picture "btn_default_pressed.9.png" and change color as you like (I needed to change it to red and for this GIMP is enough).
3) place altered picture in "res/drawable" (e.g. with name "btn_default_red.9.png")
4) define button:
<Button
android:id="#+id/info_button"
android:layout_width="wrap_content"
android:layout_height="37dip"
android:layout_marginTop="1dip"
android:background="#drawable/red_button"
android:text="[Info]" />
That's all.
This is result:
alt text http://img200.imageshack.us/img200/1349/custombutton.png
I had this problem too. As already stated, the problem is that the backgrounds aren't simple colors, they're Drawables that could take on all kinds of appearances. However, I found a work-around that may help. If your custom component looks something like an existing one, e.g. a Button or ListView entry, you can just steal their background/selector and set that as the background for your custom component. E.g., in your custom component constructor:
setBackgroundDrawable(new Button(context).getBackground());
or for a background more suitable for list-like components:
setBackgroundDrawable(new ListView(context).getSelector());
You may want to optimise that code somewhat, but you get the idea.
Those aren't colors. They are a few nine-patch images out of a StateListDrawable. I am skeptical that there will be a reliable way for you to determine what the color is, one that will work across all devices and all versions of Android.
This is pretty much a duplicate of: Android ListView Selector Color
Also, why do you need to detect the colours? Just do nothing and your widgets will fit in to the platform's existing look & feel.
Or if you're writing a custom theme, just make yours inherit from android:Theme.