Showing posts with label customization. Show all posts
Showing posts with label customization. Show all posts

Sunday, May 27, 2012

Customize your progress bar

ProgressBar is standard visual indicator for progress of some operation. It's often used by developers as a part of ProgressDialog to prevent user's interaction with application (because ProgressDialog is modal) while complex operations are performed in background.

Style of ProgressBar depends on  device vendor. It may looks like

for Samsung devices or like
for HTC devices etc. The same situation for 'indeterminate' progress with infinite round spinner. 

Fortunately, ProgressBar is very flexible and allow us to customize it (for example, when we want to have the same progress line for our application at any device). Let's start from most obvious - style. Default progress bar styles provided by sdk are:
  • Widget.ProgressBar.Horizontal
  • Widget.ProgressBar.Small
  • Widget.ProgressBar.Large
  • Widget.ProgressBar.Inverse
  • Widget.ProgressBar.Small.Inverse
  • Widget.ProgressBar.Large.Inverse
I believe the titles speaks for itself. I prefer Widget.ProgressBar.Horizontal. Further, let's define height of dialog using layout_height attribute:

   <ProgressBar  
       android:id="@+id/progress"  
       style="@android:style/Widget.ProgressBar.Horizontal"  
       android:layout_width="fill_parent"  
       android:layout_height="20dp"/>  

Now progress bar will looks like on pictures below.

To customize progress line we need create LayerDrawable background, something like this:

 <?xml version="1.0" encoding="utf-8"?>  
 <layer-list xmlns:android="http://schemas.android.com/apk/res/android">  
   <item android:id="@+id/my_bg">  
     <shape>  
       <corners android:radius="20dip"/>  
       <gradient android:startColor="#C0C0C0" android:centerColor="#F8F8FF"  
            android:centerY="0.75" android:endColor="#ffffff" android:angle="90"/>  
       <stroke android:width="1dp" android:color="#00aa00"/>  
     </shape>  
   </item>  
   <item android:id="@+id/my_progress">  
     <clip>  
       <shape>  
         <corners android:radius="20dip"/>  
         <gradient android:startColor="#CaCaC0" android:centerColor="#2828FF"  
              android:centerY="0.75" android:endColor="#325423" android:angle="270"/>  
       </shape>  
     </clip>  
   </item>  
 </layer-list>  

Tiny clarifications: since progress line has background and progress state at least 2 layers are required. And important moment: first layer is background while second is progress. Id of layers actually can be arbitrary (as in my example), but if you don't want to clip your layers by yourself you can use system hardcoded names: background for first layer and progress for second. In this case method tileify of ProgressBar class will perform clipping internally:

   private Drawable tileify(Drawable drawable, boolean clip) {  
     if (drawable instanceof LayerDrawable) {  
       LayerDrawable background = (LayerDrawable) drawable;  
       final int N = background.getNumberOfLayers();  
       Drawable[] outDrawables = new Drawable[N];  
       for (int i = 0; i < N; i++) {  
         int id = background.getId(i);  
         outDrawables[i] = tileify(background.getDrawable(i),  
             (id == R.id.progress || id == R.id.secondaryProgress));  
       }  
     ...  
     } else if (drawable instanceof BitmapDrawable) {  
     ...  
       return (clip) ? new ClipDrawable(shapeDrawable, Gravity.LEFT,  
           ClipDrawable.HORIZONTAL) : shapeDrawable;  
     }  
     return drawable;  
   }  

And this is how progress bar will looks like with crazy color in our LayerDrawable:


Not bad, isn't so?

Sunday, April 1, 2012

Rich TextView

When we want to have beauty and stylish text we call html for help. According to Formatting and Styling document there are only 3 supported tags: b, i, and u. It also contains some mentions about fromHtml method, but it doesn't nearly cover some of the useful cases.
Let's taking closer to implementation. Containing not just CharSequence, but being able to convert this buffer to SpannableString (in setText method) TextView became powerful markup container (since SpannableString implements Spannable - interface for text whith markup objects). As for possible markup values within your string resources: in the depths of AssetManager we can see, that array of StringBlock is used to retrieve data from compiled string resources and return them via getString method. So, after considering applyStyles we can list supported tags:
  • <a href="">(supports attributes "href")
  • <annotation>
  • <b>
  • <big>
  • <font> (be aware, use "fgcolor" attribute instead "color")
  • <i>
  • <li>
  • <marquee>
  • <small>
  • <strike>
  • <sub>
  • <sup>
  • <tt>
  • <u>
  • <ul>

Html#fromHtml gives us even more. It uses HtmlToSpannedConverter, which supports:
  • <a href="">(supports attributes "href")
  • <b>
  • <big>
  • <blockquote>
  • <br>
  • <cite>
  • <dfn>
  • <div>
  • <em>
  • <font>(supports attributes "color" and "face")
  • <i>
  • <img>
  • <p>
  • <small>
  • <strong>
  • <sub>
  • <sup>
  • <tt>
  • <u>

Let's take a look to example. Here is strings.xml:
 <?xml version="1.0" encoding="utf-8"?>  
 <resources>  
   <string name="simple_raw_string">I\'m simple raw string</string>  
   <string name="html_string_1">  
     <font bgcolor='#ff440050'><b>I\'m</b> <i>html</i> <font fgcolor='#ffff5400'>string</font></font>  
   </string>  
   <string name="html_string_2"><![CDATA[  
     <em>I\'m</em> <font color=\'#0054ff\'>another</font> <br> <dfn>html</dfn> <big>string</big>]]>  
   </string>  
 </resources>  

Layout

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
              android:orientation="vertical"
              android:layout_width="fill_parent"
              android:layout_height="fill_parent">

    <TextView
            android:layout_width="fill_parent"
            android:layout_height="wrap_content"
            android:text="@string/simple_raw_string"/>

    <TextView
            android:layout_width="fill_parent"
            android:layout_height="wrap_content"
            android:text="@string/html_string_1"/>

    <TextView
            android:id="@+id/html_text"
            android:layout_width="fill_parent"
            android:layout_height="wrap_content"/>


</LinearLayout>
And main activity
   @Override  
   public void onCreate(Bundle savedInstanceState) {  
     super.onCreate(savedInstanceState);  
     setContentView(R.layout.layout);  
     ((TextView) findViewById(R.id.html_text)).setText(Html.fromHtml(getString(R.string.html_string_2)));  
   }  
And how it looks on device