LoginSignup
6
5

More than 5 years have passed since last update.

スクロール可能なTextViewで0文字選択状態になる→強制終了してしまう不具合の回避方法

Last updated at Posted at 2015-09-08

メッセンジャー系のアプリをつくろうとして、ハマった内容を共有します。

現象

Screenshot_2015-09-08-14-39-47.png

  1. テキスト選択をする
  2. スクロールする
  3. つまみが0文字選択になる→つまみをさわるとアプリが強制終了する

エラーログ

Androidフレームワークの中でエラーが起きています。

    java.lang.IndexOutOfBoundsException: setSpan (-1 ... -1) starts before 0
            at android.text.SpannableStringInternal.checkRange(SpannableStringInternal.java:355)
            at android.text.SpannableStringInternal.setSpan(SpannableStringInternal.java:77)
            at android.text.SpannableString.setSpan(SpannableString.java:46)
            at android.text.Selection.setSelection(Selection.java:76)
            at android.widget.Editor$SelectionEndHandleView.updateSelection(Editor.java:3357)
            at android.widget.Editor$HandleView.positionAtCursorOffset(Editor.java:3045)
            at android.widget.Editor$SelectionEndHandleView.updatePosition(Editor.java:3372)
            at android.widget.Editor$HandleView.onTouchEvent(Editor.java:3138)
            at android.view.View.dispatchTouchEvent(View.java:7127)
            at android.view.View.dispatchPointerEvent(View.java:7307)
            at android.view.ViewRootImpl.deliverPointerEvent(ViewRootImpl.java:3172)
            at android.view.ViewRootImpl.deliverInputEvent(ViewRootImpl.java:3117)
            at android.view.ViewRootImpl.doProcessInputEvents(ViewRootImpl.java:4153)
            at android.view.ViewRootImpl.enqueueInputEvent(ViewRootImpl.java:4132)
            at android.view.ViewRootImpl$WindowInputEventReceiver.onInputEvent(ViewRootImpl.java:4224)
            at android.view.InputEventReceiver.dispatchInputEvent(InputEventReceiver.java:171)
            at android.view.InputEventReceiver.nativeConsumeBatchedInputEvents(Native Method)
            at android.view.InputEventReceiver.consumeBatchedInputEvents(InputEventReceiver.java:163)
            at android.view.ViewRootImpl.doConsumeBatchedInput(ViewRootImpl.java:4203)
            at android.view.ViewRootImpl$ConsumeBatchedInputRunnable.run(ViewRootImpl.java:4243)
            at android.view.Choreographer$CallbackRecord.run(Choreographer.java:725)
            at android.view.Choreographer.doCallbacks(Choreographer.java:555)
            at android.view.Choreographer.doFrame(Choreographer.java:523)
            at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:711)
            at android.os.Handler.handleCallback(Handler.java:615)
            at android.os.Handler.dispatchMessage(Handler.java:92)
            at android.os.Looper.loop(Looper.java:137)
            at android.app.ActivityThread.main(ActivityThread.java:4745)
            at java.lang.reflect.Method.invokeNative(Native Method)
            at java.lang.reflect.Method.invoke(Method.java:511)
            at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:786)
            at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:553)
            at dalvik.system.NativeStart.main(Native Method)

原因

正直、よくわかりません。が、おそらく

  • [-1, -1] をエラーにしちゃうSpannableStringの実装
  • どんなSpannableかは知らないがとりあえずsetSpanしちゃうSelection.setSelectionの実装

が噛み合ってないんだろうなぁと予想。

回避策

Exception名でぐぐっても似たようで違うのがいっぱい出てきてしまうので
インターフェースのキモである SpannableString.java:46でぐぐってみたところ、ヒット!

SelectableTextView

public class SelectableTextView extends TextView {

    public SelectableTextView(Context context) {
        super(context);
        init();
    }

    public SelectableTextView(Context context, AttributeSet attrs) {
        super(context, attrs);
        init();
    }

    public SelectableTextView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init();
    }

    private void init() {
        setTextIsSelectable(true);
    }

    @Override
    protected void onSelectionChanged(int selStart, int selEnd) {
        if (selStart == -1 || selEnd == -1) {
            // @hack : https://code.google.com/p/android/issues/detail?id=137509
            CharSequence text = getText();
            if (text instanceof Spannable) {
                Selection.setSelection((Spannable) text, 0, 0);
            }
        } else {
            super.onSelectionChanged(selStart, selEnd);
        }
    }
}

こんなかんじのテキストビューを用意して使えばいいらしい。

実際になおった

以上・・・。

6
5
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
6
5