ITmob-Ly
发布于 2022-12-11 / 365 阅读
0

Jetpack Compose 中获取应用的图标/自适应图标的两种方法

Android Adaptive Icon

简介:

Android 8.0(API 级别 26)引入了自适应启动器图标,它可以在不同设备型号上显示为不同的形状。例如,在一台原始设备制造商 (OEM) 设备上,自适应启动器图标可显示为圆形,而在其他设备上则可显示为方圆形。

异常:

使用 Jetpack Compose 时,我们有时需要显示自己应用的图标或其他应用的图标。

如果我们的应用支持自适应图标,直接使用 painterResource 加载图标时就会抛出 IllegalArgumentException 异常:

IllegalArgumentException: Only VectorDrawables and rasterized asset types are supported ex. PNG, JPG

代码:

Image(painter = painterResource(R.mipmap.ic_launcher), contentDescription = null)

异常:

AndroidRuntime: FATAL EXCEPTION: main
Process: cn.itmob.demo, PID: 5021
java.lang.IllegalArgumentException: Only VectorDrawables and rasterized asset types are supported ex. PNG, JPG
    at androidx.compose.ui.res.PainterResources_androidKt.loadVectorResource(PainterResources.android.kt:93)
    at androidx.compose.ui.res.PainterResources_androidKt.painterResource(PainterResources.android.kt:65)
    at cn.itmob.demo.ComposableSingletons$MainActivityKt$lambda-1$1.invoke(MainActivity.kt:39)
    at cn.itmob.demo.ComposableSingletons$MainActivityKt$lambda-1$1.invoke(MainActivity.kt:31)
    at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:107)
    at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:34)
    at androidx.compose.material3.SurfaceKt$Surface$6.invoke(Surface.kt:261)
    at androidx.compose.material3.SurfaceKt$Surface$6.invoke(Surface.kt:252)
    at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:107)
    at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:34)
    at androidx.compose.runtime.CompositionLocalKt.CompositionLocalProvider(CompositionLocal.kt:228)
    at androidx.compose.material3.SurfaceKt.Surface-ZaiARg4(Surface.kt:249)
    at androidx.compose.material3.SurfaceKt.Surface-T9BRK9s(Surface.kt:109)
    at cn.itmob.demo.ComposableSingletons$MainActivityKt$lambda-2$1.invoke(MainActivity.kt:26)
    at cn.itmob.demo.ComposableSingletons$MainActivityKt$lambda-2$1.invoke(MainActivity.kt:24)
    at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:107)
    at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:34)
    at androidx.compose.runtime.CompositionLocalKt.CompositionLocalProvider(CompositionLocal.kt:228)
    at androidx.compose.material3.TextKt.ProvideTextStyle(Text.kt:261)
    at androidx.compose.material3.MaterialThemeKt$MaterialTheme$1.invoke(MaterialTheme.kt:71)
    at androidx.compose.material3.MaterialThemeKt$MaterialTheme$1.invoke(MaterialTheme.kt:70)
    at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:107)
    at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:34)
    at androidx.compose.runtime.CompositionLocalKt.CompositionLocalProvider(CompositionLocal.kt:228)
    at androidx.compose.material3.MaterialThemeKt.MaterialTheme(MaterialTheme.kt:66)
    at cn.itmob.demo.ui.theme.ThemeKt.DemoTheme(Theme.kt:63)
    at cn.itmob.demo.ComposableSingletons$MainActivityKt$lambda-3$1.invoke(MainActivity.kt:24)
    at cn.itmob.demo.ComposableSingletons$MainActivityKt$lambda-3$1.invoke(MainActivity.kt:23)
    at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:107)
    at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:34)
    at androidx.compose.ui.platform.ComposeView.Content(ComposeView.android.kt:410)
    at androidx.compose.ui.platform.AbstractComposeView$ensureCompositionCreated$1.invoke(ComposeView.android.kt:252)
    at androidx.compose.ui.platform.AbstractComposeView$ensureCompositionCreated$1.invoke(ComposeView.android.kt:251)
    at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:107)
    at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:34)
    at androidx.compose.runtime.CompositionLocalKt.CompositionLocalProvider(CompositionLocal.kt:228)
    at androidx.compose.ui.platform.CompositionLocalsKt.ProvideCommonCompositionLocals(CompositionLocals.kt:166)
    at androidx.compose.ui.platform.AndroidCompositionLocals_androidKt$ProvideAndroidCompositionLocals$3.invoke(AndroidCompositionLocals.android.kt:123)
    at androidx.compose.ui.platform.AndroidCompositionLocals_androidKt$ProvideAndroidCompositionLocals$3.invoke(AndroidCompositionLocals.android.kt:122)

解决这个异常就需要使用其他方式加载应用图标,使用 PackageManager 加载应用图标,或使用 ResourcesCompat 直接加载自适应图标资源。

方法一:

使用 PackageManager 获取应用图标:

class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            ComposeTextDemoTheme {
                Surface(
                    modifier = Modifier
                        .fillMaxSize()
                        .padding(16.dp),
                    color = MaterialTheme.colorScheme.background
                ) {
                    Column(verticalArrangement = Arrangement.Center) {
                        getAppIconBitmap(packageName)?.let {
                            Image(bitmap = it.asImageBitmap(), contentDescription = null)
                        }
                    }
                }
            }
        }
    }

    private fun getAppIconBitmap(packageName: String): Bitmap? {
        return try {
            packageManager.let { packageManager ->
                packageManager.getLaunchIntentForPackage(packageName)?.let {
                    packageManager.getActivityIcon(it)
                } ?: packageManager.getApplicationIcon(packageName)
            }
        } catch (e: PackageManager.NameNotFoundException) {
            getDrawable(android.R.drawable.sym_def_app_icon)
        }.let {
            it?.toBitmap()
        }
    }
}

方法二:

使用 ResourceCompat 直接获取应用资源

class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            ComposeTextDemoTheme {
                Surface(
                    modifier = Modifier
                        .fillMaxSize()
                        .padding(16.dp),
                    color = MaterialTheme.colorScheme.background
                ) {
                    Column(verticalArrangement = Arrangement.Center) {
                        getAppIconBitmap()?.let {
                            Image(bitmap = it.asImageBitmap(), contentDescription = null)
                        }
                    }
                }
            }
        }
    }

    private fun getAppIconBitmap(): Bitmap? {
        return ResourcesCompat.getDrawable(
            resources,
            R.mipmap.ic_launcher,
            theme
        )?.let { drawable ->
            Bitmap.createBitmap(
                drawable.intrinsicWidth,
                drawable.intrinsicHeight,
                Bitmap.Config.ARGB_8888
            ).apply {
                val canvas = Canvas(this)
                drawable.setBounds(0, 0, canvas.width, canvas.height)
                drawable.draw(canvas)
            }
        }
    }
}