文章目录
前言:
本文主要记录Compose开发过程中遇到的问题,以及解决方案。
一、开发
1、崩溃 AndroidRuntime: FATAL EXCEPTION
在fragmen.xml添加ComposeView控件并在Fragment的onViewCreated()方法中调用’mBinding.vCompose.setContent {}’ 导致APP崩溃;
崩溃信息
2022-02-14 10:43:15.686 15864-15864/? E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.***.***, PID: 15864
java.lang.NoSuchMethodError: No virtual method setContent(Lkotlin/jvm/functions/Function0;)V in class Landroidx/compose/ui/platform/ComposeView; or its super classes (declaration of 'androidx.compose.ui.platform.ComposeView' appears in /data/app/com.***.***-sVGybdTDL2rvz-Fc4EzVSA==/base.apk)
at com.***.***.ui.MainFragment.initView(MainFragment.kt:78)
at com.***.***.ui.BaseFragment.onViewCreated(BaseFragment.kt:55)
at androidx.fragment.app.Fragment.performViewCreated(Fragment.java:3047)
at androidx.fragment.app.FragmentStateManager.createView(FragmentStateManager.java:551)
at androidx.fragment.app.FragmentStateManager.moveToExpectedState(FragmentStateManager.java:261)
at androidx.fragment.app.FragmentStore.moveToExpectedState(FragmentStore.java:113)
at androidx.fragment.app.FragmentManager.moveToState(FragmentManager.java:1387)
at androidx.fragment.app.FragmentManager.dispatchStateChange(FragmentManager.java:2876)
at androidx.fragment.app.FragmentManager.dispatchViewCreated(FragmentManager.java:2802)
at androidx.fragment.app.Fragment.performViewCreated(Fragment.java:3048)
at androidx.fragment.app.FragmentStateManager.createView(FragmentStateManager.java:551)
at androidx.fragment.app.FragmentStateManager.moveToExpectedState(FragmentStateManager.java:261)
at androidx.fragment.app.FragmentStore.moveToExpectedState(FragmentStore.java:113)
at androidx.fragment.app.FragmentManager.moveToState(FragmentManager.java:1387)
at androidx.fragment.app.FragmentManager.dispatchStateChange(FragmentManager.java:2876)
at androidx.fragment.app.FragmentManager.dispatchActivityCreated(FragmentManager.java:2809)
at androidx.fragment.app.FragmentController.dispatchActivityCreated(FragmentController.java:262)
at androidx.fragment.app.FragmentActivity.onStart(FragmentActivity.java:445)
at androidx.appcompat.app.AppCompatActivity.onStart(AppCompatActivity.java:246)
at android.app.Instrumentation.callActivityOnStart(Instrumentation.java:1521)
at android.app.Activity.performStart(Activity.java:7838)
at android.app.ActivityThread.handleStartActivity(ActivityThread.java:3399)
at android.app.servertransaction.TransactionExecutor.performLifecycleSequence(TransactionExecutor.java:221)
at android.app.servertransaction.TransactionExecutor.cycleToPath(TransactionExecutor.java:201)
at android.app.servertransaction.TransactionExecutor.executeLifecycleState(TransactionExecutor.java:173)
at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:97)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2110)
at android.os.Handler.dispatchMessage(Handler.java:107)
at android.os.Looper.loop(Looper.java:214)
at android.app.ActivityThread.main(ActivityThread.java:7697)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:516)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:950)
原因
是没有启用Compose;
解决方法: build.gradle(:app)文件中配置以下内容:
composeOptions{
kotlinCompilerExtensionVersion "$compose_version"
kotlinCompilerVersion kotlin_version
}
buildFeatures{
// 启用Jetpack Compose
compose true
}
由于使用的kotlin_version是1.6.10版本,compose_version也要升级至’1.1.0’。
2、下载文件至相册
1、MANAGE_EXTERNAL_STORAGE权限
- Android 11 新权限MANAGE_EXTERNAL_STORAGE,并且此权限只能跳至设置页开启;
- 在Android13, WRITE_EXTERNAL_STORAGE权限会失效;
- Android 11+ 请配置清单权限
<uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE" />
- 判断是否开启MANAGE_EXTERNAL_STORAGE权限;
if (Build.VERSION.SDK_INT >= 30) {
if (!Environment.isExternalStorageManager()) {
val intent = Intent(Settings.ACTION_MANAGE_ALL_FILES_ACCESS_PERMISSION)
startActivity(intent)
return
}
mLoadingDialog?.show(activity.supportFragmentManager, "loading")
startDownloadVideo("${mVideoInfo!!.works_video_id}.mp4")
return
}
- google play 不建议使用此权限(会被拒):文件管理器应用除外;
具体政策,和替代方案:具体政策 - 参考:
权限MANAGE_EXTERNAL_STORAGE
各版本安卓读写SD卡适配
3、WebView 闪白屏
表现:进入含有webView的页面,白屏,同时会闪烁;如图所示,定义webView为红色区域大小,但加载后,却导致全屏背景白色,在滑动网页后,白色才会消失。
此问题仅compose页面会出现,传统xml布局页面无此问题。
解决方案:给WebView之外包一层Surface用来控制大小,则不会导致“整页白屏”了,示例代码如下。
Surface(modifier = Modifier.padding(50.dp)){
val webViewState = rememberWebViewState(url = url)
WebView(state = webViewState)
}
4、IDEA 运行Compose For Desktop桌面项目报错
错误信息
ComposeComponentRegistrar has been compiled by a more recent version of the Java Runtime (class file version 55.0), this version of the Java Runtime only recognizes class file versions up to 52.0
原因:
项目SDK版本与gradle的java 运行时版本不匹配导致的。
解决方案:
在idea设置中修改gradle JVM 版本。 我这里项目使用的JDK 17, 所以也改为17即可。如下图所示:
5、IDEA 运行Compose For Desktop桌面项目报错
错误信息
warning: default scripting plugin is disabled: The provided plugin org.jetbrains.kotlin.scripting.compiler.plugin.ScriptingCompilerConfigurationComponentRegistrar is not compatible with this version of compiler
error: unable to evaluate script, no scripting plugin loaded
原因:
运行当前文件不是程序入口。
解决方案:
切换到Main.kt选项卡,再次点击“运行”。如下图所示:
6、在TV中,左右切换焦点,列表会上下跳动
问题描述:
在TV中,左右切换焦点,列表会上下跳动;
也就是使用animateFloatAsState将缩放应用于TvLazyRow中的列表项会使布局在Y轴上移动;
解决方法:
- TV-Compose库现在提供 ,并且可以处理缩放,而不会遇到此问题的控件:Cards Buttons Surface
- 解决此问题的另一种方法是避免直接用scale修饰符,应使用scale在drawWithContent修饰语中。
@Composable
fun Modifier.scaleOnFocus(
scaleOnFocus: Float,
animationSpec: AnimationSpec<Float> = tween()
): Modifier {
var isFocused by remember { mutableStateOf(false) }
val scale by animateFloatAsState(
targetValue = if (isFocused) scaleOnFocus else 1f,
animationSpec = animationSpec,
label = "scaleOnFocusScaleAnimation"
)
return this
.onFocusChanged { isFocused = it.isFocused }
.drawWithContent {
scale(scale) {
this@drawWithContent.drawContent()
}
}
}
参考链接:https://issuetracker.google.com/issues/283654247
7、FocusRequester 移动焦点崩溃!!!
常用的焦点移动方法
Modifier.focusProperties {
up = upRequester()
left = leftRequester()
right = rightRequester()
}
但,经常会遇到移动焦点崩溃,一般日志会有以下提示:
java.lang.IllegalStateException:
FocusRequester is not initialized. Here are some possible fixes:
【1】Remember the FocusRequester: val focusRequester = remember { FocusRequester() }
【2】Did you forget to add a Modifier.focusRequester() ?
【3】Are you attempting to request focus during composition? Focus requests should be made in
response to some event. Eg Modifier.clickable { focusRequester.requestFocus() }
上面已给出三种可能引发的原因,但也没指出具体代码位置,需要结合页面逻辑进一步分析、猜测;
另外再补充两个坑
【4】如果item焦点FocusRequester()实例在item Data数据bean里创建,List列表内item之间移动焦点,当列表Data刷新时,up\left\right等方向的焦点也要同步更新。
以下为 伪代码示例
/** item数据 */
data class ItemInfo(
val id: String = "",
val requester: FocusRequester = FocusRequester()
)
// 在Item UI中remember 焦点
val focusRequester = remember { itemInfo.requester }
// 使用焦点
Modifier.focusRequester(focusRequester )
【5】itemData数据没变, 但compose列表重组,移动焦点也会崩溃;原因界面重组时未触发remember重新执行,所以要给它设置key,可以是多个;当key发生变化时,则会触发remember(key)。
修改后代码如下:
val focusRequester = remember(itemInfo.requester) {
itemInfo.requester
}