简体   繁体   中英

How to make a horizontal ContextMenu?

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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM