安卓如何拦截EditText的复制、粘贴和剪切操作

43

我该如何拦截这类事件?

当用户尝试将一些文本粘贴到我的 EditText 中时,我需要添加一些逻辑。我知道我可以使用 TextWatcher,但是这个入口对我来说不好,因为我只需要在粘贴的情况下拦截,而不是每次用户按我的 EditText


好的挑战。看看我的答案。 - Lukas Knuth
2个回答

94

看起来通过使用 API,你似乎不能做太多事情:android paste event

阅读源代码可解救一切!

我深入研究了 TextView 的 Android 源码(EditText 是一个带有不同配置的 TextView),发现用于提供剪切、复制和粘贴选项的菜单只是修改过的 ContextMenu源代码)。

至于普通上下文菜单,视图必须创建菜单(源代码),然后在回调方法中处理交互(源代码)。

因为处理方法是 public,所以我们可以通过扩展 EditText 并覆盖这个方法来反应不同的操作。这里是一个示例实现:

import android.content.Context;
import android.util.AttributeSet;
import android.widget.EditText;
import android.widget.Toast;

/**
 * An EditText, which notifies when something was cut/copied/pasted inside it.
 * @author Lukas Knuth
 * @version 1.0
 */
public class MonitoringEditText extends EditText {

    private final Context context;

    /*
        Just the constructors to create a new EditText...
     */
    public MonitoringEditText(Context context) {
        super(context);
        this.context = context;
    }

    public MonitoringEditText(Context context, AttributeSet attrs) {
        super(context, attrs);
        this.context = context;
    }

    public MonitoringEditText(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        this.context = context;
    }

    /**
     * <p>This is where the "magic" happens.</p>
     * <p>The menu used to cut/copy/paste is a normal ContextMenu, which allows us to
     *  overwrite the consuming method and react on the different events.</p>
     * @see <a href="http://grepcode.com/file/repository.grepcode.com/java/ext/com.google.android/android/2.3_r1/android/widget/TextView.java#TextView.onTextContextMenuItem%28int%29">Original Implementation</a>
     */
    @Override
    public boolean onTextContextMenuItem(int id) {
        // Do your thing:
        boolean consumed = super.onTextContextMenuItem(id);
        // React:
        switch (id){
            case android.R.id.cut:
                onTextCut();
                break;
            case android.R.id.paste:
                onTextPaste();
                break;
            case android.R.id.copy:
                onTextCopy();
        }
        return consumed;
    }

    /**
     * Text was cut from this EditText.
     */
    public void onTextCut(){
        Toast.makeText(context, "Cut!", Toast.LENGTH_SHORT).show();
    }

    /**
     * Text was copied from this EditText.
     */
    public void onTextCopy(){
        Toast.makeText(context, "Copy!", Toast.LENGTH_SHORT).show();
    }

    /**
     * Text was pasted into the EditText.
     */
    public void onTextPaste(){
        Toast.makeText(context, "Paste!", Toast.LENGTH_SHORT).show();
    }
}

现在,当用户使用剪切/复制/粘贴功能时,会显示一个Toast(当然你也可以做其他事情)。

好的一点是,这适用于Android 1.5版本及以下,而且您不需要重新创建上下文菜单(如上面链接的问题所建议的那样),这将 保持平台的统一外观(例如HTC Sense)。


我遇到了“无法实例化类,没有空构造函数”的错误,有什么解决方法吗? - Nirav Mehta
好的,这个类没有空构造函数。你必须传递一个Context实例(如果你的代码在Activity中,你可以传递this)。 - Lukas Knuth
2
通过添加监听器,将您的解决方案扩展为可用作即插即用组件。要点请参见 https://gist.github.com/guillermomuntaner/82491cbf0c88dec560a5 - GuillermoMP
1
如何从粘贴中获取插入的 Spanned 字符串? - Zxcv
@Pierre,你可以添加一个TextWatcher并记录更改。如果发生更改并且您获得了适当的剪切/复制/粘贴操作,则可以确定内容的哪个部分发生了更改。 - Lukas Knuth
1
终于找到了更好的解决方案。我不想每次按下一个字母时都触发TextWatcher。因此,我使用ClipBoardManager来覆盖粘贴的内容。感谢:https://dev59.com/GGw15IYBdhLWcg3wYasx和https://developer.android.com/guide/topics/text/copy-paste.html。 - Zxcv

8

有一种更简单的方法,虽然不是100%可靠。

将TextChangedListener添加到您的编辑框中:

EditText et = (EditText) mView.findViewById(R.id.yourEditText);
et.addTextChangedListener(new TextWatcher() {

  @Override
  public void onTextChanged(CharSequence s, int start, int before, int count) {
     if (count > 2) toast("text was pasted");
  }

  @Override
  public void beforeTextChanged(CharSequence s, int start, int count, int after) {

  }

  public void afterTextChanged(Editable s) {
               
  }
});

如果文本更改超过2个字符,您可以假设它是粘贴的(某些表情符号占据两个字符)。

当用户粘贴1或2个字符时,它当然不会检测到粘贴,如果文本变化是由其他事情触发的,则会错误地报告粘贴。

但对于大多数目的而言,它足以完成工作。


1
如果您的EditText仅限于1个字符,并且您想要复制到多个EditText(例如每个数字都有1个EditText的代码),则此方法无法正常工作。 - tiagocarvalho92
3
是的,这在描述中已经写明了。 - lenooh
1
这看起来还不错。我会考虑与系统集成,也许使用自动填充或那个 OTP 提供商的东西,它可以自动读取消息。这是更好的用户体验。此外,添加一个“粘贴”按钮就可以解决问题了。在这种情况下,真的没有理由局限于 EditText。 - milosmns

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接