Easiest Way to Create Contextual Menu in Android

Author - Rajnit

Here you can find an easy and awesome way to create a Contextual Menu in Android. This menu is specially designed for long press anywhere on the screen and menu popup came up with circular menus, just drag circle and select menu.

Android contextual Menu

How easy it looks, also easy for implementation.

Here we create a demo app and implement a contextual menu in android for sharing the purpose. You can also use this menu for various actions.

How code works?

When long press detects on screen show contextual menu, and pass touch event to dragging circle and move menu view to the touched position.

As circle move around menu items check for the collision of the circle with each menu item. If any menu is overlap or touch by dragging circle then redraw menu layout with the touched menu item(the selected menu is shown with text).

When a user removes touch from the screen then if any menu is touched by dragging circle then trigger event for a menu is selected or if no menu selected then trigger no menu selected, then hide menu layout.

Let’s Understand Implementation of Contextual Menu in Android

First clone demo project from this Repo and follow below steps.

You can find, CircleDrag class is used for dragging circle implementation. This class contains logic for interaction with menu items and return callback to MainActivity using OnViewCrossed interface to redraw menu items.

Check MainActivity.java

First we initialize CircleDrag class.

private void initCircleDrag() {
   final int radius = (int) getResources().getDimension(R.dimen._80sdp);
   final CircleDrag circleDrag = new CircleDrag(this);
   circleDrag.init(viewCircle, this, radius, indicatorViewList);
}

Below method is used to create dynamic menu items. Just pass menu item name and icon.

public void createMenuItem(String text, @DrawableRes int drawableId) {
   textViewList.add(createTextView(relativeParent, text));
   menuItemViewList.add(createMenuItem(rlCenterView, drawableId));
   indicatorViewList.add(createMenuItem(rlCenterView, R.drawable.trans_circle));

   DOTS_COUNT = textViewList.size();
}

Below method is used to move menu layout to touched position.

public void setMenuItemsPosition(View parentView, View menuLayoutView, float x, float y) {

   //p for parent
   float pl, pr, pb, pt;
   //c for child
   float cl, cr, cb, ct;

   int[] v1_coords = new int[2];
   parentView.getLocationOnScreen(v1_coords);

   pl = 0;
   pr = parentView.getWidth();
   pt = 0;
   pb = parentView.getHeight();

   cl = menuLayoutView.getX();
   ct = menuLayoutView.getY();
   cr = menuLayoutView.getWidth() + cl;
   cb = menuLayoutView.getHeight() + ct;

   if (cl < pl) {
       if (ct < pt && cl < pl) {
           setMenusPosition(Angle.LEFT_TOP);
       } else if (cb > pb && cl < pl) {
           setMenusPosition(Angle.LEFT_BOTTOM);
       } else {
           setMenusPosition(Angle.LEFT);
       }
   } else if (cr > pr) {
       if (ct < pt && cr > pr) {
           setMenusPosition(Angle.RIGHT_TOP);
       } else if (cb > pb && cr > pr) {
           setMenusPosition(Angle.RIGHT_BOTTOM);
       } else {
           setMenusPosition(Angle.RIGHT);
       }
   } else if (ct < pt) {
       setMenusPosition(Angle.TOP);
   } else if (cb > pb) {
       setMenusPosition(Angle.BOTTOM);
   } else {
       setMenusPosition(Angle.CENTER);
   }

}

Below method is used to draw menu items circle based on the angle. If user touch in center menu items draws in round shape, if user touch on sides then menu items draw in half of the circle and if user touch on any angle of the screen then menu items draw in fourth of the circle.

private void setMenusPosition(Angle angle);

Below listener is used to show contextual menu view when long press is performed.

relativeParent.setOnLongClickListener(new View.OnLongClickListener() {
   @Override
   public boolean onLongClick(View v) {
       return false;
   }
});

final GestureDetector gestureDetector = new GestureDetector(this, new GestureDetector.SimpleOnGestureListener() {
   public void onLongPress(MotionEvent e) {
       Log.e("GestureDetector", "Longpress detected");

       if (setVisible) {
           rlMenuView.setVisibility(View.VISIBLE);
           rlTouchLayout.setBackgroundResource(R.color.menu_background);
           isLongPressDetected = true;
       }

   }
});
Below listener is used to detect touch and draw menu items around touched point.
relativeParent.setOnTouchListener(new View.OnTouchListener() {

   @Override
   public boolean onTouch(View v, MotionEvent event) {

       if (event.getX() >= getResources().getDimension(R.dimen._10sdp) && event.getX() <= relativeParent.getWidth() - getResources().getDimension(R.dimen._10sdp) &&
               event.getY() >= getResources().getDimension(R.dimen._10sdp) && event.getY() <= relativeParent.getHeight() - getResources().getDimension(R.dimen._10sdp)) {
           setVisible = true;
           //pass touch event

           viewCircle.dispatchTouchEvent(event);

           switch (event.getAction()) {

               case MotionEvent.ACTION_DOWN:

                   lastSelectedPosition = -1;

                   float centerX = event.getX() - rlMenuView.getWidth() / 2;
                   float centerY = event.getY() - rlMenuView.getHeight() / 2;

                   rlMenuView.setX(centerX);
                   rlMenuView.setY(centerY);

                   setMenuItemsPosition(relativeParent, rlMenuView, centerX, centerY);

                   break;
               case MotionEvent.ACTION_MOVE:

                   break;

               case MotionEvent.ACTION_UP:

                   if (lastSelectedPosition != -1) {
                       onMenuSelected(lastSelectedPosition, textViewList.get(lastSelectedPosition).getText().toString());
                   } else {
                       onNoMenuSelected();
                   }

                   setVisible = false;
                   setMenusPosition(Angle.CENTER);
                   rlMenuView.setVisibility(View.INVISIBLE);
                   rlTouchLayout.setBackgroundResource(R.color.tranparent);
                   hideTextViews();

                   break;
           }

       } else {

           if (event.getAction() == MotionEvent.ACTION_UP) {

               onNoMenuSelected();

               setVisible = false;
               setMenusPosition(Angle.CENTER);
               rlMenuView.setVisibility(View.INVISIBLE);
               rlTouchLayout.setBackgroundResource(R.color.tranparent);
               hideTextViews();
           }

       }

       return false;
   }

});

Below method is used to redraw menu items and menu text when item is selected or not selected.

@Override
public void OnViewTouched(int flag) {
   Log.d(TAG, "onSelect: ");

   lastSelectedPosition = -1;

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

       if (flag == indicatorViewList.get(i).getId()) {
           lastSelectedPosition = i;

           if ((int) pos_center_view[0] > (int) indicatorViewList.get(i).getX()
                   && (pos_center_view[0] - indicatorViewList.get(i).getX() > 4)) {
               menuItemViewList.get(i).setX(indicatorViewList.get(i).getX() - getResources().getDimension(R.dimen._6sdp));
           } else if ((int) pos_center_view[0] < (int) indicatorViewList.get(i).getX()
                   && (indicatorViewList.get(i).getX() - pos_center_view[0] > 4)) {
               menuItemViewList.get(i).setX(indicatorViewList.get(i).getX() + getResources().getDimension(R.dimen._6sdp));
           } else {
               menuItemViewList.get(i).setX(indicatorViewList.get(i).getX());
           }

           if ((int) pos_center_view[1] > (int) indicatorViewList.get(i).getY()
                   && (pos_center_view[1] - indicatorViewList.get(i).getY() > 4)) {
               menuItemViewList.get(i).setY(indicatorViewList.get(i).getY() - getResources().getDimension(R.dimen._6sdp));
           } else if ((int) pos_center_view[1] < (int) indicatorViewList.get(i).getY()
                   && (indicatorViewList.get(i).getY() - pos_center_view[1] > 4)) {
               menuItemViewList.get(i).setY(indicatorViewList.get(i).getY() + getResources().getDimension(R.dimen._6sdp));
           } else {
               menuItemViewList.get(i).setY(indicatorViewList.get(i).getY());
           }

           textViewList.get(i).setVisibility(View.VISIBLE);

       } else {
           menuItemViewList.get(i).setX(indicatorViewList.get(i).getX());
           menuItemViewList.get(i).setY(indicatorViewList.get(i).getY());

           textViewList.get(i).setVisibility(View.INVISIBLE);
       }
   }

}

Below methods are called when menu item is selected or nothing is selected.
@Override
public void onMenuSelected(int position, String selectedText) {
   tvResult.setText(String.format("Selected Item : %s", selectedText));
}

@Override
public void onNoMenuSelected() {
   // TODO when no menu selected.....
   tvResult.setText(String.format("Selected Item : %s", "None"));
}

You have implemented contextual menu.

If you have any query, feel free to comment below.

Don’t miss the next post!

Loading

Related Posts