Search code examples
androidonclickandroid-edittexttouch-event

In EditText, popup a dialog when clicking on specific subset of text


Found two solutions - please see selected answer

When the user clicks in a certain region of an EditText, I want to popup a dialog. I used onClick to capture the click. This partially works: the first time the user taps the EditText, the soft keyboard pops up and the dialog doesn't. Subsequent taps bring up the keyboard and then the dialog (and the keyboard disappears).

I suspect this has something to do with the EditText gaining focus.

Here's a code snip:

public class PrefixEditText extends EditText implements TextWatcher, OnClickListener
{
  public PrefixEditText (Context context)
  {
    super (context);
    setOnClickListener (this);
  }

  @Override
  public void onClick(View v)
  {
    int selStart = getSelectionStart();
    if (selStart < some_particular_pos)
      bring_up_dialog();
  }
}

IMPORTANT: I don't want to completely disable the normal EditText behavior. I want the user to be able to make region selections (for copy & paste). I probably still want it to gain focus (so I don't break the model when people with physical keyboards use the app). And it's ok for the click to set the cursor position. Thus, solutions that override onTouch and block all onTouch actions from the EditText will not work for me.

UPDATE I've discovered a bit more. If the EditText is gaining focus, onFocusChange gets called and onClick does not. If it already has focus, onClick gets called and onFocusChange does not.

Secondly, it's possible to hide the keyboard by calling

setInputType (InputType.TYPE_NULL);

Doing so in onFocusChange works - the keyboard never shows up. Doing so in onClick (assuming the keyboard was hidden before the click) apparently is too late - the keyboard shows up and then disappears.

The next idea to try would be to hide the keyboard during onTouch. However, I'm afraid to mess with that code - seems that whatever I figure out would be very fragile with respect to future versions of EditText.

Any thoughs on this?


Solution

  • After lots of experiments, here are two working solutions! I tested them on my two devices - Nexus 7 running 4.2.1, Kyocera C5170 runing 4.0.4. My preference is Solution 2.

    SOLUTION 1

    For the first, the trick was to determine the cursor position in onTouch instead of onClick, before EditText has a chance to do it's work - particularly before it pops up the keyboard.

    One additional comment: be sure to set android:windowSoftInputMode="stateHidden" in your manifest for the popup, or you'll get the keyboard along with the popup.

    Here's the whole code:

    public class ClickText extends EditText implements OnTouchListener
    {
      public ClickText (Context context, AttributeSet attrs)
      {
        super (context, attrs);
        setOnTouchListener (this);
      }
    
      @Override
      public boolean onTouch (View v, MotionEvent event)
      {
        if (event.getActionMasked() == MotionEvent.ACTION_DOWN)
        {
          int line = getLayout().getLineForVertical ((int)event.getY());
          int onTouchCursorPos = getLayout().getOffsetForHorizontal (line, event.getX());
          if (onTouchCursorPos < 10) // or whatever condition
            showPopup (this); // or whatever you want to do
        }
        return false;
      }
    
      private void showPopup (final EditText text)
      {
        Intent intent = new Intent (getContext(), Popup.class);
        ((Activity)getContext()).startActivity (intent);
      }
    }
    

    SOLUTION 2

    This one is actually simpler and, I think, is better - fewer side effects.

    Here, the trick is to let EditText do all its click processing and then override it asynchronously. The gist is: wait for the touch to "let go" - MotionEvent.ACTION_UP - and then instead of doing your action right then, post a Runnable to the event queue and do your action there.

    The whole code:

    public class ClickText extends EditText implements OnTouchListener
    {
      public ClickText (Context context, AttributeSet attrs)
      {
        super (context, attrs);
        setOnTouchListener (this);
      }
    
      @Override
      public boolean onTouch (View v, MotionEvent event)
      {
        switch (event.getActionMasked())
        {
          case MotionEvent.ACTION_UP:
          {
            post (new Runnable ()
            {
              // Do this asynch so that EditText can finish setting the selectino.
              @Override
              public void run()
              {
                int selStart = getSelectionStart();
                int selEnd = getSelectionEnd();
    
                // If selStart is different than selEnd, user has highlighed an area of
                // text; I chose to ignore the click when this happens.
                if (selStart == selEnd)
                  if (selStart >= 0 && selStart < 10) // or whatever range you want
                    showPopup (this);
              }            
            });
    
            break;
          }
        }
    
        return false;
      }
    
      private void showPopup (final EditText text)
      {
        Intent intent = new Intent (getContext(), Popup.class);
        ((Activity)getContext()).startActivity (intent);
      }
    }