4

I have a TextView that launches a context menu:

textView.setOnCreateContextMenuListener(new OnCreateContextMenuListener() {
            @Override
            public void onCreateContextMenu(ContextMenu contextMenu, 
                                            View view,
                                            ContextMenu.ContextMenuInfo contextMenuInfo) {
                // Do stuff... 
            }
        });

This textview also has the android:autoLink="all" attribute set in its XML.

Now, if I set the contents of the TextView to a URL and long press over the URL, the context menu appears first, but when I lift my finger the link is pressed and opens the browser.

Is there any way to have the context menu or the long press consume the touch event so that the link is not clicked? I have considered overriding onTouch() for the TextView to handle ACTION_UP events, but I cannot reliably keep track of when the context menu is visible to block the touch event.

allenc
  • 59
  • 5

3 Answers3

2

This seems somewhat hacky to me, but it's the only way I've been able to make this work without directly handling the View's touch events (which may very well be the proper method).

We're essentially changing out the TextView's LinkMovementMethod if the context menu opens, so the up action doesn't fire the link. When the context menu closes, we restore the LinkMovementMethod so that normal clicks on links work as expected.

Adjust your onCreateContextMenu() method as follows:

textView.setOnCreateContextMenuListener(new OnCreateContextMenuListener() {
        @Override
        public void onCreateContextMenu(ContextMenu menu,
                                        View view,
                                        ContextMenu.ContextMenuInfo info) {
            textView.setMovementMethod(ArrowKeyMovementMethod.getInstance());
            // Do stuff
        }
    });

Then override the Activity's onContextMenuClosed() like so:

@Override
public void onContextMenuClosed(Menu menu)
{
    textView.setMovementMethod(LinkMovementMethod.getInstance());
}

Of course, this assumes that textView is a class member of your Activity.

Mike M.
  • 38,532
  • 8
  • 99
  • 95
  • Unfortunately, in this case `textView` is a member of a custom view a few abstraction layers down from the Activity. It seems messy to rely on the parent activity to notify the view, so the best way may be to directly intercept the touch events via a `GestureDetector` or `OnLongClickListener` and disable ACTION_UP events via a flag until another type of touch event occurs. – allenc Dec 11 '14 at 18:40
  • @allenc Yeah, handling the touch events would definitely be a more solid solution. I guess I read your question as saying you wanted to avoid that. – Mike M. Dec 12 '14 at 00:45
0

Just like Mike M. said.

you can use setOnCreateContextMenuListener instead. and perform click parent's view in this method

Example:

here is a textview inside a viewgroup;

textview.setOnCreateContextMenuListener(
(menu, v, menuInfo) -> viewgroup.performLongClick());
yefeng
  • 111
  • 1
  • 14
-1

This solution doesn't seem to have any drawbacks:

override fun onCreateContextMenu(menu: ContextMenu, view: View, menuInfo: ContextMenuInfo) {
    // Inflate your menu, etc

    // ...

    // Cancel any tap in progress
    view.dispatchTouchEvent(MotionEvent.obtain(0, 0, MotionEvent.ACTION_CANCEL, 0f, 0f, 0))
}

Long press will show the context menu as expected, but nothing will happen when you lift your finger (because the tap has already been cancelled). Short taps that do not result in a context menu will be handled as usual.