post

How to Dynamically Change Android™ ListView Text Colors

Dynamically Change Android™ ListView Text Colors
The default Android ListView object is difficult to work with if creativity is one of your design factors. In my apps, I like to offer users the preference of choosing a light or dark color scheme as they desire. This requires programatically changing every object displayed on the screen on the fly once the user exits the preference screen. It’s no problem changing the background color of a ListView object, but changing the TextColor is a whole other issue. There is no native hook to reach the TextView field used to display the ListView’s text. There used to be in earlier versions of Android, but I’m writing for Froyo and higher. The android:setTextColor directive is no longer available in the ListView object. To further complicate matters, my ListView objects are displayed on a TabWidget. I have one ListView for each tab. For a 5-tabbed app, I have to change 5 ListViews.

Let’s start this tutorial with a simple Linear Layout for the TabHost as our main.xml.

  1. <?xml version="1.0" encoding="utf-8"?>
  2. <TabHost xmlns:android="http://schemas.android.com/apk/res/android"
  3. android:id="@android:id/tabhost"
  4. android:layout_width="fill_parent"
  5. android:layout_height="fill_parent" android:hapticFeedbackEnabled="true">
  6. <LinearLayout android:id="@+id/main_layout"
  7. android:orientation="vertical"
  8. android:layout_width="fill_parent"
  9. android:layout_height="fill_parent"
  10. android:padding="5dp">
  11. <TabWidget android:id="@android:id/tabs"
  12. android:layout_width="fill_parent"
  13. android:layout_height="wrap_content"/>
  14. <FrameLayout android:id="@android:id/tabcontent"
  15. android:layout_width="fill_parent"
  16. android:layout_height="fill_parent"
  17. android:padding="5dp">
  18. <ListView android:id="@+id/tab_list_1"
  19. android:layout_width="fill_parent"
  20. android:layout_height="fill_parent"
  21. android:cacheColorHint="#00000000" />
  22. <ListView android:id="@+id/tab_list_2"
  23. android:layout_width="fill_parent"
  24. android:layout_height="fill_parent"
  25. android:cacheColorHint="#00000000" />
  26. <ListView android:id="@+id/tab_list_3"
  27. android:layout_width="fill_parent"
  28. android:layout_height="fill_parent"
  29. android:cacheColorHint="#00000000" />
  30. <ListView android:id="@+id/tab_list_4"
  31. android:layout_width="fill_parent"
  32. android:layout_height="fill_parent"
  33. android:cacheColorHint="#00000000" />
  34. <ListView android:id="@+id/tab_list_5"
  35. android:layout_width="fill_parent"
  36. android:layout_height="fill_parent"
  37. android:cacheColorHint="#00000000" />
  38. </FrameLayout>
  39. </LinearLayout>
  40. </TabHost>
<?xml version="1.0" encoding="utf-8"?>
<TabHost xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@android:id/tabhost"
android:layout_width="fill_parent"
android:layout_height="fill_parent" android:hapticFeedbackEnabled="true">
<LinearLayout android:id="@+id/main_layout"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:padding="5dp">
<TabWidget android:id="@android:id/tabs"
android:layout_width="fill_parent"
android:layout_height="wrap_content"/>
<FrameLayout android:id="@android:id/tabcontent"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:padding="5dp">
<ListView android:id="@+id/tab_list_1"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:cacheColorHint="#00000000" />
<ListView android:id="@+id/tab_list_2"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:cacheColorHint="#00000000" />
<ListView android:id="@+id/tab_list_3"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:cacheColorHint="#00000000" />
<ListView android:id="@+id/tab_list_4"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:cacheColorHint="#00000000" />
<ListView android:id="@+id/tab_list_5"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:cacheColorHint="#00000000" />
</FrameLayout>
</LinearLayout>
</TabHost>

Notice the highlighted code at lines 21, 25, 29, 33 and 37. Setting the android:cacheColorHint to #00000000 overrides a common issue developers face when customizing the ListView background. By default, many Android widgets, ListView included, have a transparent background. This means you can see through the default window’s background. ListView also enables fading edges by default. The fading edges technique is used throughout the system to indicate that a container can be scrolled. Fading edges is a gradient display rendered as a very dark gray to black fade. The fade effect is implemented using a combination of Canvas.saveLayerAlpha() and the Porter-Duff Destination Out blending mode. When we customize the background color, the blending optimzation leaves part of the dark gradient at the bottom of the screen. Scrolling the ListView changes all the TextView abjects back to their original color. To disable the optimization, simply use the transparent color #00000000 and you won’t have any problems.

Next we’re going to create a layout for the ListView rows. This file is called tab_list_layout.xml. Start this file by coping the simple_list_item_1 layout from the Android SDK.

Simple_list_item_1.xml from the Android SDK:

  1. <?xml version="1.0" encoding="utf-8"?>
  2. <!-- Copyright (C) 2006 The Android Open Source Project
  3.  
  4. Licensed under the Apache License, Version 2.0 (the "License");
  5. you may not use this file except in compliance with the License.
  6. You may obtain a copy of the License at
  7.  
  8. http://www.apache.org/licenses/LICENSE-2.0
  9.  
  10. Unless required by applicable law or agreed to in writing, software
  11. distributed under the License is distributed on an "AS IS" BASIS,
  12. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. See the License for the specific language governing permissions and
  14. limitations under the License.
  15. -->
  16.  
  17. <TextView xmlns:android="http://schemas.android.com/apk/res/android"
  18. android:id="@android:id/text1"
  19. android:layout_width="match_parent"
  20. android:layout_height="wrap_content"
  21. android:textAppearance="?android:attr/textAppearanceLarge"
  22. android:gravity="center_vertical"
  23. android:paddingLeft="6dip"
  24. android:minHeight="?android:attr/listPreferredItemHeight"
  25. />
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2006 The Android Open Source Project

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->

<TextView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@android:id/text1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textAppearance="?android:attr/textAppearanceLarge"
android:gravity="center_vertical"
android:paddingLeft="6dip"
android:minHeight="?android:attr/listPreferredItemHeight"
/>

Tab_list_layout.xml:

  1. <?xml version="1.0" encoding="utf-8"?>
  2. <LinearLayout
  3. xmlns:android="http://schemas.android.com/apk/res/android"
  4. android:layout_width="wrap_content"
  5. android:layout_height="wrap_content">
  6. <TextView xmlns:android="http://schemas.android.com/apk/res/android"
  7. android:id="@+id/list_text_light"
  8. android:layout_width="match_parent"
  9. android:layout_height="wrap_content"
  10. android:textAppearance="?android:attr/textAppearanceLarge"
  11. android:textColor="@color/app_text_color_light"
  12. android:gravity="center_vertical"
  13. android:paddingLeft="6dip"
  14. android:minHeight="?android:attr/listPreferredItemHeight"
  15. />
  16. <TextView xmlns:android="http://schemas.android.com/apk/res/android"
  17. android:id="@+id/list_text_dark"
  18. android:layout_width="match_parent"
  19. android:layout_height="wrap_content"
  20. android:textAppearance="?android:attr/textAppearanceLarge"
  21. android:textColor="@color/app_text_color_dark"
  22. android:gravity="center_vertical"
  23. android:paddingLeft="6dip"
  24. android:minHeight="?android:attr/listPreferredItemHeight"
  25. />
  26. </LinearLayout>
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/list_text_light"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textAppearance="?android:attr/textAppearanceLarge"
android:textColor="@color/app_text_color_light"
android:gravity="center_vertical"
android:paddingLeft="6dip"
android:minHeight="?android:attr/listPreferredItemHeight"
/>
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/list_text_dark"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textAppearance="?android:attr/textAppearanceLarge"
android:textColor="@color/app_text_color_dark"
android:gravity="center_vertical"
android:paddingLeft="6dip"
android:minHeight="?android:attr/listPreferredItemHeight"
/>
</LinearLayout>

In our layout file, we’ve created two TextView objects. One for the dark color scheme and one for the light. Name them accordingly (lines 7 and 17) and assign the desired colors to them (lines 11 and 21). The colors are defined in your colors.xml resource.

Next we’re going to setup our adapter. Remember, the text color is driven by our application user preference. So first we have to retrieve the user preference:

  1. // retrieve user color scheme preference
  2. My_App app = (My_App) this.getApplication();
  3. IsLightColors = app.RetrieveBoolean(getString(R.string.LightColorsKey));
  4. int list_text_id = (IsLightColors) ? R.id.list_text_light : R.id.list_text_dark; // defined in tab_list_layout.xml
  5.  
  6. // setup ListView adapter
  7. ListView list=(ListView)findViewById(R.id.tab_list_1); // defined in main.xml
  8. String[] listitems = getResources().getStringArray(R.array.ListItems); //defined in strings.xml
  9. ArrayAdapter<String> adapter = new ArrayAdapter<String>(this,R.layout.tab_list_layout, list_text_id, listitems);
  10. list.setAdapter(adapter);
// retrieve user color scheme preference
My_App app = (My_App) this.getApplication();
IsLightColors = app.RetrieveBoolean(getString(R.string.LightColorsKey));
int list_text_id = (IsLightColors) ? R.id.list_text_light : R.id.list_text_dark; // defined in tab_list_layout.xml

// setup ListView adapter
ListView list=(ListView)findViewById(R.id.tab_list_1); // defined in main.xml
String[] listitems = getResources().getStringArray(R.array.ListItems); //defined in strings.xml
ArrayAdapter<String> adapter = new ArrayAdapter<String>(this,R.layout.tab_list_layout, list_text_id, listitems);
list.setAdapter(adapter);

Line 4 is the key to changing the colors dynamically. The variable list_text_id is set to the id number of appropriate TextView widget defined in tab_list_layout.xml depending on the user’s color scheme preference. The list_text_id variable is passed to the ArrayAdapter as a parameter. Take note: if you are using a tabbed interface, you need to create a separate adapter for each tab on which you place a ListView widget. R.array.ListItems is the actual text you want to appear in the ListView rows defined in strings.xml.

  1. <string-array name="ListItems">
  2. <item>Item 1</item>
  3. <item>Item 2</item>
  4. <item>Item 3</item>
  5. <item>Item 4</item>
  6. <item>Item 5</item>
  7. <item>Item 6</item>
  8. </string-array>
<string-array name="ListItems">
<item>Item 1</item>
<item>Item 2</item>
<item>Item 3</item>
<item>Item 4</item>
<item>Item 5</item>
<item>Item 6</item>
</string-array>

Okay, so the setup is all complete, everything up till now will get you to the point where the colors will change when you first start your app. How do we code to change the text colors dynamically immediately after a user changes their preference?

When we instantiate the preferences activity, we start it to return a result. When the result is returned from the preference activity, we run the code to instantiate our ListView adapter(s). In my case, I’ve created a method called SetTabColors() which I call twice. The first time after I create the tabs in the onCreate mehtod, the second time whenever a user changes their preferences.

  1. // create the options menu when the user presses the menu key
  2. @Override
  3. public boolean onCreateOptionsMenu(Menu menu) {
  4. super.onCreateOptionsMenu(menu);
  5. getMenuInflater().inflate(R.menu.mainmenu, menu);
  6. return true;
  7. }
  8.  
  9. @Override
  10. public boolean onOptionsItemSelected(MenuItem item) {
  11.  
  12. if (item.getItemId() == R.id.settings_menu_item) {
  13. Intent intent = new Intent().setClass(this,
  14. PreferenceActivity.class);
  15. this.startActivityForResult(intent, 0);
  16. } else if (item.getItemId() == R.id.exit_app_item) {
  17. finish();
  18. }
  19.  
  20. return true;
  21. }
  22.  
  23. @Override
  24. protected void onActivityResult(int requestCode, int resultCode, Intent data) {
  25. super.onActivityResult(requestCode, resultCode, data);
  26. setTabColors();
  27. }
  28.  
  29. protected void setTabColors() {
  30.  
  31. /**
  32. * Before we do anything, determine saved value of IsLightColors
  33. */
  34. Mobile_SDLC_App app = (Mobile_SDLC_App) this.getApplication();
  35. IsLightColors = app.RetrieveBoolean(getString(R.string.LightColorsKey));
  36.  
  37. // set the color scheme and resource based on IsLightColors user prefs
  38. int tab_indicator = IsLightColors ? R.drawable.tab_indicator_v4_light : R.drawable.tab_indicator_v4;
  39. int layoutColor = IsLightColors ? Color.WHITE : Color.BLACK;
  40. int list_text_id = (IsLightColors) ? R.id.list_text_light : R.id.list_text_dark;
  41.  
  42. View myLayout = findViewById(R.id.main_layout);
  43. myLayout.setBackgroundColor(layoutColor);
  44.  
  45. // set the background resource for each tab
  46. for (int i = 0; i < tabHost.getTabWidget().getChildCount(); i++) {
  47. tabHost.getTabWidget().getChildAt(i).setBackgroundResource(tab_indicator);
  48. }
  49.  
  50. //ListView
  51. ListView list=(ListView)findViewById(R.id.tab_list);
  52. String[] listitems = getResources().getStringArray(R.array.ListItems);
  53. ArrayAdapter<String> adapter = new ArrayAdapter<String>(this,R.layout.tab_list_layout, list_text_id, listitems);
  54. list.setAdapter(adapter);
  55.  
  56. }
// create the options menu when the user presses the menu key
@Override
public boolean onCreateOptionsMenu(Menu menu) {
super.onCreateOptionsMenu(menu);
getMenuInflater().inflate(R.menu.mainmenu, menu);
return true;
}

@Override
public boolean onOptionsItemSelected(MenuItem item) {

if (item.getItemId() == R.id.settings_menu_item) {
Intent intent = new Intent().setClass(this,
PreferenceActivity.class);
this.startActivityForResult(intent, 0);
} else if (item.getItemId() == R.id.exit_app_item) {
finish();
}

return true;
}

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
setTabColors();
}

protected void setTabColors() {

/**
* Before we do anything, determine saved value of IsLightColors
*/
Mobile_SDLC_App app = (Mobile_SDLC_App) this.getApplication();
IsLightColors = app.RetrieveBoolean(getString(R.string.LightColorsKey));

// set the color scheme and resource based on IsLightColors user prefs
int tab_indicator = IsLightColors ? R.drawable.tab_indicator_v4_light : R.drawable.tab_indicator_v4;
int layoutColor = IsLightColors ? Color.WHITE : Color.BLACK;
int list_text_id = (IsLightColors) ? R.id.list_text_light : R.id.list_text_dark;

View myLayout = findViewById(R.id.main_layout);
myLayout.setBackgroundColor(layoutColor);

// set the background resource for each tab
for (int i = 0; i < tabHost.getTabWidget().getChildCount(); i++) {
tabHost.getTabWidget().getChildAt(i).setBackgroundResource(tab_indicator);
}

//ListView
ListView list=(ListView)findViewById(R.id.tab_list);
String[] listitems = getResources().getStringArray(R.array.ListItems);
ArrayAdapter<String> adapter = new ArrayAdapter<String>(this,R.layout.tab_list_layout, list_text_id, listitems);
list.setAdapter(adapter);

}

Attribution

  • Android is a trademark of Google Inc.
  • The Android robot is reproduced or modified from work created and shared by Google and used according to terms described in the Creative Commons 3.0 Attribution License.

VictorFont.com runs on the Genesis Framework

Genesis FrameworkThe Genesis Framework empowers you to quickly and easily build incredible websites with WordPress. Genesis provides the secure and search-engine-optimized foundation that takes WordPress to places you never thought it could go.

Check out the incredible features and the selection of designs. It's that simple—start using Genesis now!

Click here to download The Genesis Guide for Absolute Beginners (PDF - 1.4 MB)

Comments

  1. Phan says

    My list view record in Database and I want set color for 1 RECORD ( ONLY 1 ). How ??? Please tell me by e-mail. Thanks !!!
    I use Cursor and add cursor to SimpleCursorAdapter. Then, I add SimpleCursorAdapter to listView by code :
    listContent.setAdapter(cursorAdapter);

    Please tell me, thanks !!!
    ( Sorry, My English is BAD but I can read the your answer. Thank you !!! ).

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code lang=""> <del datetime=""> <em> <i> <q cite=""> <strike> <strong> <pre lang="" extra="">

Current day month ye@r *