1

I'm trying to make a custom EditText for tags, so when ever you type a comma, everything before the comma, that is not from a previous tag, it's parsed into a view which then becomes an image and that image is transformed into a ImageSnap. Since you can remove tags I'm trying to make it so when you click in the right part of the tag it removes it by changing the EditText text, which because of the TextWatcher listener calls the format() function again and it should reformat everything, which it does only if I try to remove the first tag, whenever I try to do so with the second one or third one the app crashes, but the crash doesn't reference to any of my code.

Here is the whole EditTextCustom class, here is where everything happens. All the code was extracted from this answer https://stackoverflow.com/a/38241477/11914539 but it has been slightly modified.

public class TagEditText extends androidx.appcompat.widget.AppCompatEditText {

    TextWatcher textWatcher;

    String lastString;

    String separator = ",";

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


    private void init() {
        setMovementMethod(LinkMovementMethod.getInstance());

        textWatcher = new TextWatcher() {
            @Override
            public void beforeTextChanged(CharSequence s, int start, int count, int after) {
            }

            @Override
            public void onTextChanged(CharSequence s, int start, int before, int count) {

            }

            @Override
            public void afterTextChanged(Editable s) {
                String thisString = s.toString();
                if (thisString.length() > 0 && !thisString.equals(lastString)) {
                    format();

                }
            }
        };

        addTextChangedListener(textWatcher);
    }


    private void format() {

        SpannableStringBuilder sb = new SpannableStringBuilder();
        String fullString = getText().toString();

        String[] strings = fullString.split(separator);


        for (int i = 0; i < strings.length; i++) {

            String string = strings[i];
            sb.append(string);

            if (fullString.charAt(fullString.length() - 1) != separator.charAt(0) && i == strings.length - 1) {
                break;
            }

            BitmapDrawable bd = (BitmapDrawable) convertViewToDrawable(createTokenView(string));
            bd.setBounds(0, 0, bd.getIntrinsicWidth(), bd.getIntrinsicHeight());

            int startIdx = sb.length() - (string.length());
            int endIdx = sb.length();
            sb.setSpan(new ImageSpan(bd), startIdx, endIdx, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
            Log.e("TAG ", "end: " + endIdx + ", start: " + startIdx);
            MyClickableSpan myClickableSpan = new MyClickableSpan(startIdx, endIdx);
            sb.setSpan(myClickableSpan, startIdx + (int) ((endIdx - startIdx) * 0.5), endIdx, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);

            if (i < strings.length - 1) {
                sb.append(separator);
            } else if (fullString.charAt(fullString.length() - 1) == separator.charAt(0)) {
                sb.append(separator);
            }
        }


        lastString = sb.toString();

        setText(sb);
        setSelection(sb.length());

    }

    @SuppressLint("NewApi")
    public View createTokenView(String text) {


        RelativeLayout rootView = new RelativeLayout(getContext());
        rootView.setBackgroundResource(R.drawable.tag_background);
        rootView.setLayoutParams(new RelativeLayout.LayoutParams(RelativeLayout.LayoutParams.WRAP_CONTENT, RelativeLayout.LayoutParams.WRAP_CONTENT));

        TextView tagName = new TextView(getContext());
        ImageView deleteTag = new ImageView(getContext());

        rootView.addView(tagName);
        rootView.addView(deleteTag);

        //---
        tagName.setId(generateViewId());
        tagName.setText(text);

        Typeface face = ResourcesCompat.getFont(getContext(), R.font.poppins_medium);
        tagName.setTypeface(face);
        tagName.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 14);

        RelativeLayout.LayoutParams tagNameLP = (RelativeLayout.LayoutParams) tagName.getLayoutParams();
        tagNameLP.addRule(RelativeLayout.CENTER_VERTICAL, RelativeLayout.TRUE);
        tagNameLP.leftMargin = 15;

        tagName.setLayoutParams(tagNameLP);
        //---

        //---
        deleteTag.setId(generateViewId());
        deleteTag.setImageResource(R.drawable.delete_tag_cross);
        deleteTag.setScaleType(ImageView.ScaleType.FIT_CENTER);

        RelativeLayout.LayoutParams deleteTagLP = (RelativeLayout.LayoutParams) deleteTag.getLayoutParams();
        deleteTagLP.addRule(RelativeLayout.CENTER_VERTICAL, RelativeLayout.TRUE);
        deleteTagLP.addRule(RelativeLayout.RIGHT_OF, tagName.getId());
        deleteTagLP.addRule(RelativeLayout.ALIGN_PARENT_END, RelativeLayout.TRUE);

        deleteTagLP.leftMargin = 15;
        deleteTagLP.rightMargin = 15;

        deleteTag.setLayoutParams(deleteTagLP);
        //---

        return rootView;
    }

    public Object convertViewToDrawable(View view) {
        int spec = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED);
        view.measure(spec, spec);
        view.layout(0, 0, view.getMeasuredWidth(), view.getMeasuredHeight());

        Bitmap b = Bitmap.createBitmap(view.getMeasuredWidth(), view.getMeasuredHeight(), Bitmap.Config.ARGB_8888);

        Canvas c = new Canvas(b);

        c.translate(-view.getScrollX(), -view.getScrollY());
        view.draw(c);
        view.setDrawingCacheEnabled(true);
        Bitmap cacheBmp = view.getDrawingCache();
        Bitmap viewBmp = cacheBmp.copy(Bitmap.Config.ARGB_8888, true);
        view.destroyDrawingCache();
        return new BitmapDrawable(getContext().getResources(), viewBmp);
    }

    private class MyClickableSpan extends ClickableSpan {

        int startIdx;
        int endIdx;

        MyClickableSpan(int startIdx, int endIdx) {
            super();
            this.startIdx = startIdx;
            this.endIdx = endIdx;
        }

        @Override
        public void onClick(View widget) {
            String s = getText().toString();


            String s1 = s.substring(0, startIdx - 1);
            String s2 = s.substring(Math.min(endIdx, s.length()));

            Log.e("TAG", "text: " + s1 + s2);

            TagEditText.this.setText(s1 + s2);
        }

    }
}

The errors I get. I get exactly the same error each time, I tried checking the var that it says it's -1 but in the debugger it never says -1. First error.

E/InputEventReceiver: Exception dispatching input event.
E/MessageQueue-JNI: Exception in MessageQueue callback: handleReceiveCallback
E/MessageQueue-JNI: java.lang.IndexOutOfBoundsException: setSpan (-1 ... -1) starts before 0
        at android.text.SpannableStringBuilder.checkRange(SpannableStringBuilder.java:1330)
        at android.text.SpannableStringBuilder.setSpan(SpannableStringBuilder.java:684)
        at android.text.SpannableStringBuilder.setSpan(SpannableStringBuilder.java:676)
        at android.text.Selection.setSelection(Selection.java:94)
        at android.text.Selection.setSelection(Selection.java:78)
        at android.text.Selection.setSelection(Selection.java:153)
        at android.widget.Editor.onTouchUpEvent(Editor.java:2372)
        at android.widget.TextView.onTouchEvent(TextView.java:10899)
        at android.view.View.dispatchTouchEvent(View.java:13431)
        at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:3060)
        at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2755)
        at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:3060)
        at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2755)
        at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:3060)
        at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2755)
        at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:3060)
        at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2755)
        at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:3060)
        at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2755)
        at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:3060)
        at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2755)
        at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:3060)
        at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2755)
        at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:3060)
        at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2755)
        at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:3060)
        at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2755)
        at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:3060)
        at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2755)
        at com.android.internal.policy.DecorView.superDispatchTouchEvent(DecorView.java:465)
        at com.android.internal.policy.PhoneWindow.superDispatchTouchEvent(PhoneWindow.java:1849)
        at android.app.Activity.dispatchTouchEvent(Activity.java:3996)
        at androidx.appcompat.view.WindowCallbackWrapper.dispatchTouchEvent(WindowCallbackWrapper.java:69)
        at com.android.internal.policy.DecorView.dispatchTouchEvent(DecorView.java:423)
        at android.view.View.dispatchPointerEvent(View.java:13690)
        at android.view.ViewRootImpl$ViewPostImeInputStage.processPointerEvent(ViewRootImpl.java:5483)
        at android.view.ViewRootImpl$ViewPostImeInputStage.onProcess(ViewRootImpl.java:5286)
        at android.view.ViewRootImpl$InputStage.deliver(ViewRootImpl.java:4789)
        at android.view.ViewRootImpl$InputStage.onDeliverToNext(ViewRootImpl.java:4842)
        at android.view.ViewRootImpl$InputStage.forward(ViewRootImpl.java:4808)
        at android.view.ViewRootImpl$AsyncInputStage.forward(ViewRootImpl.java:4948)
        at android.view.ViewRootImpl$InputStage.apply(ViewRootImpl.java:4816)
        at android.view.ViewRootImpl$AsyncInputStage.apply(ViewRootImpl.java:5005)
        at android.view.ViewRootImpl$InputStage.deliver(ViewRootImpl.java:4789)
        at android.view.ViewRootImpl$InputStage.onDeliverToNext(ViewRootImpl.java:4842)
        at android.view.ViewRootImpl$InputStage.forward(ViewRootImpl.java:4808)
        at android.view.ViewRootImpl$InputStage.apply(ViewRootImpl.java:4816)
        at android.view.ViewRootImpl$InputStage.deliver(ViewRootImpl.java:4789)
        at android.view.ViewRootImpl.deliverInputEvent(ViewRootImpl.java:7512)
        at android.view.ViewRootImpl.doProcessInputEvents(ViewRootImpl.java:7481)
        at android.view.ViewRootImpl.enqueueInputEvent(ViewRootImpl.java:7442)
        at android.view.ViewRootImpl$WindowInputEventReceiver.onInputEvent(ViewRootImpl.java:7637)
        at android.view.InputEventReceiver.dispatchInputEvent(InputEventReceiver.java:189)
        at android.os.MessageQueue.nativePollOn

Second error.

java.lang.IndexOutOfBoundsException: setSpan (-1 ... -1) starts before 0
        at android.text.SpannableStringBuilder.checkRange(SpannableStringBuilder.java:1330)
        at android.text.SpannableStringBuilder.setSpan(SpannableStringBuilder.java:684)
        at android.text.SpannableStringBuilder.setSpan(SpannableStringBuilder.java:676)
        at android.text.Selection.setSelection(Selection.java:94)
        at android.text.Selection.setSelection(Selection.java:78)
        at android.text.Selection.setSelection(Selection.java:153)
        at android.widget.Editor.onTouchUpEvent(Editor.java:2372)
        at android.widget.TextView.onTouchEvent(TextView.java:10899)
        at android.view.View.dispatchTouchEvent(View.java:13431)
        at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:3060)
        at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2755)
        at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:3060)
        at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2755)
        at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:3060)
        at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2755)
        at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:3060)
        at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2755)
        at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:3060)
        at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2755)
        at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:3060)
        at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2755)
        at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:3060)
        at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2755)
        at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:3060)
        at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2755)
        at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:3060)
        at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2755)
        at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:3060)
        at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2755)
        at com.android.internal.policy.DecorView.superDispatchTouchEvent(DecorView.java:465)
        at com.android.internal.policy.PhoneWindow.superDispatchTouchEvent(PhoneWindow.java:1849)
        at android.app.Activity.dispatchTouchEvent(Activity.java:3996)
        at androidx.appcompat.view.WindowCallbackWrapper.dispatchTouchEvent(WindowCallbackWrapper.java:69)
        at com.android.internal.policy.DecorView.dispatchTouchEvent(DecorView.java:423)
        at android.view.View.dispatchPointerEvent(View.java:13690)
        at android.view.ViewRootImpl$ViewPostImeInputStage.processPointerEvent(ViewRootImpl.java:5483)
        at android.view.ViewRootImpl$ViewPostImeInputStage.onProcess(ViewRootImpl.java:5286)
        at android.view.ViewRootImpl$InputStage.deliver(ViewRootImpl.java:4789)
        at android.view.ViewRootImpl$InputStage.onDeliverToNext(ViewRootImpl.java:4842)
        at android.view.ViewRootImpl$InputStage.forward(ViewRootImpl.java:4808)
        at android.view.ViewRootImpl$AsyncInputStage.forward(ViewRootImpl.java:4948)
        at android.view.ViewRootImpl$InputStage.apply(ViewRootImpl.java:4816)
        at android.view.ViewRootImpl$AsyncInputStage.apply(ViewRootImpl.java:5005)
        at android.view.ViewRootImpl$InputStage.deliver(ViewRootImpl.java:4789)
        at android.view.ViewRootImpl$InputStage.onDeliverToNext(ViewRootImpl.java:4842)
        at android.view.ViewRootImpl$InputStage.forward(ViewRootImpl.java:4808)
        at android.view.ViewRootImpl$InputStage.apply(ViewRootImpl.java:4816)
        at android.view.ViewRootImpl$InputStage.deliver(ViewRootImpl.java:4789)
        at android.view.ViewRootImpl.deliverInputEvent(ViewRootImpl.java:7512)
        at android.view.ViewRootImpl.doProcessInputEvents(ViewRootImpl.java:7481)
        at android.view.ViewRootImpl.enqueueInputEvent(ViewRootImpl.java:7442)
        at android.view.ViewRootImpl$WindowInputEventReceiver.onInputEvent(ViewRootImpl.java:7637)
        at android.view.InputEventReceiver.dispatchInputEvent(InputEventReceiver.java:189)
        at android.os.MessageQueue.nativePollOnce(Native Method)
        at android.os.MessageQueue.next(MessageQueue.java:336)
        at android.os.Looper.loop(Looper.java:174)
        at android.app.ActivityThread.main(ActivityThread.java:7403)
        at java.lang.reflect.Method.invoke(Native Method)
        at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:492)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:935)

0 Answers0