I have made an Activity
called Accounts
and I want to add a horizontal ContextMenu
. This may look like the cut, copy and paste options. Is there any way to add this horizontal custom menu onLongClick
on the list items?
Here's what I've got so far.
@Override
public void onCreateContextMenu(ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo) {
super.onCreateContextMenu(menu, v, menuInfo);
LayoutInflater inflater = getLayoutInflater().from(this);
View view = inflater.inflate(R.layout.custom_listview, null, false);
menu.setHeaderView(view);
menu.add("Delete");
menu.add("Edit");
}
@Override
public boolean onContextItemSelected(MenuItem item) {
AdapterView.AdapterContextMenuInfo info = (AdapterView.AdapterContextMenuInfo) item.getMenuInfo();
int position = info.position;
View v = listView.getChildAt(position);
TextView typeTv = v.findViewById(R.id.custom_listview_type);
TextView userTv = v.findViewById(R.id.custom_listview_user);
TextView passTv = v.findViewById(R.id.custom_listview_password);
if (item.getTitle().equals("Delete")) {
db.execSQL("delete from user_added_accounts where accountType = '" + typeTv.getText().toString() + "' and username = '" + userTv.getText().toString() + "';");
recreate();
}
if (item.getTitle().equals("Edit")) {
update(typeTv.getText().toString(), userTv.getText().toString(), passTv.getText().toString());
}
return true;
}
And the current UI looks like this.
Here is something like I want,
Simply you can achieve by QuickAction library.
https://github.com/piruin/quickaction
https://github.com/lorensiuswlt/NewQuickAction
Hope this will help you!!
I think the thing you need is the PopupWindow
. Its easier to implement and has its custom layout setting option. The PopupWindow
can be set in custom position as you wish and the idea of implementing a sample copy/paste UI that you are thinking of, can be served with the implementation of PopupWindow
as well.
I found this answer very informative if you want to implement your situation with PopupWindow
instead of implementing it with context menu.
In the above answer that I mentioned and provided a like to, has a PopupWindow
which has a TextView
only. You might implement any custom/complex UI instead of having a simple TextView
like its shown there.
I hope that helps.
Update
As asked in the comment that getting locations of the position of PopupWindow
can be set dynamically as well. I am referring to another link, so that you can check the implementation from there as well.
Here's the implementation of using PopupWindow
in a list.
I used Dialog for achieving this, still searching for the standard way.Please post a better way.
Test.java
public void showOptionMenu(View view) {
int x1 = (int) view.getX();
int y1 = (int) view.getY();
LayoutInflater inflater = LayoutInflater.from(MainActivity.this);
View v = inflater.inflate(R.layout.popup_window , null);
Dialog dialog = new Dialog(MainActivity.this);
dialog.requestWindowFeature(Window.FEATURE_NO_TITLE);
WindowManager.LayoutParams wmlp = dialog.getWindow().getAttributes();
wmlp.gravity = Gravity.TOP | Gravity.LEFT;
wmlp.x = x1; //x position
wmlp.y = y1; //y position
dialog.setContentView(v);
dialog.show();
}
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/activity_main_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.example.shiva.testacitivity.MainActivity">
<Button
android:text="Show Options"
android:onClick="showOptionMenu"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="10dp"/>
</LinearLayout>
popup_windows.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="center"
android:background="#ECEFF1"
android:padding="5dp"
android:gravity="center"
android:orientation="horizontal">
<TextView
android:id="@+id/edit"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:drawableLeft="@drawable/edit"
android:layout_gravity="center"
android:gravity="center"
android:textColor="#000"
android:text="Edit" />
<View
android:layout_width="1dp"
android:layout_height="match_parent"/>
<TextView
android:id="@+id/delete"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:gravity="center"
android:drawableLeft="@drawable/delete"
android:text="Delete"
android:textColor="#000" />
</LinearLayout>
And it's result
So , I wrote below code few year back. You need to make two class first is PopUp
and second is TringleView
.
PopUp :- Make dialog box and open into near your view(where you want to open dialog). You can change popup bg color .
TringleView :- Make tringle view or you can say pointed arrow. You can change pointed arrow bg color .
View contentView = ((FragmentActivity)v.getContext()).getLayoutInflater().inflate(R.layout.edit_delete_layout,getAttachedRecyclerView(),false);
// this view denote where you click or you want to open dialog near
PopUp.showPopupOnView(((FragmentActivity) v.getContext()).getSupportFragmentManager(),contentView,view,false);
PopUp.class
public class PopUp extends DialogFragment {
protected int targetX;
protected int targetY;
protected int targetWidth;
protected int targetHeight;
protected Bitmap targetViewImage;
protected View contentView;
private SmartWorksPopUpViewHolder fragmentViewHolder;
private static int bgDrawable = R.drawable.round_corner_white_bg;
protected static int ONE_DIP;
private static int arrowBgColor = R.color.border_color;
private static int arrowWidthMultiple = 25;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (ONE_DIP == 0) {
ONE_DIP = (int) TypedValue.applyDimension(
TypedValue.COMPLEX_UNIT_DIP, 1, getResources()
.getDisplayMetrics());
}
setStyle(DialogFragment.STYLE_NO_TITLE, android.R.style.Theme_Translucent);
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
AbsoluteLayout parent = new AbsoluteLayout(getActivity());
parent.setId(R.id.parentLayout);
return parent;
}
@Override
public void onViewCreated(View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
this.fragmentViewHolder = createViewHolder(view);
bindView(fragmentViewHolder);
}
protected SmartWorksPopUpViewHolder createViewHolder(
View fragmentView) {
return new SmartWorksPopUpViewHolder(fragmentView, contentView);
}
private void bindView(SmartWorksPopUpViewHolder vh) {
if (fragmentViewHolder != null) {
setupTargetDummyView(vh);
boolean showOnTop = shouldShowOnTop();
setupArrow(vh, showOnTop);
setupContent(vh, showOnTop);
}
}
protected void setupContent(SmartWorksPopUpViewHolder vh, boolean showOnTop) {
final int y;
AbsoluteLayout.LayoutParams arrowParams = (android.widget.AbsoluteLayout.LayoutParams) vh.arrow
.getLayoutParams();
int measureHeight = View.MeasureSpec.makeMeasureSpec(
ViewGroup.LayoutParams.WRAP_CONTENT, View.MeasureSpec.UNSPECIFIED);
int measureWidth = View.MeasureSpec.makeMeasureSpec(
getActivity().getWindow().getDecorView().getWidth(), View.MeasureSpec.EXACTLY);
vh.popupView.measure(measureWidth, measureHeight);
if (showOnTop) {
y = this.targetY - vh.popupView.getMeasuredHeight() + ONE_DIP;
} else {
y = arrowParams.y + arrowParams.height - ONE_DIP * 2;
}
updateAbsoluteLayoutParams(
getActivity().getResources().getDimensionPixelOffset(R.dimen.sixty_dp),
y,
getActivity().getWindow().getDecorView().getWidth() -
getActivity().getResources().getDimensionPixelOffset(R.dimen.seventy_dp),
ViewGroup.LayoutParams.WRAP_CONTENT, vh.popupView);
vh.parent.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
exit();
}
});
}
private void setupArrow(SmartWorksPopUpViewHolder vh, boolean showOnTop) {
final int arrowHeight = 15 * ONE_DIP;
final int arrowWidth = arrowWidthMultiple * ONE_DIP;
vh.arrow.setDirectionAndColor(showOnTop ? "down" : "top", vh.popupView.getContext().getResources().getColor(arrowBgColor));
final int x = (int) (targetX + targetWidth / 3 - arrowWidth / 2);
final int y = targetY + (showOnTop ? -arrowHeight : targetHeight);
updateAbsoluteLayoutParams(x, y, arrowWidth, arrowHeight, vh.arrow);
}
private void setupTargetDummyView(SmartWorksPopUpViewHolder vh) {
vh.targetViewDummy.setImageBitmap(targetViewImage);
updateAbsoluteLayoutParams(targetX, targetY, targetWidth, targetHeight, vh.targetViewDummy);
}
protected void updateAbsoluteLayoutParams(int x, int y, int width, int height, View view) {
AbsoluteLayout.LayoutParams layoutParams =
(android.widget.AbsoluteLayout.LayoutParams) view.getLayoutParams();
layoutParams.x = x;
layoutParams.y = y;
layoutParams.height = height;
layoutParams.width = width;
view.setLayoutParams(layoutParams);
}
private boolean shouldShowOnTop() {
int windowHeight = getActivity().getWindow().getDecorView().getHeight();
int windowMid = windowHeight / 4;
return targetY > windowMid;
}
@Override
public void onDestroyView() {
this.fragmentViewHolder = null;
super.onDestroyView();
}
protected static class SmartWorksPopUpViewHolder {
protected AbsoluteLayout parent;
protected View popupView;
protected TringleView arrow;
protected AppCompatImageView targetViewDummy;
protected SmartWorksPopUpViewHolder(View fragmentView, View content) {
this.parent = (AbsoluteLayout) fragmentView;
final Context mContext = fragmentView.getContext();
this.popupView = content;
this.arrow = new TringleView(mContext);
this.targetViewDummy = new SmartWorksAppCompactImageView(mContext);
this.parent.addView(popupView);
this.parent.addView(arrow);
this.parent.addView(targetViewDummy);
this.parent.setBackgroundColor(0x00000000);
content.setBackgroundResource(bgDrawable);
}
}
public static PopUp showPopupOnView(FragmentManager fm, View contentView, View targetView, boolean showTargetView) {
int[] location = new int[2];
targetView.getLocationInWindow(location);
PopUp fragment = new PopUp();
fragment.targetX = location[0];
fragment.targetY = (int) (location[1] - TypedValue
.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 25,
targetView.getResources().getDisplayMetrics()));
fragment.targetWidth = targetView.getMeasuredWidth();
fragment.targetHeight = targetView.getMeasuredHeight();
fragment.contentView = contentView;
fragment.show(fm, "offer");
return fragment;
}
public void exit() {
dismiss();
}
public static void setArrowBackgroundColor(int color) {
arrowBgColor = color;
}
public static void setArrowWidthMultiple(int arrowWidth) {
arrowWidthMultiple = arrowWidth;
}
}
TringleView.class
public class TringleView extends View {
private String direction;
private int color;
public TringleView(Context context) {
super(context);
setDirectionAndColor("right", Color.RED);
}
public TringleView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
setDirectionAndColor(attrs.getAttributeValue(null, "direction"), Color.RED);
}
public TringleView(Context context, AttributeSet attrs) {
super(context, attrs);
setDirectionAndColor(attrs.getAttributeValue(null, "direction"), Color.RED);
}
public void setDirectionAndColor(String direction, int color) {
if (direction != null && !direction.equals(this.direction) || this.color != color) {
createTriangleDrawable(direction, color);
}
}
private void createTriangleDrawable(String string, int color) {
int width = MeasureSpec.makeMeasureSpec(30, MeasureSpec.UNSPECIFIED);
int height = MeasureSpec.makeMeasureSpec(20, MeasureSpec.UNSPECIFIED);
Path path = new Path();
if (string == null) {
string = "right";
}
if (string.equals("top")) {
path.moveTo(0, height);
path.lineTo(width / 2, 0);
path.lineTo(width, height);
} else if (string.equals("left")) {
path.moveTo(width, 0);
path.lineTo(0, height / 2);
path.lineTo(width, height);
} else if (string.equals("right")) {
path.moveTo(0, 0);
path.lineTo(width, height / 2);
path.lineTo(0, height);
} else if (string.equals("down")) {
path.moveTo(0, 0);
path.lineTo(width / 2, height);
path.lineTo(width, 0);
}
path.close();
ShapeDrawable shapeDrawable = new ShapeDrawable(new PathShape(path, width, height));
shapeDrawable.getPaint().setColor(color);
setBackground(shapeDrawable);
this.color = color;
this.direction = string;
}
}
edit_delete_layout.xml
<?xml version="1.0" encoding="utf-8"?>
<android.support.v7.widget.CardView
app:cardCornerRadius="@dimen/five_dp"
android:layout_margin="@dimen/ten_dp"
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
app:layout_behavior="android.support.design.widget.BottomSheetBehavior"
android:layout_width="match_parent"
android:layout_height="wrap_content" >
<LinearLayout
app:layout_behavior="android.support.design.widget.BottomSheetBehavior"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:weightSum="5"
android:gravity="center"
android:orientation="horizontal"
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<sis.com.smartworks.widget.SmartWorksTextView
android:id="@+id/share"
android:textSize="@dimen/smallest_text_size"
android:textColor="@color/black"
android:visibility="visible"
android:gravity="center"
android:paddingRight="@dimen/four_dp"
android:paddingLeft="@dimen/four_dp"
android:paddingTop="@dimen/ten_dp"
android:paddingBottom="@dimen/ten_dp"
android:text="Share"
android:layout_weight="1"
android:textStyle="bold"
android:layout_width="0dp"
android:layout_height="wrap_content" />
<View
android:layout_width="1dp"
android:background="@color/grey_unselect"
android:layout_height="match_parent" />
<sis.com.smartworks.widget.SmartWorksTextView
android:id="@+id/reportSpam"
android:textSize="@dimen/smallest_text_size"
android:textColor="@color/black"
android:visibility="visible"
android:gravity="center"
android:paddingRight="@dimen/four_dp"
android:paddingLeft="@dimen/four_dp"
android:paddingTop="@dimen/ten_dp"
android:paddingBottom="@dimen/ten_dp"
android:text="Spam"
android:layout_weight="1"
android:textStyle="bold"
android:layout_width="0dp"
android:layout_height="wrap_content" />
<View
android:layout_width="1dp"
android:background="@color/grey_unselect"
android:layout_height="match_parent" />
<!--<View-->
<!--android:layout_width="match_parent"-->
<!--android:layout_marginLeft="@dimen/three_dp"-->
<!--android:layout_marginRight="@dimen/three_dp"-->
<!--android:background="@color/white"-->
<!--android:layout_height="@dimen/one_dp" />-->
<sis.com.smartworks.widget.SmartWorksTextView
android:id="@+id/edit"
android:textSize="@dimen/smallest_text_size"
android:textColor="@color/black"
android:gravity="center"
android:paddingRight="@dimen/four_dp"
android:paddingLeft="@dimen/four_dp"
android:paddingTop="@dimen/ten_dp"
android:paddingBottom="@dimen/ten_dp"
app:swFontName="robotoNormal"
android:layout_weight="1"
android:text="@string/edit"
android:textStyle="bold"
android:layout_width="0dp"
android:layout_height="wrap_content" />
<View
android:layout_width="1dp"
android:background="@color/grey_unselect"
android:layout_height="match_parent" />
<!--<View-->
<!--android:layout_width="match_parent"-->
<!--android:layout_marginLeft="@dimen/three_dp"-->
<!--android:layout_marginRight="@dimen/three_dp"-->
<!--android:background="@color/white"-->
<!--android:layout_height="@dimen/one_dp" />-->
<sis.com.smartworks.widget.SmartWorksTextView
android:id="@+id/delete"
android:textSize="@dimen/smallest_text_size"
android:textColor="@color/black"
android:gravity="center"
android:paddingRight="@dimen/four_dp"
android:paddingLeft="@dimen/four_dp"
android:paddingTop="@dimen/ten_dp"
android:paddingBottom="@dimen/ten_dp" android:layout_weight="1"
android:text="@string/delete"
android:textStyle="bold"
android:layout_width="0dp"
android:layout_height="wrap_content" />
<View
android:layout_width="1dp"
android:background="@color/grey_unselect"
android:layout_height="match_parent" />
<sis.com.smartworks.widget.SmartWorksTextView
android:id="@+id/cancel"
android:textSize="@dimen/smallest_text_size"
android:textColor="@color/black"
android:gravity="center"
android:layout_weight="1"
android:visibility="visible"
android:paddingRight="@dimen/four_dp"
android:paddingLeft="@dimen/four_dp"
android:paddingTop="@dimen/ten_dp"
android:paddingBottom="@dimen/ten_dp"
android:textStyle="bold"
android:text="@string/select_cancel"
android:layout_width="0dp"
android:layout_height="wrap_content" />
</LinearLayout>
</android.support.v7.widget.CardView>
Result
So if you want to make view as horizontal then you need to make horizontal layout according to your requirement. So can do this task to change your edit_delete_layout.xml
which your putting into contentView
then pass to Popup class method
.
Note:- You can customise popup class according to your requirement and I know this code having so many deprecated view so you can update yourself.
To show compact contextual menu you need to create ActionMode
for the Menu
, let me show you how:
Suppose your action menu XML have delete, copy and forward actions:
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<item
android:id="@+id/action_copy"
android:icon="@drawable/ic_vector_menu_copy"
android:title="Copy"
app:showAsAction="always" />
<item
android:id="@+id/action_delete"
android:icon="@drawable/ic_vector_menu_delete"
android:title="Delete"
app:showAsAction="always" />
<item
android:id="@+id/action_forward"
android:icon="@drawable/ic_vector_menu_forward"
android:title="Forward"
app:showAsAction="always" />
</menu>
Create your action menu in your Activity
//Global variable in Activity/Fragment to manage close the menu
private ActionMode mActionMode;
//Action mode callbacks
//Contextual Action bar - for showing delete/copy/... on action bar
private ActionMode.Callback mActionModeCallback = new ActionMode.Callback() {
// Called when the action mode is created; startActionMode() was called
@Override
public boolean onCreateActionMode(ActionMode mode, Menu menu) {
// Inflate a menu resource providing context menu items
MenuInflater inflater = mode.getMenuInflater();
inflater.inflate(R.menu.menu_contextual_action, menu);
return true;
}
// Called each time the action mode is shown.
// Always called after onCreateActionMode, but
// may be called multiple times if the mode is invalidated.
@Override
public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
return false; // Return false if nothing is done
}
// Called when the user selects a contextual menu item
@Override
public boolean onActionItemClicked(final ActionMode mode, MenuItem item) {
switch (item.getItemId()) {
case R.id.action_delete:
//Do the delete action
//mAdapter.resetSelection();
mode.finish(); // Action picked, so close the TAB
//showToast "Deleted successfully"
return true;
case R.id.action_copy:
//mAdapter.resetSelection();
MyClipboardManager.copyToClipboard(ChatDetailActivity.this, mAdapter.getSelectedMessageText());
mode.finish(); // Action picked, so close the TAB
//showToast "Text copied to clipboard"
return true;
default:
return false;
}
}
// Called when the user exits the action mode
@Override
public void onDestroyActionMode(ActionMode mode) {
mActionMode = null;
//mAdapter.resetSelection();
}
};
@Override
public void onBackPressed() {
//Closing menu first if it's visible rather than doing the back press action
if (mActionMode != null && mActionMode.getMenu().hasVisibleItems()) {
mActionMode.finish();
return;
}
super.onBackPressed();
}
@Override
public void onDestroy() {
//Closing menu
if (mActionMode != null) {
mActionMode.finish();
}
super.onDestroy();
}
*Set the callback to the global action mode variable
mActionMode = startSupportActionMode(mActionModeCallback);
*Set title to the menu
mActionMode.setTitle("Menu title");
*Invalidate the menu after settings value
mActionMode.invalidate();
Style to manage compact contextual menu
<!-- Base application theme. -->
<style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">
<!-- Customize your theme here. -->
<item name="colorPrimary">@color/colorPrimary</item>
<item name="colorPrimaryDark">@color/colorPrimaryDark</item>
<item name="colorAccent">@color/colorAccent</item>
<item name="android:windowDisablePreview">true</item>
<!--CONTEXTUAL action MODE-->
<item name="android:windowContentOverlay">@null</item>
<!--ActionMode background color-->
<!-- <item name="android:actionModeBackground">@color/colorPrimary</item>-->
<!--To Overlay existing toolbar, NOTE We are not using android: to let it work everywhere-->
<item name="windowActionModeOverlay">true</item>
<item name="actionModeStyle">@style/AppActionModeStyle</item>
<item name="android:actionModeCloseDrawable">@drawable/ic_arrow_back_24dp</item>
</style>
<style name="AppActionModeStyle" parent="@style/Widget.AppCompat.ActionMode">
<!--ActionMode background color-->
<item name="background">@color/colorPrimary</item>
<!--ActionMode text title color-->
<item name="titleTextStyle">@style/ActionModeTitleTextStyle</item>
</style>
<style name="ActionModeTitleTextStyle" parent="@style/TextAppearance.AppCompat.Widget.ActionMode.Title">
<item name="android:textColor">@android:color/white</item>
</style>
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.