EditText小部件的onClickListener中的SetSelection行为很奇怪

时间:2012-11-20 16:08:46

标签: android android-edittext

我目前正在处理多行编辑文本,该文本可能在其文本中包含占位符。为了绕过这些占位符的修改,我在EditText小部件中添加了一个onClickListener,用于检查光标位置是否在这样的占位符中。在这种情况下,应选择占位符,以防止任何修改,除非完全删除它。

这在我的Android 2.3设备上完全正常,但在Android 4.x上,选择在onClick事件后被修改,光标显示在占位符的开头而没有选择。

onClickListener的源代码。

protected void textClickListener(EditText v) { 
    Pattern p = Pattern.compile(placeholderRegex);
    Matcher matcher = p.matcher(v.getText());
    int sel_start = v.getSelectionStart();
    int sel_end = v.getSelectionEnd();
    if (sel_start == -1) {
        return;
    }
    while (matcher.find()) {
        int pattern_start = matcher.start();
        int pattern_end = pattern_start + 25;
        if (pattern_start > sel_end) {
            continue;
        }
        if (pattern_end < sel_start) {
            continue;
        }
        v.setSelection(Math.min(sel_start, pattern_start), Math.max(sel_end, pattern_end));
        return;
    }
}

此代码工作正常,使用正确的值调用setSelection,并且实际设置了选择。我在Selection.setSelection方法上设置了一个断点,发现它是从PositionListener调用的,它是android.widget.editor的内部类,它将选择长度设置为0.以下是此setSelection调用的堆栈跟踪:

Selection.setSelection(Spannable, int) line: 87 
Editor$InsertionHandleView.updateSelection(int) line: 3271  
Editor$InsertionHandleView(Editor$HandleView).positionAtCursorOffset(int, boolean) line: 3045   
Editor$InsertionHandleView(Editor$HandleView).updatePosition(int, int, boolean, boolean) line: 3064 
Editor$PositionListener.onPreDraw() line: 2047  
ViewTreeObserver.dispatchOnPreDraw() line: 671  
ViewRootImpl.performTraversals() line: 1820 
ViewRootImpl.doTraversal() line: 1000   
ViewRootImpl$TraversalRunnable.run() line: 4214 
Choreographer$CallbackRecord.run(long) line: 725    
Choreographer.doCallbacks(int, long) line: 555  
Choreographer.doFrame(long, int) line: 525  
Choreographer$FrameDisplayEventReceiver.run() line: 711 
Handler.handleCallback(Message) line: 615   
Choreographer$FrameHandler(Handler).dispatchMessage(Message) line: 92   
Looper.loop() line: 137 
ActivityThread.main(String[]) line: 4745    
Method.invokeNative(Object, Object[], Class, Class[], Class, int, boolean) line: not available [native method]  
Method.invoke(Object, Object...) line: 511  
ZygoteInit$MethodAndArgsCaller.run() line: 786  
ZygoteInit.main(String[]) line: 553 
NativeStart.main(String[]) line: not available [native method]

如何防止此操作或如何在此PositionListener操作后设置选择?

1 个答案:

答案 0 :(得分:1)

从问题迁移

最后我发现,您必须在OnTouchListener中修改选择。为了使其更加复杂,您必须在该侦听器的两次调用中修改选择(ACTION_DOWNACTION_UP)。如果你只做一次(因为我出于性能原因),它也不会工作。所以最终工作的代码是:

@TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH)
protected boolean textTouchListener(EditText v, MotionEvent event) {
    placeholder_selected = false;
    Pattern p = Pattern.compile(placeHolderRegex);
    Matcher matcher = p.matcher(v.getText());
    int click_position = v.getOffsetForPosition(event.getX(), event.getY());
    int sel_start = click_position;
    int sel_end = click_position;
    if (sel_start == -1) {
        return false;
    }
    while (matcher.find()) {
        int pattern_start = matcher.start();
        int pattern_end = pattern_start + 25;
        if (pattern_start > sel_end) {
            continue;
        }
        if (pattern_end < sel_start) {
            continue;
        }
        v.setSelection(Math.min(sel_start, pattern_start), Math.max(sel_end, pattern_end));
        placeholder_selected = true;
        return true;
    }
    return false;
}

出于兼容性原因,仍然需要OnClickListener,因为方法getOffsetForPosition不适用于低于11的API级别(Honeycomb)。根据使用的API,只需要执行两个侦听器中的一个。