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:



7 comments:

  1. The following code is understandable but if anybody provides it as a project format thn it will b better.In this class what is CustomMenuItem class,can any body gives code for it?

    ReplyDelete
    Replies
    1. The project source code is available on google code:
      https://code.google.com/p/android-tips-demo/
      The link also is at the beginning of the article

      Delete
  2. So many thanks Horrible,using the above link i did that Action sheet program.Really that link is very helpful for my project,thanks a lot........

    ReplyDelete
    Replies
    1. Hello Basant can you give me the code because this time either the code is deleted or i can not found so please if you have then can you send me my email address is siddhpuraamitr1@gmaill.com

      Delete
  3. I used the "option menu" example provided in the tips.demo. Very useful!!!! Thanks. But I want to share a simple change in the code, just to ensure multilingual capabilities. Steps

    - 1 --> Create a new entry in strings.xml with name "popup_close" and "Close" in the desired language.

    -2 --> In CustomMenu.java line
    if (cmi.getCaption().toString().equals("Close")){

    should be changed by

    if (cmi.getCaption().toString().equals(mContext.getString(R.string.popup_close))){

    With this simple change the close event will support multilingual apps.

    Thank you very much for all well done work! Your app helped me a lot.

    ReplyDelete
  4. I can not get Code from http://code.google.com/p/android-tips-demo/
    so please can any body help me to share this code thanks a lot

    ReplyDelete
    Replies
    1. All works fine:
      http://code.google.com/p/android-tips-demo/source/browse/#svn%2FTips.Demo

      Delete