【问题标题】:ClipboardManager OnPrimaryClipChangedListener is called twice for every copyClipboardManager OnPrimaryClipChangedListener 为每个副本调用两次
【发布时间】:2013-08-22 22:17:56
【问题描述】:

当我将文本复制到剪贴板时,onPrimaryClipChanged 方法被调用了两次。任何想法为什么?

@Override
public boolean onCreateOptionsMenu(Menu menu) {
    getMenuInflater().inflate(R.menu.main, menu);
    final ClipboardManager cliboardManager = (ClipboardManager) getSystemService(CLIPBOARD_SERVICE);

    cliboardManager
            .addPrimaryClipChangedListener(new OnPrimaryClipChangedListener() {

                @Override
                public void onPrimaryClipChanged() {
                    ClipData clipData = cliboardManager.getPrimaryClip();
                    System.out
                            .println("********** clip changed, clipData: "
                                    + clipData.getItemAt(0));
                }
            });
    return true;
}

测试用例: 从 BBC 网站复制文本“继续”将产生以下输出:

继续

继续

但如果我调试程序,我可以看到 clipData 对象有值:

ClipData { text/plain {T:continue } }

第一次调用 onPrimaryClipChanged() 并且

ClipData { text/plain "BBC - 主页" {T:continue } }

下次调用 onPrimaryClipChanged() 时。

所以基本上第一次 ClipDescription 是 { text/plain } 而第二次是 ClipDescription { text/plain "BBC - Homepage" }(即包括网页的标题)

【问题讨论】:

  • 你确定你没有不小心注册了两个监听器? :)
  • 剪贴板的双重设置可能是某些应用程序中的错误。我从 Chrome 收到两条剪贴板消息,它们看起来是相同的(都是 text/html,所有字段似乎都相同,toString 两次都返回相同的 html)。我在 Whatsapp、Youtube、Hangouts 和默认消息应用程序上进行了测试,所有这些应用程序都只设置了一次剪贴板。 Chrome 这样做可能是有原因的,但我现在没有时间深入研究它们的源代码。

标签: android clipboardmanager


【解决方案1】:

我假设您没有注册多个侦听器,我不能说这是错误,您仍然可以解决它。试试这样的:

   String mPreviousText = "";

   cliboardManager
                .addPrimaryClipChangedListener(new OnPrimaryClipChangedListener() {

                    @Override
                    public void onPrimaryClipChanged() {
                        ClipData clipData = cliboardManager.getPrimaryClip();
                        System.out
                                .println("********** clip changed, clipData: "
                                        + clipData.getItemAt(0));
                         ClipData.Item item = clipData.getItemAt(0);
                         if(mPreviousText.equals(item.getText().toString())) return;
                         else{
                            /// do something
                            mPrevousText = item.getText().toString();
                         }
                    }
                });

【讨论】:

  • 这是一个很好的解决方法,但是当用户确实想要复制相同的文本两次时,它就行不通了 :(.
  • @DaveChen 如果您复制相同的文本两次,剪贴板上的文本仍然是相同的。
  • +1 因为我确实最终使用了这个解决方法,但问题是如果用户决定复制文本两次,/// do something 将不会被点击两次。
【解决方案2】:

我也遇到这个问题,终于知道为什么会调用多次!!!

通常我们addPrimaryClipChangedListener(),但我们不removePrimaryClipChangedListener()

见: http://developer.android.com/reference/android/content/Context.html#CLIPBOARD_SERVICE

与 getSystemService(String) 一起使用来检索 ClipboardManager 以访问和修改全局剪贴板的内容。

这意味着我们应该手动removePrimaryClipChangedListener()

我的解决方案代码:

ClipboardManager myClipBoard ;
static boolean bHasClipChangedListener = false;

ClipboardManager.OnPrimaryClipChangedListener mPrimaryClipChangedListener = new ClipboardManager.OnPrimaryClipChangedListener() {
    public void onPrimaryClipChanged() {
        ClipData clipData = myClipBoard.getPrimaryClip();
        Log.d("henrytest", "********** clip changed, clipData: " + clipData.getItemAt(0));
    }
};

private void RegPrimaryClipChanged(){
    if(!bHasClipChangedListener){
        myClipBoard.addPrimaryClipChangedListener(mPrimaryClipChangedListener);
        bHasClipChangedListener = true;
    }
}
private void UnRegPrimaryClipChanged(){
    if(bHasClipChangedListener){
        myClipBoard.removePrimaryClipChangedListener(mPrimaryClipChangedListener);
        bHasClipChangedListener = false;
    }
}

@Override
protected void onCreate(Bundle savedInstanceState) {
    myClipBoard = (ClipboardManager) Clipboard.this.getSystemService(android.content.Context.CLIPBOARD_SERVICE);
}

@Override
protected void onResume() {
    super.onResume();
    RegPrimaryClipChanged();
}

@Override
protected void onPause() {
    super.onPause();
    UnRegPrimaryClipChanged();
}

@Override
protected void onStop() {
    super.onStop();
    //UnRegPrimaryClipChanged();
}

@Override
protected void onDestroy() {
    super.onDestroy();
    //UnRegPrimaryClipChanged();
}

【讨论】:

  • 如果您将 UnRegPrimaryClipChanged() 放置到 onPause() 中,则所有其他放置到 onStop() 和 onDestroy() 都是不必要的。
【解决方案3】:

这是我防止 ClipboardManager OnPrimaryClipChangedListener 为每个副本调用两次的解决方法。

long lastCopiedTime = 0;
ClipboardManager.OnPrimaryClipChangedListener onPrimaryClipChangedListener = new ClipboardManager.OnPrimaryClipChangedListener() {
    @Override
    public void onPrimaryClipChanged() {
                ClipboardManager clipBoard = (ClipboardManager) getSystemService(CLIPBOARD_SERVICE);
                String pasteData = "";
                ClipData.Item item = clipBoard.getPrimaryClip().getItemAt(0);
                if (System.currentTimeMillis() - lastCopiedTime > TimeUnit.SECONDS.toMillis(1)) {
                    if (StringUtils.isNotBlank(item.getText())) {
                        String s = item.getText().toString();
                        if (StringUtils.isNotBlank(s)) {
                            pasteData = s.trim();
                            if (StringUtils.isNotBlank(pasteData)) {
                                  /// do something here
                            }
                        }
                    }
                }
                lastCopiedTime = System.currentTimeMillis();
            }
        }
    }
};

【讨论】:

  • 成功了,谢谢
【解决方案4】:

//防止OnPrimaryClipChangedListener的重复动作。

private ExecutorService mThreadPool = Executors.newSingleThreadExecutor();
static boolean isExpireData = true;

class hasExpireData implements Runnable {

    @Override
    public void run() {
        // TODO Auto-generated method stub

        isExpireData = true;
    }

}

private ClipboardManager.OnPrimaryClipChangedListener mOnPrimaryClipChangedListener =
        new ClipboardManager.OnPrimaryClipChangedListener() {
    @Override
    public void onPrimaryClipChanged() {
        Log.d(TAG, "onPrimaryClipChanged");
        ClipData clip = mClipboardManager.getPrimaryClip();

        Thread expireData = new Thread(new hasExpireData());
        new Handler().postDelayed(expireData, 500); // 0.5 seconds wait...

        if(isExpireData) {
            isExpireData = false;
            mThreadPool.execute(new TextRunnable(
                    clip.getItemAt(0).getText()));
        }

    }
};

private class TextRunnable implements Runnable {
    public TextRunnable(CharSequence text) {
        // text store to this class
    }

    @Override
    public void run() {
        // text other process
    }
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-07-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多