I need to prevent seekbar from user inputs in special cases. If I use setEnabled(false) it becomes gray instead of white.
Is there any method to disable seekbar without dimming or set another drawable for progress in disabled seekbar ?
Yes. It is possible! But you need to override SeekBar's drawableStateChanged function, with something like this:
#Override
protected void drawableStateChanged() {
super.drawableStateChanged();
final Drawable progressDrawable = getProgressDrawable();
if(isEnabled() == false && progressDrawable != null) progressDrawable.setAlpha(255);
}
Actually I was very angry, when I saw hardcoded alpha value in AbsSeekBar:
mDisabledAlpha = a.getFloat(com.android.internal.R.styleable.Theme_disabledAlpha, 0.5f);
Because there is no function, which will turn off, or even change disabled alpha value for SeekBar. Just take a look at those lines of code in drawableStateChanged function:
if (progressDrawable != null) {
progressDrawable.setAlpha(isEnabled() ? NO_ALPHA : (int) (NO_ALPHA * mDisabledAlpha));
}
I'm not sure why you'd want to change this, and I don't believe it's good practice to override the visual queue for a user that something is disabled. If it looks active, but doesn't interact, I'm going to be mad at your app.
Regardless, to answer your question, you should look at StateListDrawable this question outlines it specifically for seek bars.
Better solution here....
seekBar.setOnTouchListener(new OnTouchListener(){
#Override
public boolean onTouch(View v, MotionEvent event) {
return true;
}
});
You can use setEnabled(false) and set the Theme_disabledAlpha attribute as #Ilya Pikin mentioned above like this:
<item name="android:disabledAlpha">1.0</item>
Android changes alpha value of color to 127 from 255 in disabled state. Just make sure you set it back to 255 after disabling seekbar.
seekBar.post(new Runnable(){
#Override
public void run()
{
seekBar.getProgressDrawable().setAlpha(255);
}
});
view.post is required only if you are unsure that seekbar is rendered yet or not, otherwise just
seekBar.getProgressDrawable().setAlpha(255);
is enough.
I´m a bit late to the party, but here is a solution to solve your question.
First of all I recommand to use com.google.android.material.slider.Slider nowadays. It is much more flexible than the old seekbar was. To do so, use the following import in your gradle file
implementation 'com.google.android.material:material:1.6.0' //1.4.0 or newer
Now create your layout file, let´s it call layout_slider
e.g.:
<?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="wrap_content"
android:background="#android:color/white">
<com.google.android.material.slider.Slider
android:id="#+id/sbSize"
style="#style/sliderTheme"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:value="25"
android:valueFrom="0.0"
android:valueTo="100.0"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
Now open your styles.xml file and create the sliderTheme. It should look like this:
<style name="sliderTheme" parent="Widget.MaterialComponents.Slider">
<item name="trackColorInactive">#color/slider_track_inactive</item>
<item name="thumbColor">#color/slider_thumb_color</item>
<item name="trackColorActive">#color/slider_track_active</item>
</style>
The next step we need to do is create a new Resource Directory named color inside the res folder. (Resource type is color)
Now create a file to change the trackColor for active and inactive state. Let´s name it slider_track_inactive.xml
Copy the following content inside that file:
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:alpha="0.24" android:color="?attr/colorPrimary" android:state_enabled="true"/>
<item android:alpha="0.12" android:color="?attr/colorOnSurface"/>
</selector>
the first value is for the active and the second is for the inactive color. Change alpha and color to a value to want. All my sample values are the Google default values.
Now create a file called slider_thumb_color.xml inside the color folder. It should look like this:
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:color="?attr/colorPrimary" android:state_enabled="true"/>
<item android:alpha="0.38" android:color="?attr/colorOnSurface"/>
</selector>
Again, change these values as you want.
Last, create a file called slider_track_active.xml inside the color folder. It should look like this:
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:color="?attr/colorPrimary" android:state_enabled="true"/>
<item android:alpha="0.32" android:color="?attr/colorOnSurface"/>
</selector>
Now you have customized all colors for the disabled slider. There are much more values you can customize with the same technique. All default keys you can customize inside your styles.xml file are:
<item name="haloColor">#color/material_slider_halo_color</item>
<item name="haloRadius">#dimen/mtrl_slider_halo_radius</item>
<item name="labelStyle">#style/Widget.MaterialComponents.Tooltip</item>
<item name="thumbColor">#color/material_slider_thumb_color</item>
<item name="thumbElevation">#dimen/mtrl_slider_thumb_elevation</item>
<item name="thumbRadius">#dimen/mtrl_slider_thumb_radius</item>
<item name="tickColorActive">#color/material_slider_active_tick_marks_color</item>
<item name="tickColorInactive">#color/material_slider_inactive_tick_marks_color</item>
<item name="trackColorActive">#color/material_slider_active_track_color</item>
<item name="trackColorInactive">#color/material_slider_inactive_track_color</item>
<item name="trackHeight">#dimen/mtrl_slider_track_height</item>
<item name="minSeparation">0dp</item>
Related
I haven't been able to find quite what I am trying to do on SO, but I feel like it should be such a common interface need that there must be a straightforward way of accomplishing this that I'm missing.
In my style.xml I have two button styles, a standard "active" button and an "inactive" button.
<style name="ButtonStandard">
<item name="android:background">#color/colorGreen</item>
<item name="android:textColor">#android:color/white</item>
<item name="android:padding">#dimen/element_padding</item>
</style>
<style name="ButtonInactive">
<item name="android:background">#color/colorLight</item>
<item name="android:textColor">#android:color/black</item>
<item name="android:padding">#dimen/element_padding</item>
</style>
I am setting one button to ButtonStandard and the other to ButtonInactive. When I click the inactive button, I want to change it to use the ButtonStandard type and vice versa. I don't want to programmatically set the styles individually in case I decide to later change the button styles and it would be unreliable to have to change it in every location.
activeButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
activeButton.[somehowsetstyle](R.style.ButtonInactive);
inactiveButton.[somehowsetstyle](R.style.ButtonStandard);
}
});
How can I change between these styles when users click on buttons? The most important is to not have to set specific styles within the code which is just a last resort imho.
Thanks!
Solution Notes
Generally I followed the solution below but instead I created the selector as a drawable and used android:drawable instead because it seems the button background needs that, even if just specifying a color. I also used state_activated rather than enabled so that it is only changing the look of the button and doesn't prevent clicks.
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_activated="false"
android:drawable="#color/colorPrimaryDark" />
<item android:state_activated="true"
android:drawable="#color/colorGreen" />
<item android:drawable="#color/colorGreen" />
In XML
android:background="#drawable/selector_btn_bkg"
android:state_activated="false"
In Java
myButton.setActivated(true);
What you are looking for is ColorStateList
drawable/my_selector.xml
<selector xmlns:android="http://schemas.android.com/apk/res/android" >
<item android:color="#color/enabled_color"/>
<item android:color="#color/enabled_color"
android:state_enabled = "true"/>
<item android:color="#color/disbaled_color"
android:state_enabled = "false"/>
</selector>
my_view.xml
...
<Button
android:id="#+id/my_button"
android:enabled="false"
android:background="#drawable/my_selector"/>
Java code
onClick(View v){
myButton.setEnabled(true);
}
I'm trying to achieve that an icon in ActionBar will not change states discretely, but by fading animation. When I add android:enterFadeDuration and android:exitFadeDuration to the selector tag, my drawable is initially invisible - when I tap it, it changes state to state_pressed (properly with enter fade duration) and when I release it, it turns back to its normal visible unselected state.
I must be missing something obvious, or is this a bug of some kind?
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android" android:enterFadeDuration="150" android:exitFadeDuration="150">
<item android:drawable="#drawable/filters_toggle_icon_selected" android:state_focused="true"/>
<item android:drawable="#drawable/filters_toggle_icon_selected" android:state_pressed="true"/>
<item android:drawable="#drawable/filters_toggle_icon" android:state_focused="false" android:state_pressed="false"/>
</selector>
I had a similar problem, with my code looking like this:
<selector xmlns:android="http://schemas.android.com/apk/res/android"
android:enterFadeDuration="#android:integer/config_mediumAnimTime"
android:exitFadeDuration="#android:integer/config_mediumAnimTime" >
<item android:state_pressed="true" android:drawable="#color/pressed" />
<item android:drawable="#color/default" />
</selector>
At first, I found a hint to get rid of enterFadeDuration and only use exitFadeDuration. That solved the problem with initial invisibility, but the view still faded into invisibility during the first interraction.
Then, I modified my structure as follows:
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="#color/default" />
<item>
<selector android:enterFadeDuration="#android:integer/config_mediumAnimTime"
android:exitFadeDuration="#android:integer/config_mediumAnimTime" >
<item android:state_pressed="true" android:drawable="#color/pressed" />
</selector>
</item>
</layer-list>
Basically, I just pushed the default drawable out of the selector. It's a workaround and it also works for selectors with multiple states, but has some notable limitations:
The default drawable is always visible as a bottom layer. It works for opaque colors, but transparency may cause undesirable results.
If the view starts in one of the states tested by selector, in still displays as default, because the selector still starts as invisible.
It might not be applicable to the original problem, but it's something to consider for overcoming this behaviour of selectors.
Use android:enterFadeDuration="#android:integer/config_mediumAnimTime" and android:exitFadeDuration="#android:integer/config_mediumAnimTime".
My problem was similar as well, the issue was that after setting a background drawable on my view, it was in the wrong state (sometimes it even mixed the stroke and solid of two states...). This was only before the first interaction, like receiving focus or changing enabled state.
I've found that if you call jumpToCurrentState() on the drawable (which has the fade duration properties) after setting it on the view, it will be set on the correct state, and you can keep on using the enter / exit fade duration properties.
Here's how I did it:
val stateList = (darkBackground as? RippleDrawable)?.findDrawableByLayerId(android.R.id.background) as? StateListDrawable
background = darkBackground
stateList?.jumpToCurrentState()
In this example I had a ripple drawable which contained the selector which had the fade properties (I had to add an ID to the selector so I could look it up using the ID).
This seems to be a bug that happens on specific Android versions. You can turn off the android:enterFadeDuration programmatically in Java code, by accessing the Selector with a StateListDrawable:
// Disable android:enterFadeDuration/exitFadeDuration on Android 4.2 only
if (Build.VERSION.SDK_INT == Build.VERSION_CODES.JELLY_BEAN_MR1) {
StateListDrawable stateListDrawable =
(StateListDrawable) animatedButton.getBackground();
stateListDrawable.setEnterFadeDuration(0);
stateListDrawable.setExitFadeDuration(0);
}
I am using drawables to nicely style my buttons, and that works fine, except for the text color in the button.
I have defined a state_enabled="false" item in a selector and using setEnabled gives me the right button styles, but I have to jump through quite some loops to get the text color different. This code for example doesn't work (it shows no, or black, text when disabled, and darkgray when enabled):
public void setButtonsEnabled(boolean enable) {
btnAccept.setEnabled(enable);
btnDecline.setEnabled(enable);
int color = R.color.White;
if (!enable) {
color = R.color.DarkGray;
}
btnAccept.setTextColor(color);
btnDecline.setTextColor(color);
}
I found the solution.
The key lies in also setting the TextColor to a selector in res/colors:
android:textColor="#color/button_text"
android:background="#drawable/button_selector"
For the background selector I used this:
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_enabled="true" android:state_pressed="false" android:drawable="#drawable/btn_buddy_enabled"></item>
<item android:state_enabled="false" android:drawable="#drawable/btn_buddy_disabled"></item>
<item android:state_enabled="true" android:state_pressed="true" android:drawable="#drawable/btn_buddy_clicked"></item>
</selector>
And the textColor selector is this:
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_enabled="true" android:state_pressed="false" android:color="#color/White"></item>
<item android:state_enabled="false" android:color="#color/Gray"></item>
<item android:state_enabled="true" android:state_pressed="true" android:color="#color/White"></item>
</selector>
Simply calling setEnabled() will make everything work fine.
You are using the wrong value for the color. R.color.White returns the resource ID of the value, not the value itself. Try Color.WHITE, or getResources().getColor(R.color.White)
Have you checked out ColorStateLists? They are pretty awesome. So basically apply all those ideas of Drawable selectors to a set of colors.
Make a folder called [Your Project]/res/colors/ and then put an xml file in there called, button_color.xml (or whatever).
button_color.xml
<?xml version="1.0" encoding="utf-8"?>
<selector
xmlns:android="http://schemas.android.com/apk/res/android"
>
<!-- Any Enabled button, gets White Text -->
<item
android:color="#color/White"
android:state_enabled="true" />
<!-- Buttons with any other state, get DarkGray Text -->
<item
android:color="#color/DarkGray"/>
</selector>
And then for your TextView, you can just do something like, mTextView.setTextColor(R.color.button_color); At that point there is no need for that if/else type of logic, the selector will do it for you. The selector gets rolled up into the color resource but the class it actually generates is called a ColorStateList in case you find it referenced in other documentation.
There's a question for the same functionality on Blackberry, and a few different threads referred to this bug (which has since been closed without resolution as far as I can tell), but I haven't found one specifically for Android.
I'm calling setEnabled(false) on certain MenuItems based on some state, but they visually look the same. I'd like them to be offset in some way, so that the user knows that the option currently isn't available -- is there any way to do that?
On all android versions, easiest way to use this to SHOW a menu action icon as disabled AND make it FUNCTION as disabled as well:
#Override
public boolean onPrepareOptionsMenu(Menu menu) {
MenuItem item = menu.findItem(R.id.menu_my_item);
if (myItemShouldBeEnabled) {
item.setEnabled(true);
item.getIcon().setAlpha(255);
} else {
// disabled
item.setEnabled(false);
item.getIcon().setAlpha(130);
}
}
I had the same issue. There are two ways of getting this to work:
Put your icons in a StateList so that a different icon will be used on disable
What I use now. Change the icon yourself with something like this in onPrepareOptionsMenu():
public boolean onPrepareOptionsMenu(Menu menu) {
boolean menusEnabled = reachedEndOfSlidehow(); // enable or disable?
MenuItem item = menu.findItem(R.id.menu_next_slide);
Drawable resIcon = getResources().getDrawable(R.drawable.ic_next_slide);
if (!menusEnabled)
resIcon.mutate().setColorFilter(Color.GRAY, PorterDuff.Mode.SRC_IN);
item.setEnabled(menusEnabled); // any text will be automatically disabled
item.setIcon(resIcon);
}
You can call invalidateOptionsMenu() (or from ABS, supportInvalidateOptionsMenu()) to rebuild the menu.
EDIT: Updated solution 2
Source: https://groups.google.com/forum/?fromgroups#!topic/actionbarsherlock/Z8Ic8djq-3o
I found a new way to solve this issue using a drawable selector xml file. You just create a selector with the icon you want to use in your menu item, then you can either change the tint, alpha or both of the bitmap:
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_enabled="true">
<bitmap android:src="#drawable/ic_menu_item"
android:tint="#color/enabled_color"
android:alpha="#integer/enabled_alpha"/>
</item>
<item android:state_enabled="false">
<bitmap android:src="#drawable/ic_menu_item"
android:tint="#color/disabled_color"
android:alpha="#integer/disabled_alpha"/>
</item>
</selector>
As a side note; I like to set the tint to "?android:attr/textColorPrimary" for enabled state and "?android:attr/textColorHint" for disabled state. This way it will adjust depending on the theme used.
Then you can just set the icon in your menu xml file to the selector resource:
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<item android:id="#+id/menu_action"
android:orderInCategory="0"
android:title="#string/title_menu_action"
android:icon="#drawable/ic_menu_item_selector"
app:showAsAction="ifRoom"/>
</menu>
Then when you call item.setEnabled(enabled) the color and/or alpha of the icon will change along with the state!
The way I did it is by using "itemIconTint" in NavigationView, you can also grey out the text by using "itemTextColor"
This is Navigationview:
<android.support.design.widget.NavigationView
android:id="#+id/nav_view"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_gravity="start"
app:itemBackground="#color/white"
android:background="#color/white"
app:itemTextColor="#color/menu_text_color"
app:itemIconTint="#color/menu_text_color"
app:menu="#menu/main_drawer" />
and the "#color/menu_text_color" is a selector:
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_checked="true" android:color="#color/primaryColor" />
<item android:state_enabled="false" android:color="#color/disabled_text_color" />
<item android:color="#color/primaryText" />
</selector>
Finally, if you want to disable a menuitem,
MenuItem item = mNavigationView.getMenu().findItem(R.id.your_menu_item);
item.setEnabled(isEnable);
Done!
I was having difficulty with this on modern android with MaterialComponents theme. My problem was I had set <item name="actionMenuTextColor">#color/blue</item> in styles.xml and this overrides the text color whether the item is enabled or disabled. The solution is to set a Color state list and not a color directly.
My styles attribute now looks like:
<item name="actionMenuTextColor">#color/menu_color_selector</item>
I had an issue where neither my the text nor the icon was visibly changing. The other answers either didn't work for me or weren't very elegant. Here's an answer that works for the latest Material recommendations.
You should be able to simply call menu.findItem(R.id.menu_my_item).isEnabled = false in onPrepareOptionsMenu(menu: Menu).
(If you need onPrepareOptionsMenu to run again, you can simply call invalidateOptionsMenu() or activity?.invalidateOptionsMenu() (from a fragment) and the application will queue up the menu to be recreated. Alternatively you can store off the menu item in a member variable to modify it later, but be careful to destroy your reference to it within onDestroyOptionsMenu to avoid a memory leak.)
The fact that the menu item is disabled should be enough to grey out the text or the icon automatically. The difficulty is in setting up your styles to make this work.
Short Answer
First create a color state list my_color_state_list.xml that you want your icons and text to use (e.g. black when enabled, grey when disabled). (See the full answer for an example.)
If you're using com.google.android.material.appbar.MaterialToolbar, you can tell it to use this selector for icons and text by providing a custom theme overlay. In your activity's XML, give the toolbar the attribute android:theme="#style/Foo" and define that style somewhere as:
<style name="Foo">
<item name="colorControlNormal">#color/my_color_state_list</item>
<item name="actionMenuTextColor">#color/my_color_state_list</item>
</style>
Now when the menu item is enabled or disabled via menu.findItem(R.id.menu_my_item).isEnabled = false the text will automatically change color, and any icons which use the color ?attr/colorControlNormal will also automatically change color.
Full answer
My starting place
My menu items are part of a Material toolbar. This answer may help for other kinds of toolbar/app bar, but your mileage may vary. In my activity I have something like this:
<com.google.android.material.appbar.MaterialToolbar
android:id="#+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:theme="#style/ThemeOverlay.MaterialComponents.Toolbar.Surface"/>
and the theme I'm using looks something like this:
<style name="Theme.MyApp" parent="Theme.MaterialComponents.DayNight.NoActionBar">
<item name="colorPrimary">#color/blue</item>
<item name="colorSecondary">#color/green</item>
<item name="colorSurface">#color/lightGrey</item>
<item name="colorOnSurface">#color/black</item>
[...]
<item name="windowActionModeOverlay">true</item>
</style>
It is also convention that the icon you use in buttons and menu items (and everywhere really) should have their default color be ?attr/colorControlNormal. So for example I might have a vector image which looks like:
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24"
android:tint="?attr/colorControlNormal"
android:tintMode="src_atop">
<path android:pathData="..." android:fillColor="#android:color/white"/>
</vector>
If you download an icon from Material Icons you will see they all use colorControlNormal.
What I needed to do
If you look back at the definition of my toolbar, you will see it uses a ThemeOverlay ThemeOverlay.MaterialComponents.Toolbar.Surface which is defined as:
<style name="ThemeOverlay.MaterialComponents.Toolbar.Surface" parent="">
<item name="colorControlNormal">#color/material_on_surface_emphasis_medium</item>
<item name="actionMenuTextColor">#color/material_on_surface_emphasis_medium</item>
</style>
This sets the menu item text color and icon color to #color/material_on_surface_emphasis_medium which does not respond to being enabled or not. #color/material_on_surface_emphasis_medium looks like:
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:alpha="#dimen/material_emphasis_medium" android:color="?attr/colorOnSurface"/>
</selector>
(You may be using ThemeOverlay.MaterialComponents.Toolbar.Primary instead, which has a similar issue - it simply uses colorOnPrimary.)
We need to replace this with our own color state list which responds to enabled state. So, make a new file res/color/menu_item_selector.xml that looks something like this:
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_enabled="true" android:alpha="#dimen/material_emphasis_medium" android:color="?attr/colorOnSurface"/>
<item android:alpha="#dimen/material_emphasis_disabled" android:color="?attr/colorOnSurface"/>
</selector>
You see I've used the same conventions that the material library does by using their constants to define the alpha values, and I used colorOnSurface as my color. If you were using ThemeOverlay.MaterialComponents.Toolbar.Primary you would want colorOnPrimary instead. Of course you can use any color or alpha here, it's up to you.
And now make a new ThemeOverlay in res/values/styles.xml to point to this selector, inheriting from whatever ThemeOverlay you were using:
<!-- Toolbar - overrides the menu text color to use a selector that responds to whether it's enabled or not -->
<style name="ThemeOverlay.MyTheme.Toolbar" parent="ThemeOverlay.MaterialComponents.Toolbar.Surface">
<!-- Color used in the icons of menu actions (i.e. non-overflow menu items). This is just convention, this will affect anything that uses ?attr/colorControlNormal) -->
<item name="colorControlNormal">#color/menu_item_color_selector</item>
<!-- Color used in the text of menu actions (i.e. non-overflow menu items) -->
<item name="actionMenuTextColor">#color/menu_item_color_selector</item>
</style>
And now finally we can apply this ThemeOverlay to the toolbar:
<com.google.android.material.appbar.MaterialToolbar
android:id="#+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:theme="#style/ThemeOverlay.MyTheme.Toolbar"/>
setEnabled(false) works fine on API Level < 14 but on 14 the item still clickable.
Have a look at this link
setEnabled can also be used for MenuItems.
Here's a simple way to do it (using Kotlin):
fun changeMenuItemColour(enabled: Boolean) {
var menuItem = SpannableString(mCustomToolbar?.menu?.findItem(R.id.some_menu_item)?.title)
var style = activity?.resources?.getColor(R.color.darkGraphite)!!
if (enabled) style = activity?.resources?.getColor(R.color.black)!!
menuItem.setSpan(ForegroundColorSpan(style), 0, menuItem.length, 0)
}
I made a layout that is just simply a textview that says "What do you want?", followed by a series of buttons underneath it.
All of the buttons can be clicked/touched, but when I scroll with the trackball, none of them become highlighted. I noticed, however, then when I disable the background colors on the buttons, I can see the orange box that shows that button's focus.
Is there any way I can visibly see the focus while still being able to have a background color on the buttons?
EDIT: Found the solution! This helped A LOT. Standard Android Button with a different color
Create a "selector" resource in your res/drawable. It can look something like this:
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:state_selected="false"
android:state_pressed="false"
android:drawable="#color/white" />
<item
android:state_pressed="true"
android:drawable="#color/orange" />
<item
android:state_selected="true"
android:state_pressed="false"
android:drawable="#color/blue" />
</selector>
Then set the background of your button to be:
android:background="#drawable/your_selector"
Rather than applying a simple background color to buttons, try applying a ColorStateList instead.
To do so, define a new XML file at
/res/color/buttonstate.xml
and use code such as the following:
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:color="# FOCUSED COLOR HERE"
android:state_focused="true" />
<item android:drawable="# DEFAULT COLOR HERE" />
</selector>
Notes:
You can definitely add more colors for more states, such as pressed, enabled, and certain other factors.
In the layout or code just reference R.color.buttonstate or #color/buttonstate (the XML's filename).
Make sure the default color is last. This is because it goes down the list and finds the first item that has all of the states the same as it. If you don't provide android:state_focused="false" for the default item and put it first, it will always display.
You can do a similar thing with drawables and
nine-patch drawables to make your own custom button styles.
Rather than just change the background color, consider using a 9-patch style. This is more work to begin, but you'll have much more control over your app's appearance.
Modify your Button layout to look something like this (the style line is the kicker):
<Button
style="#style/PushButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
Your styles.xml resource file then should contain a style similar to this:
<style name="PushButton">
<item name="android:background">#drawable/btn</item>
</style>
Then the btn.xml (put in in res/drawable) contents should look something like this:
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="#drawable/btn_pressed"
android:state_pressed="true" />
<item android:drawable="#drawable/btn_focused"
android:state_pressed="false"
android:state_focused="true" />
<item android:drawable="#drawable/btn_default"
android:state_focused="false"
android:state_pressed="false" />
You would then use some image editor to create files named btn_pressed.9.png, btn_focused.9.png, and btn_default.9.png. Drop these files in your res/drawable.
A good starting point is the Google IO app (I lifted the code examples from it). Just grab the png files and modify them to match your desired style.
Keep in mind you can put all sorts of stuff in the style now, like text size, height and width.