Thursday, November 3, 2011

Android. iPhone-style dialog. XML – only.

Source Code

Русский перевод


As the next exercise I will show how to implement the dialog that looks like iPhone alert view. No pictures will be used. We will use only XML.

Really it’s simple. First, let’s define xml-drawable for the button:


<layer-list xmlns:android="http://schemas.android.com/apk/res/android" >

    <item>
<shape android:shape="rectangle" >

            <corners android:radius="8dip" />

            <gradient
                android:angle="270"
                android:endColor="#FF440000"
                android:startColor="#FF990000"
                android:type="linear" />
        </shape></item>

    <item android:top="20dip">
<shape android:shape="rectangle" >

            <corners
                android:bottomLeftRadius="8dp"
                android:bottomRightRadius="8dp" />

            <solid android:color="#40000000" />
        </shape></item>

</layer-list>


Here we have two layers. The first layer – the rectangle with the gradient. The second layer – the rectangle shifted 20 dip top. This layer should overlap the half of the first one. So, real button should be 40 dip high.

Second, we’ll define the content of the dialog – the header, the text view for a message and the OK button:

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

    <LinearLayout
        xmlns:android="http://schemas.android.com/apk/res/android"
        android:id="@+id/alert_wrapper"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:layout_marginLeft="20dip"
        android:layout_marginRight="20dip"
        android:gravity="center_horizontal"
        android:orientation="vertical" >

        <TextView
            android:id="@+id/dialog_title"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginTop="5dip"
            android:text="Header container"
            android:textColor="#ffffff"
            android:textSize="17dip"
            android:textStyle="bold" />

        <TextView
            android:id="@+id/dialog_message"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_margin="10dip"
            android:gravity="center_horizontal"
            android:maxLines="5"
            android:scrollbars="vertical"
            android:text="Text container"
            android:textColor="#ffffff"
            android:textSize="15dip" />

        <LinearLayout
            xmlns:android="http://schemas.android.com/apk/res/android"
            android:layout_width="fill_parent"
            android:layout_height="wrap_content"
            android:layout_marginBottom="10dip"
            android:layout_marginTop="10dip"
            android:gravity="center_horizontal"
            android:orientation="horizontal" >

            <Button
                android:id="@+id/ok"
                android:layout_width="fill_parent"
                android:layout_height="40dip"
                android:layout_marginBottom="10dip"
                android:layout_marginLeft="10dip"
                android:layout_marginRight="10dip"
                android:background="@drawable/iphone_style_button"
                android:text="@string/ok"
                android:textColor="@color/White"
                android:textSize="17dip"
                android:textStyle="bold" />
        </LinearLayout>
    </LinearLayout>

</LinearLayout>


And at last the background for the dialog will be defined in a code. We will need a drawable with three layers for this – a rectangle shape with white border, a rectangle shape with main color and a rectangle shape that will contain a gloss effect.
To make the white border visible under the first layer we’ll set insets for this layer:



// Layers array
  Drawable[] arr = new Drawable[3];

  float roundedCorner[] = new float[] { 8, 8, 8, 8, 8, 8, 8, 8 };

  // First layer - to make a border
  GradientDrawable first = new GradientDrawable();
  first.setShape(GradientDrawable.RECTANGLE);
  first.setCornerRadii(roundedCorner);
  first.setStroke(2, Color.WHITE);

  // Second layer - background
  GradientDrawable second = new GradientDrawable();
  second.setShape(GradientDrawable.RECTANGLE);
  second.setCornerRadii(roundedCorner);
  second.setColor(Color.argb(255, 127, 0, 0));

  // Third layer - for the gloss effect
  GlossDrawable third = new GlossDrawable();

  arr[0] = first;
  arr[1] = second;
  arr[2] = third;

  LayerDrawable background = new LayerDrawable(arr);





The more complicated stuff is in the GlossDrawable class. There we will override onDraw method to calculate where the gloss gradient will be.

The picture describing calculations:



Using the Pythagoras' theorem we have sides of the inscribed triangle:

Then using Heron’s formula we’ll find the area of the inscribed triangle:



And at last the radius:


Now we should draw the circle slightly lower (1/8 of the shape height). The center of the circle will be:


int centerX = (int) shape.getWidth() / 2;
  int centerY = (int) (-radius + shape.getHeight() / 2);


The rectangle to draw the circle will be:

RectF rectf = new RectF(shape.getWidth() / 2 - radius,  
  shape.getHeight() / 4 - radius * 2, 
  shape.getWidth() / 2 + radius, shape.getHeight() / 4);

Applying the gradient we have:



Use the same technique to make info and confirm dialogs. All you need is to change the background color and the content layout.

Cheerio!

2 comments:

  1. Very nice, if other users are looking for Mobile equivalents we just released: PortKit: UX Metaphor Equivalents for iOS & Android

    http://kintek.com.au/blog/portkit-ux-metaphor-equivalents-for-ios-and-android/

    It has side by side comparisons of the native ui widgets and links to downloadable PSDs for designing.

    ReplyDelete