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.
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; } } });
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); } } }
@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.