Странный IndexOutOfBoundsException при использовании двух редактируемых текстов - PullRequest
1 голос
/ 23 января 2020

РЕДАКТИРОВАТЬ Ошибка по-прежнему отображается даже без копирования и вставки.

illustration

Я пытаюсь создать простое приложение-конвертер, и все работает нормально, но я обнаружил проблему, которую, похоже, не могу найти способ вперед. Вот logcat:

2020-01-22 22:45:50.905 15060-15060/com.example.unitconverter E/may be Problem: 10.220462 
2020-01-22 22:45:50.911 15060-15060/com.example.unitconverter E/text: 4.635924 //shows the text is set correctly
2020-01-22 22:45:50.924 15060-15060/com.example.unitconverter E/AndroidRuntime: FATAL EXCEPTION: 
main
Process: com.example.unitconverter, PID: 15060
java.lang.IndexOutOfBoundsException
    at android.graphics.Paint.getRunAdvance(Paint.java:2861)
    at android.text.TextLine.getRunAdvance(TextLine.java:848)
    at android.text.TextLine.handleText(TextLine.java:897)
    at android.text.TextLine.handleRun(TextLine.java:1144)
    at android.text.TextLine.measureRun(TextLine.java:531)
    at android.text.TextLine.measure(TextLine.java:327)
    at android.text.Layout.getHorizontal(Layout.java:1199)
    at android.text.Layout.getHorizontal(Layout.java:1177)
    at android.text.Layout.getPrimaryHorizontal(Layout.java:1148)
    at android.text.Layout.getCursorPath(Layout.java:1816)
    at android.widget.TextView.getUpdatedHighlightPath(TextView.java:7784)
    at android.widget.TextView.onDraw(TextView.java:8091)
    at android.view.View.draw(View.java:21875)
    at android.view.View.updateDisplayListIfDirty(View.java:20748)
    at android.view.ViewGroup.recreateChildDisplayList(ViewGroup.java:4542)
    at android.view.ViewGroup.dispatchGetDisplayList(ViewGroup.java:4514)
    at android.view.View.updateDisplayListIfDirty(View.java:20703)
    at android.view.View.draw(View.java:21601)
    at android.view.ViewGroup.drawChild(ViewGroup.java:4558)
    at android.view.ViewGroup.dispatchDraw(ViewGroup.java:4333)
    at android.view.View.draw(View.java:21878)
    at com.google.android.material.textfield.TextInputLayout.draw(TextInputLayout.java:3614)
    at android.view.View.updateDisplayListIfDirty(View.java:20748)
    at android.view.ViewGroup.recreateChildDisplayList(ViewGroup.java:4542)
    at android.view.ViewGroup.dispatchGetDisplayList(ViewGroup.java:4514)
    at android.view.View.updateDisplayListIfDirty(View.java:20703)
    at android.view.ViewGroup.recreateChildDisplayList(ViewGroup.java:4542)
    at android.view.ViewGroup.dispatchGetDisplayList(ViewGroup.java:4514)
    at android.view.View.updateDisplayListIfDirty(View.java:20703)
    at android.view.ViewGroup.recreateChildDisplayList(ViewGroup.java:4542)
    at android.view.ViewGroup.dispatchGetDisplayList(ViewGroup.java:4514)
    at android.view.View.updateDisplayListIfDirty(View.java:20703)
    at android.view.ViewGroup.recreateChildDisplayList(ViewGroup.java:4542)
    at android.view.ViewGroup.dispatchGetDisplayList(ViewGroup.java:4514)
    at android.view.View.updateDisplayListIfDirty(View.java:20703)
    at android.view.ViewGroup.recreateChildDisplayList(ViewGroup.java:4542)
    at android.view.ViewGroup.dispatchGetDisplayList(ViewGroup.java:4514)
    at android.view.View.updateDisplayListIfDirty(View.java:20703)
    at android.view.ViewGroup.recreateChildDisplayList(ViewGroup.java:4542)
    at android.view.ViewGroup.dispatchGetDisplayList(ViewGroup.java:4514)
    at android.view.View.updateDisplayListIfDirty(View.java:20703)
    at android.view.ViewGroup.recreateChildDisplayList(ViewGroup.java:4542)
    at android.view.ViewGroup.dispatchGetDisplayList(ViewGroup.java:4514)
    at android.view.View.updateDisplayListIfDirty(View.java:20703)
    at android.view.ViewGroup.recreateChildDisplayList(ViewGroup.java:4542)
    at android.view.ViewGroup.dispatchGetDisplayList(ViewGroup.java:4514)
    at android.view.View.updateDisplayListIfDirty(View.java:20703)
    at android.view.ThreadedRenderer.updateViewTreeDisplayList(ThreadedRenderer.java:725)
    at android.view.ThreadedRenderer.updateRootDisplayList(ThreadedRenderer.java:731)
    at android.view.ThreadedRenderer.draw(ThreadedRenderer.java:840)
    at android.view.ViewRootImpl.draw(ViewRootImpl.java:3951)
    at android.view.ViewRootImpl.performDraw(ViewRootImpl.java:3725)
    at android.view.ViewRootImpl.performTraversals(ViewRootImpl.java:3034)
    at android.view.ViewRootImpl.doTraversal(ViewRootImpl.java:1897)
    at android.view.ViewRootImpl$TraversalRunnable.run(ViewRootImpl.java:8516)
    at android.view.Choreographer$CallbackRecord.run(Choreographer.java:986)
    at android.view.Choreographer.doCallbacks(Choreographer.java:764)
    at android.view.Choreographer.doFrame(Choreographer.java:699)
    at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:965)
    2020-01-22 22:45:50.925 15060-15060/com.example.unitconverter E/AndroidRuntime:     at 
android.os.Handler.handleCallback(Handler.java:873)
    at android.os.Handler.dispatchMessage(Handler.java:99)
    at android.os.Looper.loop(Looper.java:214)
    at android.app.ActivityThread.main(ActivityThread.java:7099)
    at java.lang.reflect.Method.invoke(Native Method)
    at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:494)
    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:965)

Поскольку прикрепленные ссылки не указывают на мой код (большинство из них указывают на блоки комментариев в исходном коде android), я не уверен, какой код для публикации ..

Вот файл XML, содержащий действие.

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.motion.widget.MotionLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/convert_parent"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="#DD9911"
    app:layoutDescription="@xml/convert_scene"
    app:motionProgress="1"
    tools:context=".ConvertActivity">

    <androidx.appcompat.widget.Toolbar
        android:id="@+id/convert_app_bar"
        android:layout_width="match_parent"
        android:layout_height="66dp"
        android:elevation="4dp"
        android:gravity="center"
        android:paddingStart="0dp"
        android:paddingEnd="8dp"
        android:paddingBottom="4dp"
        android:theme="@style/ThemeOverlay.AppCompat.ActionBar"
        app:layout_constraintBottom_toTopOf="@id/convertScroll"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:popupTheme="@style/ThemeOverlay.AppCompat.DayNight" />

    <TextView
        android:id="@+id/convert_header"
        android:layout_width="0dp"
        android:layout_height="50dp"
        android:fontFamily="sans-serif-light"
        android:gravity="center"
        android:lineSpacingMultiplier="0.88"
        android:text="@string/angularAcceleration"
        android:textAlignment="center"
        android:textAppearance="@style/TextAppearance.AppCompat.Body2"
        android:textColor="#030303"
        android:textSize="38sp" />

    <TextView
        android:id="@+id/app_bar_text"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:alpha="0"
        android:text="@string/mass"
        android:textAlignment="center"
        android:textAppearance="@style/TextAppearance.AppCompat.Medium"
        android:textColor="#030303"
        android:textSize="20sp"
        app:layout_constraintStart_toStartOf="@+id/convert_app_bar"
        tools:layout_editor_absoluteY="219dp" />

    <androidx.constraintlayout.widget.Guideline
        android:id="@+id/convert_app_barGuideline"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:orientation="horizontal"
        app:layout_constraintGuide_percent="0.3" />

    <com.example.unitconverter.subclasses.ConvertScrollView
        android:id="@+id/convertScroll"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:background="@drawable/rounded_front"
        android:theme="@style/Theme.MaterialComponents.Light.NoActionBar"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintHorizontal_bias="0.0"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@id/convert_app_bar">

        <androidx.constraintlayout.widget.ConstraintLayout
            android:id="@+id/convert_inner"
            android:layout_width="match_parent"
            android:splitMotionEvents="false"
            android:layout_height="wrap_content">

            <androidx.constraintlayout.widget.Guideline
                android:id="@+id/topGuide"
                android:layout_width="0dp"
                android:layout_height="0dp"
                android:orientation="horizontal"
                app:layout_constraintGuide_percent="0.1" />

            <com.google.android.material.textfield.TextInputLayout
                android:id="@+id/firstBox"
                style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox"
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                app:boxCornerRadiusBottomEnd="10dp"
                app:boxCornerRadiusBottomStart="10dp"
                app:boxCornerRadiusTopEnd="10dp"
                app:boxCornerRadiusTopStart="10dp"
                app:boxStrokeColor="#F8BBD0"
                app:hintTextColor="@android:color/black"
                app:layout_constraintEnd_toEndOf="parent"
                app:layout_constraintHorizontal_bias="0.2"
                app:layout_constraintStart_toStartOf="parent"
                app:layout_constraintTop_toTopOf="@id/topGuide"
                app:layout_constraintWidth_percent="0.85">

                <com.google.android.material.textfield.TextInputEditText
                    android:id="@+id/firstEditText"
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:inputType="text"
                    android:letterSpacing="0.06"
                    android:paddingStart="17dp"
                    android:paddingEnd="52dp"
                    android:textSize="18sp" />
            </com.google.android.material.textfield.TextInputLayout>

            <com.google.android.material.textfield.TextInputLayout
                android:id="@+id/secondBox"
                style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox"
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:layout_marginTop="40dp"
                app:boxCornerRadiusBottomEnd="10dp"
                app:boxCornerRadiusBottomStart="10dp"
                app:boxCornerRadiusTopEnd="10dp"
                app:boxCornerRadiusTopStart="10dp"
                app:boxStrokeColor="#F8BBD0"
                app:boxStrokeErrorColor="@color/design_default_color_primary_dark"
                app:hintTextColor="@android:color/black"
                app:layout_constraintBottom_toBottomOf="parent"
                app:layout_constraintEnd_toEndOf="parent"
                app:layout_constraintHorizontal_bias="0.2"
                app:layout_constraintStart_toStartOf="parent"
                app:layout_constraintTop_toBottomOf="@id/firstBox"
                app:layout_constraintWidth_percent="0.85">

                <com.google.android.material.textfield.TextInputEditText
                    android:id="@+id/secondEditText"
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:inputType="text"
                    android:letterSpacing="0.06"
                    android:paddingStart="17dp"
                    android:paddingEnd="52dp"
                    android:textSize="18sp" />
            </com.google.android.material.textfield.TextInputLayout>

            <TextView
                android:id="@+id/topTextView"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:gravity="center"
                android:maxWidth="43dp"
                app:layout_constraintBottom_toBottomOf="@+id/firstBox"
                app:layout_constraintEnd_toEndOf="@+id/firstBox"
                app:layout_constraintHorizontal_bias="0.97"
                app:layout_constraintStart_toStartOf="@id/firstBox"
                app:layout_constraintTop_toTopOf="@+id/firstBox"
                app:layout_constraintVertical_bias="0.52" />

            <TextView
                android:id="@+id/bottomTextView"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:gravity="center"
                android:maxWidth="43dp"
                app:layout_constraintBottom_toBottomOf="@+id/secondBox"
                app:layout_constraintEnd_toEndOf="@id/secondBox"
                app:layout_constraintHorizontal_bias="0.97"
                app:layout_constraintStart_toStartOf="@id/secondBox"
                app:layout_constraintTop_toTopOf="@+id/secondBox"
                app:layout_constraintVertical_bias="0.49" />

            <com.google.android.material.button.MaterialButton
                android:id="@+id/top_button"
                style="@style/Widget.MaterialComponents.Button.TextButton"
                android:layout_width="42dp"
                android:layout_height="52dp"
                android:layout_marginStart="5dp"
                android:layout_marginEnd="5dp"
                android:insetLeft="2dp"
                android:insetRight="0dp"
                android:paddingLeft="-7dp"
                android:paddingTop="0dp"
                android:paddingRight="0dp"
                android:paddingBottom="0dp"
                app:cornerRadius="12dp"
                app:icon="@drawable/arrow_down"
                app:layout_constraintBottom_toBottomOf="@+id/firstBox"
                app:layout_constraintEnd_toEndOf="parent"
                app:layout_constraintStart_toEndOf="@id/firstBox"
                app:layout_constraintTop_toTopOf="@id/topGuide"
                app:strokeWidth="2dp" />

            <com.google.android.material.button.MaterialButton
                android:id="@+id/bottom_button"
                style="@style/Widget.MaterialComponents.Button.TextButton"
                android:layout_width="42dp"
                android:layout_height="52dp"
                android:layout_marginStart="5dp"
                android:layout_marginEnd="5dp"
                android:insetLeft="2dp"
                android:insetRight="0dp"
                android:paddingLeft="-7dp"
                android:paddingTop="0dp"
                android:paddingRight="0dp"
                android:paddingBottom="0dp"
                app:cornerRadius="12dp"
                app:icon="@drawable/arrow_down"
                app:layout_constraintBottom_toBottomOf="@+id/secondBox"
                app:layout_constraintEnd_toEndOf="parent"
                app:layout_constraintStart_toEndOf="@id/secondBox"
                app:layout_constraintTop_toTopOf="@id/secondBox"
                app:strokeWidth="2dp" />

        </androidx.constraintlayout.widget.ConstraintLayout>
    </com.example.unitconverter.subclasses.ConvertScrollView>
</androidx.constraintlayout.motion.widget.MotionLayout>

Вот фильтр, используемый для редактирования текста

    //filters
    fun filters(comma: Char, fullStop: Char, editText: TextInputEditText): Array<InputFilter> {
        val filter = InputFilter { source, start, end, _, _, _ ->
            val stringBuilder = StringBuilder(end - start)
            var count = 0

            for (i in start until end) {
                if (source[i].isDigit() ||
                    source[i] == comma ||
                    source[i] == fullStop
                ) {
                    if (source[i] == fullStop) {
                        // ensures only one decimal separator is in the text
                        count++
                        if (count >= 2) continue
                        if (source.length > 1 && editText.isFocused) {
                            val text = editText.text
                            if (text != null
                                && text.contains(fullStop)
                            ) continue
                        }
                    }
                    stringBuilder.append(source[i])
                }
            }
            stringBuilder
        }
        val lengthFilter = InputFilter.LengthFilter(50)

        return arrayOf(filter, lengthFilter)
    }

И затем в деятельности.kt

class ConvertActivity : AppCompatActivity(), ConvertFragment.ConvertDialogInterface {
    private var swap = false
    private var randomColor = -1
    private var viewId = -1
    private lateinit var dialog: ConvertFragment
    private var isPrefix = false
    private val bundle = Bundle()
    lateinit var function: (String) -> String
    var groupingSeparator by Delegates.notNull<Char>()
    var decimalSeparator by Delegates.notNull<Char>()

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_convert)
        setSupportActionBar(convert_app_bar)
        supportActionBar?.apply {
            setDisplayHomeAsUpEnabled(true)
            setDisplayShowTitleEnabled(false)
        }
        setSeparators()
        firstEditText.apply {
            setRawInputType(Configuration.KEYBOARD_12KEY)
            filters = filters(groupingSeparator, decimalSeparator,this)
        }
        secondEditText.apply {
            filters = filters(groupingSeparator, decimalSeparator,this)
            setRawInputType(Configuration.KEYBOARD_12KEY)
        }
        getTextWhileTyping()
    }
         ... // other methods

         inner class CommonWatcher(editText: EditText, private val secondEditText: EditText) :
        SeparateThousands(editText, groupingSeparator, decimalSeparator) {
        override fun afterTextChanged(s: Editable?) {
            super.afterTextChanged(s)
            s?.toString()?.apply {
                this.removeCommas(decimalSeparator)?.also {
                    Log.e("may be Problem", it)
                    secondEditText.setText(callBack(function, it))
                    Log.e("text","${secondEditText.text}")
                    }
                }
            }
        }// end of inner class

        private fun getTextWhileTyping() {
            firstEditText.apply {
                firstWatcher = CommonWatcher(this, secondEditText)
                setOnFocusChangeListener { _, hasFocus ->
                    firstBoolean = hasFocus
                    if (hasFocus) {
                        addTextChangedListener(firstWatcher)
                        secondEditText.removeTextChangedListener(secondCommonWatcher)
                        reverse = false
                    }
                }
            }
            secondEditText.apply {
                secondCommonWatcher = CommonWatcher(this, firstEditText)
                setOnFocusChangeListener { _, hasFocus ->
                    secondBoolean = hasFocus
                    if (hasFocus) {
                        addTextChangedListener(secondCommonWatcher)
                        firstEditText.removeTextChangedListener(firstWatcher)
                        reverse = true
                    }
                 }
             } 
         }
     }
...