Tuesday, January 19, 2010

Android UI: Colored dialogs


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.



Post a Comment