Search
Saturday 24 June 2017
  • :
  • :

How to Implement iOS ActionSheet in Android?

ActionSheet is one type of view which is basically introduced in iOS. It is much like Alert Dialog in android from which user can select option from list. There are many ways to create ActionSheet in android for example using native alert dialog, extending fragments , using sliding drawer which open’s from bottom. Android does not provide default view which is same as ActionSheet but we can create it using android’s default view and customizing it which looks same as iOS ActionSheet.

Purpose of ActionSheet is to give the user list of options with cancel button which opens from bottom of the screen and it hides automatically after selecting option or by touching outside the ActionSheet or by pressing Cancel button. So we can say that it’s behavior is much like Alert Dialog in android.

Here we will create our custom ActionSheet by extending and customizing android’s Dialog class. We will also need some drawable images to make the view same as iOS and call it through our custom drawable xml file.

Android App Development

So we will create a new project in our Android Studio named ActionSheetDemo and follow the instructions mentioned below.

Design:

drawable/actionsheet_other_bt_top.xml

<selector xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:drawable="@drawable/actionsheet_top_pressed" android:state_pressed="true"/>
    <item android:drawable="@drawable/actionsheet_top_normal"/>
</selector>

In above xml actionsheet_top_pressed and actionsheet_top_normal are the 9 patch image files. We can use any image for it because it will be the background of single option item in ActionSheet. We can also use custom shape drawable for it to get click effect for each option. We will save it in our drawable folder.

Next we will need some attributes to set Spacing, Colors, Background of each option item in our ActionSheet. So android app development India team will create one attribute file for our ActionSheet like below.

values/attrs.xml

<resources>
    <declare-styleable name="ActionSheets">
        <attr name="actionSheetStyle" format="reference" />
    </declare-styleable>
    <declare-styleable name="ActionSheet">
        <attr name="actionSheetBackground" format="color|reference" />
        <attr name="cancelButtonBackground" format="color|reference" />
        <attr name="otherButtonTopBackground" format="color|reference" />
        <attr name="otherButtonMiddleBackground" format="color|reference" />
        <attr name="otherButtonBottomBackground" format="color|reference" />
        <attr name="otherButtonSingleBackground" format="color|reference" />
        <attr name="cancelButtonTextColor" format="color|reference" />
        <attr name="otherButtonTextColor" format="color|reference" />
        <attr name="actionSheetPadding" format="dimension|reference" />
        <attr name="otherButtonSpacing" format="dimension|reference" />
        <attr name="cancelButtonMarginTop" format="dimension|reference" />
        <attr name="actionSheetTextSize" format="dimension|reference" />
    </declare-styleable>
</resources>

We will save this file in values directory of our application. Above file is used to indicate color reference , spacing , margin of each options. This will be used in our custom class.

Next we will create custom style for our ActionSheet and name it ActionSheetStype.

values/styles.xml

<style name="ActionSheetStyle">
    <item name="actionSheetBackground">@android:color/transparent</item>
    <item name="cancelButtonBackground">@drawable/actionsheet_other_bt_single</item>
    <item name="otherButtonTopBackground">@drawable/actionsheet_other_bt_single</item>
    <item name="otherButtonMiddleBackground">@drawable/actionsheet_other_bt_single</item>
    <item name="otherButtonBottomBackground">@drawable/actionsheet_other_bt_single</item>
    <item name="otherButtonSingleBackground">@drawable/actionsheet_other_bt_single</item>
    <item name="cancelButtonTextColor">#1E82FF</item>
    <item name="otherButtonTextColor">#1E82FF</item>
    <item name="actionSheetPadding">10dp</item>
    <item name="otherButtonSpacing">0dp</item>
    <item name="cancelButtonMarginTop">10dp</item>
    <item name="actionSheetTextSize">16sp</item>
</style>

You can see that we have used drawable xml files which we have created previously for different purposes. We have also specified some colors , padding and text size for our ActonSheet. We can later modify it as per our needs.

As all styling attributes have been created, lets create layout file for our activity from which our ActionSheet will be opened and displayed. Here we will create one button which will open ActionSheet that we will create.

layout/activity_main.xml

<Button
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_centerInParent="true"
    android:text="Show ActionSheet"
    android:id="@+id/btn_actionsheet"/>

By clicking above button we will open our ActionSheet. However we can use other views also to open ActionSheet but for demo purpose we will use a button.

Now all  we have completed the design setup for our ActionSheet and its time to write some code for it.

Custom ActionSheet Class:

We can create ActionSheet in android by extending Fragment also but if you need to integrate ActionSheet in fragment then FragmentManager class will cause some problems. So here we will create Custom ActionSheet for android by extending Dialog class because it provides more customizability.

ActionSheet.java

public class ActionSheet extends Dialog implements View.OnClickListener{
        private  static final int CANCEL_BUTTON_ID =  100 ;
        private static final int BG_VIEW_ID = 10;
        private static final int TRANSLATE_DURATION = 300 ;
        private static final int ALPHA_DURATION =  300 ;
        private Context mContext;
        private Attributes mAttrs;
        private MenuItemClickListener mListener;
        private View mView;
        private LinearLayout mPanel;
        private View mbg;
        private List<String> itemsView;
        private String cancelTitle "" ;
        private boolean  mCancelableOnTouchOutside;
        private boolean  mDismissed = true ;
        private boolean  isCancel = true ;

        public ActionSheet(Context context){
            super (context, android.R.style.Theme_Light_NoTitleBar); // Full Screen
            this .mContext = context;
            initViews();
            getWindow ().setGravity (Gravity.BOTTOM);
            Drawable drawable = new ColorDrawable();
            drawable.setAlpha ( 0 ); // set transparent background
            getWindow().setBackgroundDrawable(drawable);
        }

        public void  initViews(){
            /* Hide soft keyboard*/
            InputMethodManager imm = (InputMethodManager)mContext.getSystemService (Context.INPUT_METHOD_SERVICE);
            if(imm.isActive()){
                View focusView = ((Activity) mContext).getCurrentFocus();
                if(focusView != null)
                    imm.hideSoftInputFromWindow(focusView.getWindowToken (),0);
            }
            mAttrs = readAttribute(); // Get themes properties
            mView = createView();
            mbg.startAnimation (createAlphaInAnimation());
            mPanel.startAnimation (createTranslationInAnimation());
        }

        private Animation createTranslationInAnimation(){
            int type = TranslateAnimation.RELATIVE_TO_SELF;
            TranslateAnimation  translateAnimation =  new  TranslateAnimation (type,0,type,0,type,1,type,0);
            translateAnimation.setDuration (TRANSLATE_DURATION);
            return  translateAnimation;
        }

        private  Animation createAlphaInAnimation(){
            AlphaAnimation alphaAnimation = new AlphaAnimation (0,1);
            alphaAnimation.setDuration (ALPHA_DURATION);
            return  alphaAnimation;
        }
        private Animation createTranslationOutAnimation(){
            int type = TranslateAnimation.RELATIVE_TO_SELF;
            TranslateAnimation translateAnimation = new  TranslateAnimation (type,0,type,0,type,0,type,1);
            translateAnimation.setDuration(TRANSLATE_DURATION);
            translateAnimation.setFillAfter(true);
            return  translateAnimation;
        }

        private Animation createAlphaOutAnimation(){
            AlphaAnimation  alphaAnimation = new AlphaAnimation(1,0);
            alphaAnimation.setDuration(ALPHA_DURATION);
            alphaAnimation.setFillAfter(true);
            return  alphaAnimation;
        }
        /*Create a basic background view*/
        private View createView(){
            FrameLayout  parent = new FrameLayout(mContext);
            FrameLayout.LayoutParams  parentParams = new FrameLayout.LayoutParams (ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
            parentParams.gravity = Gravity.BOTTOM;
            parent.setLayoutParams (parentParams);
            mbg = new View(mContext);
            mbg.setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
            mbg.setBackgroundColor (Color.argb (136,0,0,0));
            mbg.setId(ActionSheet.BG_VIEW_ID);
            mbg.setOnClickListener(this);
            mPanel = new LinearLayout (mContext);
            FrameLayout.LayoutParams  mPanelParams = new FrameLayout.LayoutParams(FrameLayout.LayoutParams.MATCH_PARENT, FrameLayout.LayoutParams.WRAP_CONTENT);
            mPanelParams.gravity = Gravity.BOTTOM;
            mPanel.setLayoutParams (mPanelParams);
            mPanel.setOrientation (LinearLayout.VERTICAL);
            parent.addView (mbg);
            parent.addView (mPanel);
            return  parent;
        }
        /*Create a MenuItem*/
        private void createItems() {
            if (itemsView != null && itemsView.size() > 0) {
                for (int i = 0; i < itemsView.size(); i++) {
                    Button btn = new Button(mContext);
                    btn.setId(CANCEL_BUTTON_ID + i + 1);
                    btn.setOnClickListener(this);
                    btn.setBackgroundDrawable(getOtherButtonBg(itemsView.toArray(new String[itemsView.size()]),i));
                    btn.setText(itemsView.get(i));
                    btn.setTextColor(mAttrs.otherButtonTextColor);
                    btn.setTextSize(TypedValue.COMPLEX_UNIT_PX, mAttrs.actionSheetTextSize);
                    if(i > 0)
                    {
                        LinearLayout.LayoutParams params = createButtonLayoutParams();
                        params.topMargin = mAttrs.otherButtonSpacing;
                        mPanel.addView(btn, params);
                    }else {
                        mPanel.addView(btn);
                    }
                }
                Button btn1 = new Button(mContext);
                btn1.getPaint().setFakeBoldText(true);
                btn1.setTextSize(TypedValue.COMPLEX_UNIT_PX, mAttrs.actionSheetTextSize);
                btn1.setId(ActionSheet.CANCEL_BUTTON_ID);
                btn1.setBackgroundDrawable(mAttrs.cancelButtonBackground);
                btn1.setText(cancelTitle);
                btn1.setTextColor(mAttrs.cancelButtonTextColor);
                btn1.setOnClickListener(this);
                LinearLayout.LayoutParams params = createButtonLayoutParams();
                params.topMargin = mAttrs.cancelButtonMarginTop;
                mPanel.addView(btn1, params);
                mPanel.setBackgroundDrawable(mAttrs.background);
                mPanel.setPadding(mAttrs.padding, mAttrs.padding, mAttrs.padding, mAttrs.padding);
            }
        }

        public  LinearLayout.LayoutParams createButtonLayoutParams(){
            LinearLayout.LayoutParams  params = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
            return  params;
        }
        /*
         The color of the item button
         @param titles
         @param i
         @return*/
        private Drawable getOtherButtonBg(String [] titles, int i){
            if(titles.length == 1) {
                return mAttrs.otherButtonSingleBackground;
            }else if(titles.length ==  2 ){
                switch(i){
                case 0:
                    return mAttrs.otherButtonTopBackground;
                case 1:
                    return mAttrs.otherButtonBottomBackground;
                }
            }else if(titles.length>  2 ){
                if(i == 0)
                    return mAttrs.otherButtonTopBackground;
                else if(i == (titles.length - 1))
                    return mAttrs.otherButtonBottomBackground;
                else
                    return mAttrs.getOtherButtonMiddleBackground ();
            }
            return null ;
        }

        public void showMenu(){
            if(!mDismissed)
                return ;
            show();
            getWindow().setContentView(mView);
            mDismissed = false ;
        }
        /*
        * The dismiss menu
        */
        public void dismissMenu(){
            if(mDismissed)
                return ;
            dismiss();
            onDismiss();
            mDismissed = true ;
        }

        private void onDismiss(){
            mPanel.startAnimation (createTranslationOutAnimation ());
            mbg.startAnimation (createAlphaOutAnimation ());
        }
        /*
        * Cancel the caption text of the button
        *
        * @param title
        * @return
        */
        public ActionSheet setCancelButtonTitle (String title){
            this.cancelTitle = title;
            return this;
        }

        /*
        * whether the outer edge can be canceled
        *
        * @param cancelable
        * @return
        */
        public ActionSheet setCancelableOnTouchMenuOutside(Boolean cancelable){
            mCancelableOnTouchOutside = cancelable;
            return this;
        }

        public ActionSheet addItems(String...titles){
            if(titles == null || titles.length == 0)
                return this;
            itemsView = Arrays.asList(titles);
            createItems();
            return this;
        }
        public ActionSheet setItemClickListener(MenuItemClickListener listener){
            this.mListener = listener;
            return this;
        }

    private Attributes readAttribute() {
        Attributes attrs = new Attributes(mContext);
        TypedArray a = mContext.getTheme().obtainStyledAttributes(null,
                R.styleable.ActionSheet, R.attr.actionSheetStyle, 0);
        Drawable background = a
                .getDrawable(R.styleable.ActionSheet_actionSheetBackground);
        if (background != null) {
            attrs.background = background;
        }
        Drawable cancelButtonBackground = a
                .getDrawable(R.styleable.ActionSheet_cancelButtonBackground);
        if (cancelButtonBackground != null) {
            attrs.cancelButtonBackground = cancelButtonBackground;
        }
        Drawable otherButtonTopBackground = a
                .getDrawable(R.styleable.ActionSheet_otherButtonTopBackground);
        if (otherButtonTopBackground != null) {
            attrs.otherButtonTopBackground = otherButtonTopBackground;
        }
        Drawable otherButtonMiddleBackground = a
                .getDrawable(R.styleable.ActionSheet_otherButtonMiddleBackground);
        if (otherButtonMiddleBackground != null) {
            attrs.otherButtonMiddleBackground = otherButtonMiddleBackground;
        }
        Drawable otherButtonBottomBackground = a
                .getDrawable(R.styleable.ActionSheet_otherButtonBottomBackground);
        if (otherButtonBottomBackground != null) {
            attrs.otherButtonBottomBackground = otherButtonBottomBackground;
        }
        Drawable otherButtonSingleBackground = a
                .getDrawable(R.styleable.ActionSheet_otherButtonSingleBackground);
        if (otherButtonSingleBackground != null) {
            attrs.otherButtonSingleBackground = otherButtonSingleBackground;
        }
        attrs.cancelButtonTextColor = a.getColor(
                R.styleable.ActionSheet_cancelButtonTextColor,
                attrs.cancelButtonTextColor);
        attrs.otherButtonTextColor = a.getColor(
                R.styleable.ActionSheet_otherButtonTextColor,
                attrs.otherButtonTextColor);
        attrs.padding = (int) a.getDimension(
                R.styleable.ActionSheet_actionSheetPadding, attrs.padding);
        attrs.otherButtonSpacing = (int) a.getDimension(
                R.styleable.ActionSheet_otherButtonSpacing,
                attrs.otherButtonSpacing);
        attrs.cancelButtonMarginTop = (int) a.getDimension(
                R.styleable.ActionSheet_cancelButtonMarginTop,
                attrs.cancelButtonMarginTop);
        attrs.actionSheetTextSize = a.getDimensionPixelSize(R.styleable.ActionSheet_actionSheetTextSize, (int) attrs.actionSheetTextSize);
        a.recycle();
        return attrs;
    }
    @Override
    public void onClick(View v){
        if(v.getId () == ActionSheet.BG_VIEW_ID &&! mCancelableOnTouchOutside)
        return ;
        dismissMenu ();
        if(v.getId() != ActionSheet.CANCEL_BUTTON_ID && v.getId() != ActionSheet.BG_VIEW_ID){
        if(mListener != null)
            mListener.onItemClick(v.getId () - CANCEL_BUTTON_ID - 1);
        isCancel = false;
        }
    }
        /*
        * Custom properties for control themes
        *
        */
    private class  Attributes {
            private Context mContext;
            private Drawable background;
            private Drawable cancelButtonBackground;
            private Drawable otherButtonTopBackground;
            private Drawable otherButtonMiddleBackground;
            private Drawable otherButtonBottomBackground;
            private Drawable otherButtonSingleBackground;
            private int cancelButtonTextColor;
            private int otherButtonTextColor;
            private int padding;
            private int otherButtonSpacing;
            private int cancelButtonMarginTop;
            private float actionSheetTextSize;
            public Attributes(Context context) {
                this.mContext = context;
                this.background = new ColorDrawable(Color.TRANSPARENT);
                this.cancelButtonBackground = new ColorDrawable(Color.BLACK);
                ColorDrawable gray = new ColorDrawable(Color.GRAY);
                this.otherButtonTopBackground = gray;
                this.otherButtonMiddleBackground = gray;
                this.otherButtonBottomBackground = gray;
                this.otherButtonSingleBackground = gray;
                this.cancelButtonTextColor = Color.WHITE;
                this.otherButtonTextColor = Color.BLACK;
                this.padding = dp2px(20);
                this.otherButtonSpacing = dp2px(2);
                this.cancelButtonMarginTop = dp2px(10);
                this.actionSheetTextSize = dp2px(16);
            }
            private int dp2px(int DP) {
                return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, DP, mContext.getResources().getDisplayMetrics());
            }
            public Drawable getOtherButtonMiddleBackground() {
                if (otherButtonMiddleBackground instanceof StateListDrawable) {
                    TypedArray a = mContext.getTheme().obtainStyledAttributes(null,
                            R.styleable.ActionSheet, R.attr.actionSheetStyle, 0);
                    otherButtonMiddleBackground = a
                            .getDrawable(R.styleable.ActionSheet_otherButtonMiddleBackground);
                    a.recycle();
                }
                return otherButtonMiddleBackground;
            }
        }
    public static interface  MenuItemClickListener{
        void  onItemClick(int itemPosition);
    }
}

As we can see that we have used the Dialog class to create our custom ActionSheet which is more suitable to integrate in any activity or fragment.

Starting from first method initViews() this method is used to initialize our variable and views so that we can use it in different methods.  Below that we have created createTranslationInAnimation(), createAlphaInAniation(), createTranslationOutAnimation() and createAlphaOutAnimation() methods which is responsible to animate our layout from bottom. Translation methods are used in our Views to animate and Alpha methods are used to animate our parent layout which includes views in it.

Next we have created createItems() method which is used to create our Buttons in ActionSheet. It will create number of buttons provided in string array that we pass when calling this view. Below this method we have created getOtherButtonBg() which is responsible to assign background for buttons reading from our attribute class.

At bottom of ActionSheet class we have created Attribute class. This class will initialize the background variables for buttons. These variables will be used to read the styling attributes from our styles.xml file which we have already created. This class itself can not assign background attributes to our views so that we have created a method called readAttributes(). This method uses our Attribute class to get variable references and assign background attribute to our views from styles.xml file. Method also reads the attrs.xml file for other padding and margin of views inside our ActionSheet.

Finally we have created the interface which is used to identify which button is clicked by the user. It contains onItemClick(int itemPosition) interface method. This method returns item’s position which is clicked by user so that we can easily perform desired actions on specific button click.

The Activity:

We have created all required files and custom ActionSheet class above but it cannot be called itself so we need to create Activity or Fragment which will inherit the interface of our  ActionSheet class and call it from UI. We have already created the xml file for our MainActivity which contains a button to call ActionSheet class. Here we will call it in button click event of our MainActivity.

MainActivity.java

public class MainActivity extends AppCompatActivity implements ActionSheet.MenuItemClickListener{
    Button btnActionsheet;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        btnActionsheet = (Button)findViewById(R.id.btn_actionsheet);
        btnActionsheet.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                setTheme(R.style.ActionSheetStyle);
                showActionSheet();
            }
        });
    }
    public void showActionSheet() {
        ActionSheet actionSheet = new ActionSheet(this);
        actionSheet.setCancelButtonTitle("Cancel");
        actionSheet.addItems("Cricket","Football","Hockey","BasketBall");
        actionSheet.setItemClickListener(this);
        actionSheet.setCancelableOnTouchMenuOutside(true);
        actionSheet.showMenu();
    }
    @Override
    public void onItemClick(int itemPosition) {
        Toast.makeText(MainActivity.this,"Item "+itemPosition+" Clicked",Toast.LENGTH_SHORT).show();
    }
}

We have implemented the MenuItemClickListener interface in our activity as above which will provide it’s interface method onItemClick() to identify the position of button clicked in ActionSheet by the user.

Notice the method showActionSheet() , inside this method we have created the object of our ActionSheet class and called other required methods. AddItems(…) method will accept array of string. We can pass number of strings in this method. It will consider each string as title of our ActionSheet button and create number of buttons with number of string. Here each string considered as each button. ShowMenu() will display the ActionSheet animating from bottom of the screen. Clicking outside the ActionSheet will hide the view by animating it.

This example will display the ActionSheet same as iOS. However we can customize all the styling attributes, images and colors as our need. The view can be used in any activity or fragment easily all we need is implement it’s interface and call it’s method as when we want to display.

Application Screens :

1 2 3

The codes shared by experts of android app development team in India are for reference only. You can follow the instruction and use ActionSheet in android just like professionals. Don’t forget to feedback about this post. Readers want to know your reaction.



Vijay is a compulsive blogger who likes to educate like-minded people on various new technologies and trends. He works with Aegis SoftTech as a software developer and has been developing software for years. Stay Connected to him on Facebook and Google+.