1

EDIT Error still shows even without copy and paste.

illustration

I'm trying to build a simple converter app and everything works fine but I discovered a problem which I can't seem to find the way forward. Here's the 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)

Since the links attached doesn't point to my code (most of them point to comment blocks in the android source code) I'm not sure what code to post..

Here's the XML file containing the activity.

<?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>

Here's the filter used on the edit text

    //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)
    }

And then in the activity.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
                    }
                 }
             } 
         }
     }
Kofi
  • 525
  • 6
  • 15
  • Does it happen if you refer to [`dest`](https://developer.android.com/reference/android/text/InputFilter.html#filter(java.lang.CharSequence,%20int,%20int,%20android.text.Spanned,%20int,%20int)) rather than `editText.text`? – greeble31 Jan 23 '20 at 04:29
  • Please have a look at [this](https://stackoverflow.com/q/14980227/7948109). Sorry I don't know kotlin but you can try - clear previous text then paste new one. 1 question if you write directly in pounds it will convert it to kilos? – Rahul Gaur Jan 23 '20 at 05:47
  • @RahulGaur yes it will. When you input in the first edit text, the text of the second one changes and vice versa – Kofi Jan 23 '20 at 06:34
  • Have you checked the question? maybe you have to remove previous text and then paste new one, also add `try catch` around your logic – Rahul Gaur Jan 23 '20 at 06:42
  • I tried clearing the text before setting it but the error still shows. I've tried `try catch` almost everywhere in the code but since the error doesn't point to my code, no exception is being caught with the try statement – Kofi Jan 23 '20 at 06:48
  • @greeble31 `for (i in start until end) { if (source[i].isDigit() ||source[i] == comma ||source[i] == fullStop ) { if (source[i] == fullStop) { count++ if (count >= 2) continue } stringBuilder.append(source[i]) }`. This still throws that error(without editText.text) – Kofi Jan 23 '20 at 06:50
  • Hi @Kofi did you find a solution to this? I am finding a similar issue too. – blackpanther Aug 14 '20 at 17:25
  • @blackpanther I think it's from the android drawing the texts in the text view itself not necessarily the code. The exception is thrown on the `onDraw()` method when the text is modified(You can test that by extending `EditText` and putting a `try catch` block on the `super.onDraw` method) . You should try and control how you modify the texts especially when deleting characters as deleting characters have a higher tendency to throw that exception. – Kofi Aug 18 '20 at 14:44

0 Answers0