在EditText中,单击特定文本子集时弹出对话框

Per*_*man 6 android onclick touch-event android-edittext

找到两个解决方案 - 请参阅选定的答案

当用户点击EditText的某个区域时,我想弹出一个对话框.我使用onClick来捕获点击.这部分工作:用户第一次点击EditText时,弹出软键盘而对话框不弹出.随后的点击会弹出键盘,然后是对话框(键盘会消失).

我怀疑这与EditText获得关注有关.

这是一个代码片段:

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();
  }
}
Run Code Online (Sandbox Code Playgroud)

重要说明:我不想完全禁用正常的EditText行为.我希望用户能够进行区域选择(用于复制和粘贴).我可能仍然希望它获得焦点(因此当具有物理键盘的人使用该应用程序时,我不打破模型).点击可以设置光标位置.因此,覆盖onTouch并阻止EditText中所有onTouch操作的解决方案对我来说不起作用.

更新 我发现了一点.如果EditText正在获得焦点,则会调用onFocusChange而onClick不会.如果它已经有焦点,则onClick被调用而onFocusChange没有.

其次,可以通过调用隐藏键盘

setInputType (InputType.TYPE_NULL);
Run Code Online (Sandbox Code Playgroud)

在onFocusChange中这样做 - 键盘永远不会出现.在onClick中这样做(假设键盘在点击之前被隐藏)显然为时已晚 - 键盘显示然后消失.

下一个尝试的想法是在onTouch期间隐藏键盘.但是,我害怕弄乱那些代码 - 似乎无论我想出什么都会对EditText的未来版本非常脆弱.

对此有何看法?

Per*_*man 1

经过大量实验,这里有两个可行的解决方案!我在我的两台设备上测试了它们 - 运行 4.2.1 的 Nexus 7,运行 4.0.4 的 Kyocera C5170。我的偏好是解决方案 2。

解决方案1

首先,技巧是在 EditText 有机会完成其工作之前(特别是在弹出键盘之前)在 onTouch 而不是 onClick 中确定光标位置。

另一条注释:请务必在弹出窗口的清单中设置 android:windowSoftInputMode="stateHidden",否则您将随弹出窗口一起获得键盘。

这是整个代码:

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);
  }
}
Run Code Online (Sandbox Code Playgroud)

解决方案2

这个实际上更简单,而且我认为更好——副作用更少。

这里的技巧是让 EditText 完成所有的点击处理,然后异步覆盖它。要点是:等待触摸“放开” - MotionEvent.ACTION_UP - 然后不要立即执行操作,而是将 Runnable 发布到事件队列并在那里执行操作。

整个代码:

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);
  }
}
Run Code Online (Sandbox Code Playgroud)