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,触发原因是文本绘制时索引越界,可尝试以下修复方式:
禁用硬件加速:
针对lyrics_editor所在的TextInputLayout禁用硬件加速,避免绘制时的索引计算错误:// 在dialog.show()之前添加 view.findViewById<TextInputLayout>(R.id.lyrics_input_layout).setLayerType(View.LAYER_TYPE_SOFTWARE, null)文本预处理:
清理已有歌词文本中的异常换行或特殊字符,避免绘制解析错误:val cleanedLyrics = lyrics?.replace("\r\n", "\n")?.trim() ?: "" lyricsEditor.setText(cleanedLyrics)延迟设置文本:
延迟到视图绘制完成后再设置文本,避免初始化阶段的绘制冲突:lyricsEditor.post { lyricsEditor.setText(lyrics) lyricsEditor.textAlignment = View.TEXT_ALIGNMENT_TEXT_START }降级Material组件版本:
若使用高版本Material Components,尝试降级到1.4.0等稳定版本,减少新组件与旧系统的兼容性问题。
内容的提问来源于stack exchange,提问作者Rj Stha
相关产品推荐
相关产品推荐

