Wednesday, January 11, 2012

Options menu like ActionSheet








Now I'll show how to create options menu that will look like iPhone's Action Sheet.

Preparings.


First let's create animations for the menu. The menu will show from the bottom of the device screen. So the "in" animation will look like:

<set xmlns:android="http://schemas.android.com/apk/res/android">
 <translate
  android:fromXDelta="100%"
  android:toXDelta="100%"
  android:fromYDelta="0%"
  android:toYDelta="100%"
  android:duration="@android:integer/config_shortAnimTime"
  android:interpolator="@android:anim/linear_interpolator"
 >
 </translate>
</set>


As you can see it's the "translate" animation. The animated view will be always 100% screen width and will grow from 0% to 100% it's height.
Vice versa the "out" we'll define as following:
<set xmlns:android="http://schemas.android.com/apk/res/android">
 <translate
  android:fromXDelta="100%"
  android:toXDelta="100%"
  android:fromYDelta="100%"
  android:toYDelta="0%"
  android:duration="@android:integer/config_shortAnimTime"
  android:interpolator="@android:anim/linear_interpolator"
 >
 </translate>
</set>

It will shrink from 100% to 0% height.
And to apply the animation to a view:
    <style name="Animations.MenuAnimation">
        <item name="android:windowEnterAnimation">
                @anim/menu_animation_in</item>
        <item name="android:windowExitAnimation">
                @anim/menu_animation_out</item>
    </style>

The menu items will be the same as in Dialog demo, but with gray colors. And at last to complete XML stuff there are the menu item layout and the menu layout.
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"
    android:layout_gravity="center"
    android:background="#224c4c4c"
    android:gravity="center"
    android:orientation="vertical"
    android:padding="4dip" >

    <Button
        android:id="@+id/custom_menu_item_caption"
        style="@style/button_black"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:text="Add from URL" >
    </Button>

</LinearLayout>

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:background="#00000000"
    android:orientation="vertical"
    android:padding="0dip" >

    <View
        android:layout_width="fill_parent"
        android:layout_height="0.5dip"
        android:background="#ff4f4f4f" />

    <ImageView
        android:layout_width="fill_parent"
        android:layout_height="20dip"
        android:gravity="center"
        android:scaleType="fitXY"
        android:src="@drawable/menu_header" />

    <View
        android:layout_width="fill_parent"
        android:layout_height="0.5dip"
        android:background="#ff4f4f4f" />

    <LinearLayout
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        android:background="#aa000000"
        android:orientation="vertical"
        android:padding="0dip" >

        <TableLayout
            android:id="@+id/custom_menu_table"
            android:layout_width="fill_parent"
            android:layout_height="wrap_content"
            android:focusable="true"
            android:focusableInTouchMode="true"
            android:stretchColumns="*" >
        </TableLayout>
    </LinearLayout>

</LinearLayout>


Menu Header:
<shape xmlns:android="http://schemas.android.com/apk/res/android">
 <gradient
  android:angle="270"
  android:type="linear"
  android:endColor="#444c4c4c"
  android:startColor="#44ffffff"
 />
</shape>

The code
The code is quite clear. On base of the PopupWindow we form our menu. The layout for the popup window is the table layout. And each item of the menu we add to the layout. And to complete the menu we add a listener to each item.
 /**
  * Opened menu
  */
 private static volatile CustomMenu mMenu = null;

 private ArrayList<CustomMenuItem> mMenuItems;
 private OnMenuItemSelectedListener mListener = null;
 private Activity mContext = null;
 private static volatile PopupWindow mPopupWindow = null;
 private static boolean mIsShowing = false;

 /**
  * Menu item selected listener
  */
 public interface OnMenuItemSelectedListener {
  public void MenuItemSelectedEvent(Integer selection);
 }

 /**
  * Is the menu opened
  * 
  * @return boolean isShowing
  */
 public static boolean isShowing() {
  return mIsShowing;
 }

 /**
  * Constructor
  * 
  * @param activity
  *            Activity where the menu will be shown
  * @param OnMenuItemSelectedListener
  *            listener Listener
  * @param LayoutInflater
  *            items Items list
  * @return void
  */
 public CustomMenu(Activity activity, 
                        OnMenuItemSelectedListener listener,
   HashMap<Integer, String> items) {

  mListener = listener;
  mMenuItems = new ArrayList<CustomMenuItem>();
  mContext = activity;

  // Add items to the menu
  for (Integer id : items.keySet()) {

   String name = items.get(id);
   CustomMenuItem cmi = new CustomMenuItem();
   cmi.setCaption(name);
   cmi.setId(id);
   mMenuItems.add(cmi);

  }

 }


 public void show(View v) {

  // The menu is shown
  mIsShowing = true;
  int itemCount = mMenuItems.size();

  if (itemCount < 1)
   return; // Nothing to show

  if (mPopupWindow != null)
   return; // The menu is opened

  // Display settings
  Display display = ((WindowManager) mContext
    .getSystemService(Context.WINDOW_SERVICE))
                                .getDefaultDisplay();

  // The view to show
  View mView = ((Activity) mContext).getLayoutInflater().inflate(
    R.layout.custom_menu, null);

  // Create popup window to show
  mPopupWindow = new PopupWindow(mView, LayoutParams.FILL_PARENT,
    LayoutParams.WRAP_CONTENT, false);
  mPopupWindow.setAnimationStyle(R.style.Animations_MenuAnimation);
  mPopupWindow.setWidth(display.getWidth());
  mPopupWindow.showAtLocation(v, Gravity.BOTTOM, 0, 0);

  // Add menu items
  TableLayout table = (TableLayout) mView
    .findViewById(R.id.custom_menu_table);
  table.removeAllViews();

  for (int i = 0; i < itemCount; i++) {

   TableRow row = null;
   Button btn = null;

   // create headers
   row = new TableRow(mContext);
   row.setLayoutParams(new LayoutParams(
                            LayoutParams.FILL_PARENT,
                            LayoutParams.WRAP_CONTENT));

   final CustomMenuItem cmi = mMenuItems.get(i);
   View itemLayout = ((Activity) mContext).getLayoutInflater()
     .inflate(R.layout.custom_menu_item, null);
   btn = (Button) itemLayout
     .findViewById(R.id.custom_menu_item_caption);
   btn.setText(cmi.getCaption());

   btn.setOnClickListener(new OnClickListener() {
    @Override
    public void onClick(View v) {
     mListener.MenuItemSelectedEvent(cmi.getId());
     hide();
    }
   });

   row.addView(itemLayout);
   table.addView(row);
  }

 }



And we have: