You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Android 8.0三星Experience 9.0编辑TextInputEditText时应用崩溃

Android 8.0三星Experience 9.0系统下EditText编辑已有数据崩溃问题

问题现象:

  • 仅在Android 8.0(Oreo)三星Experience 9.0系统中触发,其他Android版本运行正常
  • 编辑lyrics_editor对应的已有数据时应用必现崩溃,新建编辑器无异常
  • 尝试将控件类型从EditText改为TextInputEditText后,问题仍未解决

Logcat报错信息:

2023-01-30 14:03:54.222 AndroidRuntime E  FATAL EXCEPTION: main
   Process: com.rj.mysongbook, PID: 12196
   java.lang.ArrayIndexOutOfBoundsException: length=9; index=-1
    at android.text.DynamicLayout.getBlockIndex(DynamicLayout.java:646)
    at android.widget.Editor.drawHardwareAccelerated(Editor.java:1820)
    at android.widget.Editor.onDraw(Editor.java:1789)
    at android.widget.TextView.onDraw(TextView.java:7779)
    at android.view.View.draw(View.java:20370)
    at android.view.View.updateDisplayListIfDirty(View.java:19315)
    at android.view.View.draw(View.java:20093)
    at android.view.ViewGroup.drawChild(ViewGroup.java:4421)
    at android.view.ViewGroup.dispatchDraw(ViewGroup.java:4207)
    at android.view.View.updateDisplayListIfDirty(View.java:19306)
    at android.view.View.draw(View.java:20093)
    at android.view.ViewGroup.drawChild(ViewGroup.java:4421)
    at android.view.ViewGroup.dispatchDraw(ViewGroup.java:4207)
    at android.view.View.draw(View.java:20373)
    at com.google.android.material.textfield.TextInputLayout.draw(TextInputLayout.java:3906)
    at android.view.View.updateDisplayListIfDirty(View.java:19315)
    at android.view.View.draw(View.java:20093)
    at android.view.ViewGroup.drawChild(ViewGroup.java:4421)
    at android.view.ViewGroup.dispatchDraw(ViewGroup.java:4207)
    at android.view.View.updateDisplayListIfDirty(View.java:19306)
    at android.view.View.draw(View.java:20093)
    at android.view.ViewGroup.drawChild(ViewGroup.java:4421)
    at android.view.ViewGroup.dispatchDraw(ViewGroup.java:4207)
    at android.view.View.updateDisplayListIfDirty(View.java:19306)
    at android.view.View.draw(View.java:20093)
    at android.view.ViewGroup.drawChild(ViewGroup.java:4421)
    at android.view.ViewGroup.dispatchDraw(ViewGroup.java:4207)
    at android.view.View.updateDisplayListIfDirty(View.java:19306)
    at android.view.View.draw(View.java:20093)
    at android.view.ViewGroup.drawChild(ViewGroup.java:4421)
    at android.view.ViewGroup.dispatchDraw(ViewGroup.java:4207)
    at android.view.View.updateDisplayListIfDirty(View.java:19306)
    at android.view.View.draw(View.java:20093)
    at android.view.ViewGroup.drawChild(ViewGroup.java:4421)
    at android.view.ViewGroup.dispatchDraw(ViewGroup.java:4207)
    at android.view.View.updateDisplayListIfDirty(View.java:19306)
    at android.view.View.draw(View.java:20093)
    at android.view.ViewGroup.drawChild(ViewGroup.java:4421)
    at android.view.ViewGroup.dispatchDraw(ViewGroup.java:4207)
    at android.view.View.updateDisplayListIfDirty(View.java:19306)
    at android.view.View.draw(View.java:20093)
    at android.view.ViewGroup.drawChild(ViewGroup.java:4421)
    at android.view.ViewGroup.dispatchDraw(ViewGroup.java:4207)
    at android.view.View.draw(View.java:20373)
    at com.android.internal.policy.DecorView.draw(DecorView.java:980)
    at android.view.View.updateDisplayListIfDirty(View.java:19315)
    at android.view.ThreadedRenderer.updateViewTreeDisplayList(ThreadedRenderer.java:686)
    at android.view.ThreadedRenderer.updateRootDisplayList(ThreadedRenderer.java:692)
    at android.view.ThreadedRenderer.draw(ThreadedRenderer.java:800)
    at android.view.ViewRootImpl.draw(ViewRootImpl.java:3501)
    at android.view.ViewRootImpl.performDraw(ViewRootImpl.java:3288)
    at android.view.ViewRootImpl.performTraversals(ViewRootImpl.java:2823)
    at android.view.ViewRootImpl.doTraversal(ViewRootImpl.java:1785)
    at android.view.ViewRootImpl$TraversalRunnable.run(ViewRootImpl.java:7832)
    at android.view.Choreographer$CallbackRecord.run(Choreographer.java:911)
    at android.view.Choreographer.doCallbacks(Choreographer.java:723)
    at android.view.Choreographer.doFrame(Choreographer.java:658)
    at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:897)
    at android.os.Handler.handleCallback(Handler.java:789)
    at android.os.Handler.dispatchMessage(Handler.java:98)
    at android.os.Looper.loop(Looper.java:164)
    at android.app.ActivityThread.main(ActivityThread.java:6944)
    at java.lang.reflect.Method.invoke(Native Method)
    at com.android.internal.os.Zygote$MethodAndArgsCaller.run(Zygote.java:327)
    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1374)

布局代码(editor_layout.xml):

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">

<com.google.android.material.textfield.TextInputLayout
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layout_margin="10dp"
    android:hint="@string/lyrics_title"
    android:textColorHint="#EF1AF6"
    app:hintTextColor="@color/red">

    <com.google.android.material.textfield.TextInputEditText
        android:id="@+id/title_editor"
        style="@style/lyricsTvStyle"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:background="@drawable/square_edittext"
        android:textColor="@color/black" />
</com.google.android.material.textfield.TextInputLayout>


<com.google.android.material.textfield.TextInputLayout
    android:id="@+id/lyrics_input_layout"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layout_margin="10dp"
    android:hint="@string/song_lyrics"
    android:textColorHint="#EF1AF6"
    app:hintTextColor="@color/red">

    <com.google.android.material.textfield.TextInputEditText
        android:id="@+id/lyrics_editor"
        style="@style/lyricsTvStyle"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:background="@drawable/square_edittext"
        android:textColor="@color/blue" />
</com.google.android.material.textfield.TextInputLayout 
</LinearLayout>

业务代码:

private fun showEditorNew(
    songContainer: SongContainer,
    isDelete: Boolean,
) {
    var songNumber = songContainer.songNumber
    val songTypes = songContainer.songType
    val othersType = songContainer.othersType
    val title = songContainer.title
    val lyrics = songContainer.lyrics
    val engLyrics = songContainer.engLyrics
    val motherNode = songContainer.motherNode


    val builder = AlertDialog.Builder(requireActivity(), R.style.DialogTheme)
    val view = LayoutInflater.from(activity).inflate(R.layout.editor_layout, null)
    view.keepScreenOn = true
    val titleEditor = view.findViewById<EditText>(R.id.title_editor)
    val lyricsEditor = view.findViewById<EditText>(R.id.lyrics_editor)

    titleEditor.setText(title)
    lyricsEditor.setText(lyrics)

    lyricsEditor.textAlignment = View.TEXT_ALIGNMENT_TEXT_START
    builder.setView(view)
    builder.setCustomTitle(requireContext().setCustomTitle("Song Editor"))
    builder.setCancelable(false)

    var posButton = "Edit"
    var snackMsg =
        "Edited!!! if you want to update, Click Edit Again!!!\nOr Click Cancel to go back!!!"
    if (isDelete) {
        posButton = "Delete"
        snackMsg =
            "Are you Sure to Delete?, Click Delete Again!!!\nOr Click Cancel to go back!!!"
    } else {
        builder.setNeutralButton(
            "Preview"
        ) { dialogInterface: DialogInterface?, i: Int -> }
    }
    builder.setPositiveButton(
        posButton
    ) { dialogInterface: DialogInterface?, i: Int -> }

    builder.setNegativeButton(
        "Cancel"
    ) { dialogInterface: DialogInterface, i: Int -> dialogInterface.dismiss() }
    val dialog = builder.create()
    dialog.show()

    dialog.getButton(DialogInterface.BUTTON_POSITIVE).setOnClickListener {
        if (isEditPermitted()) {
            if (!IsInternetAvailable(requireContext()).isConnected()) {
                showToast(getString(R.string.no_active_connection))
            } /*else if (!isEdit[0]) {
                showSnackBar(
                    snackMsg,
                    view,
                    true
                )
                isEdit[0] = true
                lyricsEditor.isFocusable = true
            }*/ else {
                val newTitle = titleEditor.text.toString().trim()
                val newLyrics = lyricsEditor.text.toString().trim()

                showToast("Editing in the server!!!")
                val songToDb =
                    HashMap<String, String>()
                songToDb["title"] = newTitle
                songToDb["lyrics"] = newLyrics

                if (songTypes != "null") {
                    songToDb["songType"] = songTypes
                }
                if (othersType != "null") {
                    songToDb["othersType"] = othersType
                }
                if (songNumber.contains(".")) {
                    songNumber = songNumber.split(".")[0].trim()
                }
                if (songNumber != "null") {
                    songToDb["songNumber"] = songNumber
                }
                if (engLyrics != "null") {
                    songToDb["engLyrics"] = engLyrics
                }

                val previewConfirm = requireContext().showWebPreview(newTitle, newLyrics)
                previewConfirm.setTitle("Are Sure to $posButton?")
                    .setIcon(R.drawable.ic_warning)
                previewConfirm.setPositiveButton("OK") { _, _ ->
                    newEditDb(motherNode, songToDb, dialog, isDelete)
                }
                    .setNegativeButton("Cancel", null)
                    .show()

            }
        }
    }

    dialog.getButton(DialogInterface.BUTTON_NEUTRAL).setOnClickListener { view1: View? ->
        if (titleEditor.text.isEmpty()) {
            requireContext().showAlert("No Title?", "Please give a title for this song")
        } else if (lyricsEditor.text.isEmpty()) {
            requireContext().showAlert("No Lyrics?", "Please write some Lyrics or paste here")
        } else {
            requireContext().showWebPreview(
                titleEditor.text.toString(),
                lyricsEditor.text.toString()
            )
                .setPositiveButton("OK", null)
                .show()
        }
    }
}

解决方案:
该崩溃是Android 8.0三星定制系统的DynamicLayout绘制bug,触发原因是文本绘制时索引越界,可尝试以下修复方式:

  1. 禁用硬件加速:
    针对lyrics_editor所在的TextInputLayout禁用硬件加速,避免绘制时的索引计算错误:

    // 在dialog.show()之前添加
    view.findViewById<TextInputLayout>(R.id.lyrics_input_layout).setLayerType(View.LAYER_TYPE_SOFTWARE, null)
    
  2. 文本预处理:
    清理已有歌词文本中的异常换行或特殊字符,避免绘制解析错误:

    val cleanedLyrics = lyrics?.replace("\r\n", "\n")?.trim() ?: ""
    lyricsEditor.setText(cleanedLyrics)
    
  3. 延迟设置文本:
    延迟到视图绘制完成后再设置文本,避免初始化阶段的绘制冲突:

    lyricsEditor.post {
        lyricsEditor.setText(lyrics)
        lyricsEditor.textAlignment = View.TEXT_ALIGNMENT_TEXT_START
    }
    
  4. 降级Material组件版本:
    若使用高版本Material Components,尝试降级到1.4.0等稳定版本,减少新组件与旧系统的兼容性问题。


内容的提问来源于stack exchange,提问作者Rj Stha

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.02 22:50:23