Android UI: Colored dialogs
This document can be read in Google Docs (http://docs.google.com/View?id=ddwc44gs_232dw6nf66h), cut and paste link if you have problems accessing it.
If you have problems seeing the images, like the icon in this box don't blame me, it seems to be an issue when you publish to Blogspot from Google Docs. |
introduction
Sometimes you need a stronger visual cue for a very special situation that may arise during the lifecycle of your application.
One approach is to copy <ANDROID-SDK>/platforms/android-2.1/data/res/drawable-mdpi/panel_background.9.png, change its color and then use it as the dialog background in your theme.
For example, extending the default Theme
1 <style name="MyColorDialogTheme" parent="android:Theme.Dialog">
2 <item name="android:windowBackground">@drawable/color_panel_background/item>
3 </style>
|
This was discussed in
stackoverflow, but I though there should be a better solution. Copying and changing images is not very attractive if you want to have several colors or even decide it at runtime.
colored dialogs
We will be trying to discover the simplest yet usable way of colorizing AlertDialogs.
Some standard behavior should be changed, so we need to extend AlertDialog.
MyColorDialog and Builder
What we are going to do is to extend AlertDialog to create a class that given a theme that defines the android:tint attribute then all the Views in the dialog are tinted with this specific color.
We need to iterate over the Views so a simple iterator is also defined.
1 public static class MyColorDialog extends AlertDialog {
2 private static int NONE = -1;
3 private int tint = NONE;
4
5 /**
6 * @param context
7 * @param theme
8 */
9 protected MyColorDialog(Context context) {
10 super(context);
11 init();
12 }
13
14 /**
15 * @param context
16 * @param theme
17 */
18 protected MyColorDialog(Context context, int theme) {
19 super(context, theme);
20 init();
21 }
22
23 /**
24 *
25 */
26 private void init() {
27 final Theme theme = getContext().getTheme();
28 final TypedArray attrs = theme.obtainStyledAttributes(new int[] { android.R.attr.tint });
29 tint = attrs.getColor(0, NONE);
30 }
31
32 /* (non-Javadoc)
33 * @see android.app.Dialog#show()
34 */
35 @Override
36 public void show() {
37 super.show();
38 setTint(tint);
39 }
40
41 /**
42 * @param tint
43 */
44 public void setTint(int tint) {
45 this.tint = tint;
46
47 if ( tint != NONE ) {
48 Iterator<View> vi = iterator(android.R.id.content);
49 while ( vi.hasNext() ) {
50 tintView(vi.next(), tint);
51 }
52 }
53 }
54
55 /**
56 * Set the {@link tint} color for the {@link View}
57 *
58 * @param v the {@link View} to change the tint
59 * @param tint color tint
60 */
61 private static void tintView(final View v, final int tint) {
62 if ( v != null ) {
63 final Mode mode = PorterDuff.Mode.SRC_ATOP;
64
65 if ( v instanceof ImageView ) {
66 final Drawable d = ((ImageView)v).getDrawable();
67 if ( d != null ) {
68 d.mutate().setColorFilter(tint, mode);
69 }
70 }
71 else {
72 final Drawable d = v.getBackground();
73 if ( d != null ) {
74 d.setColorFilter(tint, mode);
75 }
76 }
77 }
78 }
79
80
81 /**
82 * @param button
83 */
84 public void setCancelButton(Button button) {
85 button.setOnClickListener(new View.OnClickListener() {
86
87 @Override
88 public void onClick(View v) {
89 cancel();
90 }
91
92 });
93 }
94
95 /**
96 * @param button
97 */
98 public void setPositiveButton(Button button) {
99 button.setOnClickListener(new View.OnClickListener() {
100
101 @Override
102 public void onClick(View v) {
103 dismiss();
104 }
105
106 });
107 }
108
109
110 /**
111 * Return a {@link ChildrenIterator} starting at the {@link ViewGroup}
112 * identified by the specified resource id.
113 *
114 * @param res resource id of the {@link ViewGroup} to start the iteration
115 * @return iterator
116 */
117 public Iterator<View> iterator(int res) {
118 final ViewGroup vg = (ViewGroup)findViewById(res);
119 return new ChildrenIterator<View>(vg);
120 }
121
122
123 public static class Builder extends AlertDialog.Builder {
124
125 private MyColorDialog dialog;
126
127 public Builder(Context context) {
128 super(context);
129 dialog = new MyColorDialog(context);
130 }
131
132 public Builder(Context context, int theme) {
133 super(context);
134 dialog = new MyColorDialog(context, theme);
135 }
136
137 @Override
138 public MyColorDialog create() {
139 return dialog;
140 }
141
142 @Override
143 public Builder setMessage(CharSequence message) {
144 dialog.setMessage(message);
145 return this;
146 }
147
148 @Override
149 public Builder setTitle(CharSequence title) {
150 dialog.setTitle(title);
151 return this;
152 }
153
154 @Override
155 public Builder setPositiveButton(CharSequence text,
156 OnClickListener listener) {
157 dialog.setButton(BUTTON_POSITIVE, text, listener);
158 return this;
159 }
160
161 @Override
162 public Builder setIcon(int iconId) {
163 dialog.setIcon(iconId);
164 return this;
165 }
166
167 }
168 }
|
Iterator
This is the iterator used in
MyColorDialog.
1 public static class ChildrenIterator<V extends View> implements Iterator<V> {
2 ArrayList<V> list;
3 private int i;
4
5 public ChildrenIterator(ViewGroup vg) {
6 super();
7
8 if ( vg == null ) {
9 throw new RuntimeException("ChildrenIterator needs a ViewGroup != null to find its children");
10 }
11 init();
12 findChildrenAndAddToList(vg, list);
13 }
14
15 private void init() {
16 list = new ArrayList<V>();
17 i = 0;
18 }
19
20 @Override
21 public boolean hasNext() {
22 return ( i < list.size() );
23 }
24
25 @Override
26 public V next() {
27 return list.get(i++);
28 }
29
30 @Override
31 public void remove() {
32 list.remove(i);
33 }
34
35 @SuppressWarnings("unchecked")
36 private void findChildrenAndAddToList(final ViewGroup root, final ArrayList<V> list) {
37 for (int i=0; i < root.getChildCount(); i++) {
38 V v = (V)root.getChildAt(i);
39 list.add(v);
40 if ( v instanceof ViewGroup ) {
41 findChildrenAndAddToList((ViewGroup)v, list);
42 }
43 }
44 }
45
46 }
|
styles
Let's just define some styles to colorize our dialogs using android:style/Theme.Dialog as the parent.
1 <?xml version="1.0" encoding="utf-8"?>
2 <resources>
3 <!-- Orange -->
4 <style name="OrangeDialogTheme" parent="@android:style/Theme.Dialog">
5 <item name="android:tint">@color/orange_tint</item>
6 </style>
7
8 <style name="OrangeAlertDialogTheme" parent="OrangeDialogTheme">
9 <item name="android:windowBackground">@color/transparent</item>
10 </style>
11
12
13 <!-- Red -->
14 <style name="RedDialogTheme" parent="@android:style/Theme.Dialog">
15 <item name="android:tint">@color/red_tint</item>
16 </style>
17
18 <style name="RedAlertDialogTheme" parent="RedDialogTheme">
19 <item name="android:windowBackground">@color/transparent</item>
20 </style>
21 </resources>
|
colors
1 <?xml version="1.0" encoding="utf-8"?>
2 <resources>
3 <color name="red_tint">#88FF0000</color>
4 <color name="blue_tint">#880000FF</color>
5 <color name="yellow_tint">#88FFFF00</color>
6 <color name="purple_tint">#88995f86</color>
7 <color name="orange_tint">#aaffbf00</color>
8 <color name="magenta_tint">#88ff33cc</color>
9 <color name="transparent">#0000</color>
10 </resources>
|
usage
From our Activities we can use MyColorDialog as any other AlertDialog, in this case we will be using a custom theme defined previously
1 dialog = (new MyColorDialog.Builder(this, R.style.RedAlertDialogTheme))
2 .setTitle("Warning")
3 .setMessage("This phone will self-destruct in five seconds.\nPlease stay back.")
4 .setPositiveButton("Dismiss", null)
5 .create();
6 dialog.show();
|
samples
These are some dialogs created using different color variations.
conclusion
We introduced a simple way of changing the appearance of your dialogs just from styles.
These classes are oversimplifications from the ones that are part of
auito, formerly known as
autoandroid. More information and samples will be provided soon.
Comments are gladly welcome.
Copyright © 2010 Diego Torres Milano. All rights reserved.