AndroidKMP之瀑布流实现

AndroidKMP之瀑布流实现

1.前言:

之前写了入门的kmpdemo,最近用compose写了一个瀑布流,然后使用KMP在Android、桌面和web端都运行了一下,发现非常强大,iOS由于没有设备就暂时不贴效果图了.

2.项目配置:

AGP9.3.2和Gradle9.7.1

ini 复制代码
#Sun Sep 06 14:14:56 CST 2026
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionSha256Sum=553c78f50dafcd54d65b9a444649057857469edf836431389695608536d6b746
distributionUrl=https://mirrors.cloud.tencent.com/gradle/gradle-9.7.1-bin.zip
validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
ini 复制代码
[versions]
agp = "9.3.2"
android-compileSdk = "37"
android-minSdk = "24"
android-targetSdk = "37"
androidx-activity = "1.13.0"
androidx-appcompat = "1.8.0"
androidx-core = "1.19.0"
androidx-espresso = "3.7.0"
androidx-lifecycle = "2.11.0"
androidx-testExt = "1.3.0"
composeMultiplatform = "1.12.0"
junit = "4.13.2"
kotlin = "2.4.10"
kotlin-wrappers = "2026.8.4"
kotlinx-coroutines = "1.11.0"
material3 = "1.11.0-alpha07"
​
[libraries]
kotlin-test = { module = "org.jetbrains.kotlin:kotlin-test", version.ref = "kotlin" }
kotlin-testJunit = { module = "org.jetbrains.kotlin:kotlin-test-junit", version.ref = "kotlin" }
junit = { module = "junit:junit", version.ref = "junit" }
androidx-core-ktx = { module = "androidx.core:core-ktx", version.ref = "androidx-core" }
androidx-testExt-junit = { module = "androidx.test.ext:junit", version.ref = "androidx-testExt" }
androidx-espresso-core = { module = "androidx.test.espresso:espresso-core", version.ref = "androidx-espresso" }
androidx-appcompat = { module = "androidx.appcompat:appcompat", version.ref = "androidx-appcompat" }
androidx-activity-compose = { module = "androidx.activity:activity-compose", version.ref = "androidx-activity" }
compose-uiTooling = { module = "org.jetbrains.compose.ui:ui-tooling", version.ref = "composeMultiplatform" }
androidx-lifecycle-viewmodelCompose = { module = "org.jetbrains.androidx.lifecycle:lifecycle-viewmodel-compose", version.ref = "androidx-lifecycle" }
androidx-lifecycle-runtimeCompose = { module = "org.jetbrains.androidx.lifecycle:lifecycle-runtime-compose", version.ref = "androidx-lifecycle" }
compose-runtime = { module = "org.jetbrains.compose.runtime:runtime", version.ref = "composeMultiplatform" }
compose-foundation = { module = "org.jetbrains.compose.foundation:foundation", version.ref = "composeMultiplatform" }
compose-material3 = { module = "org.jetbrains.compose.material3:material3", version.ref = "material3" }
compose-ui = { module = "org.jetbrains.compose.ui:ui", version.ref = "composeMultiplatform" }
compose-components-resources = { module = "org.jetbrains.compose.components:components-resources", version.ref = "composeMultiplatform" }
compose-uiToolingPreview = { module = "org.jetbrains.compose.ui:ui-tooling-preview", version.ref = "composeMultiplatform" }
kotlinx-coroutinesSwing = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-swing", version.ref = "kotlinx-coroutines" }
wrappers-browser = { module = "org.jetbrains.kotlin-wrappers:kotlin-browser", version.ref = "kotlin-wrappers" }
​
[plugins]
androidApplication = { id = "com.android.application", version.ref = "agp" }
androidMultiplatformLibrary = { id = "com.android.kotlin.multiplatform.library", version.ref = "agp" }
composeMultiplatform = { id = "org.jetbrains.compose", version.ref = "composeMultiplatform" }
composeCompiler = { id = "org.jetbrains.kotlin.plugin.compose", version.ref = "kotlin" }
kotlinJvm = { id = "org.jetbrains.kotlin.jvm", version.ref = "kotlin" }
kotlinMultiplatform = { id = "org.jetbrains.kotlin.multiplatform", version.ref = "kotlin" }

3.项目模块总览:

模块 作用 运行平台 入口文件
shared 共享核心模块,存放公共 Compose UI、业务逻辑、数据模型;所有平台复用 Android / Desktop(JVM) / iOS / Web(Wasm‑JS) src/commonMain/kotlin/.../LazyVerticalStaggeredGridDemo.kt
androidApp Android 平台应用壳,仅 Android 宿主,不写业务 UI Android src/main/kotlin/com/staggeredgrid/kmpdemo/MainActivity.kt
desktopApp 桌面 (JVM) 应用壳,桌面启动入口 Desktop(JVM) src/main/kotlin/com/staggeredgrid/kmpdemo/main.kt
webApp Web (Wasm‑JS) 应用壳,浏览器运行 Web(Wasm‑JS) src/webMain/kotlin/com/staggeredgrid/kmpdemo/main.kt
iosApp iOS 平台壳(截图可见存在,未展示代码) iOS iosMain 入口

4.shared 共享模块(核心)

shared 是 KMP 核心模块,所有平台共用代码,不同平台源码放在不同 sourceSet:

scss 复制代码
shared/src
├─ commonMain        【通用】所有平台共用,Compose UI、业务逻辑、数据模型
├─ commonTest        通用单元测试
├─ androidMain        Android平台专属代码(Android SDK API)
├─ androidHostTest
├─ iosMain           iOS平台专属代码
├─ iosTest
├─ jvmMain           Desktop(JVM)平台专属
├─ jvmTest
├─ jsMain            JS/旧Web
├─ wasmJsMain        Wasm‑JS(Web浏览器)
└─ webTest

关键:LazyVerticalStaggeredGridDemo() 这个 Composable 组件写在 commonMain,Android、Desktop、Web 全部直接调用,不用重复写 UI。


5. 各平台宿主模块

5.1 androidApp(Android 宿主)

  • 传统 Android App 模块,依赖shared模块
  • 结构:
bash 复制代码
androidApp/src/main
├─ kotlin/com/staggeredgrid/kmpdemo
│  └─ MainActivity.kt  // Android Activity,调用shared的Compose组件
└─ res
└─ AndroidManifest.xml
  • 逻辑:Activity 中 setContent,调用LazyVerticalStaggeredGridDemo(),UI 来自 shared 模块。

5.2 desktopApp(JVM 桌面宿主)

  • 纯 JVM Compose Desktop 应用
  • 入口:desktopApp/src/main/kotlin/com/staggeredgrid/kmpdemo/main.kt
kotlin 复制代码
fun main() = application {
    Window(
        onCloseRequest = ::exitApplication,
        title = "KMP StaggeredGrid Demo",
    ) {
        LazyVerticalStaggeredGridDemo() // 调用shared共享UI
    }
}

5.3 webApp(Web Wasm‑JS 宿主)

  • Compose for Web,编译为 Wasm‑JS,浏览器运行
  • 入口:webApp/src/webMain/kotlin/com/staggeredgrid/kmpdemo/main.kt
kotlin 复制代码
@OptIn(ExperimentalComposeUiApi::class)
fun main() {
    ComposeViewport {
        LazyVerticalStaggeredGridDemo() // 调用shared共享UI
    }
}

5.4 iosApp(iOS 宿主)

  • 截图可见目录存在,iOS 平台壳,入口在iosMain,同样调用 shared 的 Compose 组件。

6.项目完整目录:

css 复制代码
AndroidKMPDemo/
├─ .gradle
├─ .idea
├─ .kotlin
├─ androidApp
│  ├─ build
│  └─ src/main
│     ├─ kotlin/com/staggeredgrid/kmpdemo/MainActivity.kt
│     ├─ res
│     └─ AndroidManifest.xml
├─ app (旧Android模块,可废弃)
├─ build
├─ desktopApp
│  └─ src/main/kotlin/com/staggeredgrid/kmpdemo/main.kt
├─ gradle
├─ iosApp
├─ kotlin‑js‑store
│  ├─ wasm
│  └─ yarn.lock
├─ shared
│  └─ src
│     ├─ commonMain
│     ├─ commonTest
│     ├─ androidMain
│     ├─ androidHostTest
│     ├─ iosMain
│     ├─ iosTest
│     ├─ jvmMain
│     ├─ jvmTest
│     ├─ jsMain
│     ├─ wasmJsMain
│     └─ webTest
├─ webApp
│  └─ src/webMain/kotlin/com/staggeredgrid/kmpdemo/main.kt
└─ settings.gradle.kts

7.项目架构(KMP):

  • 业务 UI、逻辑、数据模型全部放在 shared/commonMain,实现一份代码多端运行;

  • 各个平台(androidApp /desktopApp/webApp /iosApp)只做

    宿主壳:

    • 负责平台生命周期、窗口 / 视图初始化;
    • 仅仅调用 shared 模块的 Composable 根组件;
  • 平台差异化代码:如果需要调用平台特有 API,写在对应 sourceSet:androidMain / iosMain / jvmMain / wasmJsMain

  • 测试:commonTest写跨平台单元测试;各平台可以写各自平台测试。

数据流示意图:

scss 复制代码
[shared/commonMain] 业务Compose UI(LazyVerticalStaggeredGridDemo)
        ↓
┌──────────┬──────────┬──────────┬──────────┐
│androidApp│desktopApp│webApp    │iosApp    │
│(Android) │(JVM桌面) │(Wasm‑JS) │(iOS)     │
└──────────┴──────────┴──────────┴──────────┘

8.根目录settings.gradle.kts配置

scss 复制代码
pluginManagement {
    repositories {
        google {
            mavenContent {
                includeGroupAndSubgroups("androidx")
                includeGroupAndSubgroups("com.android")
                includeGroupAndSubgroups("com.google")
            }
        }
        mavenCentral()
        gradlePluginPortal()
        maven ("https://repo.huaweicloud.com/repository/maven/")
        maven ("https://jitpack.io")
    }
}
dependencyResolutionManagement {
    repositories {
        google{
            mavenContent {
                includeGroupAndSubgroups("androidx")
                includeGroupAndSubgroups("com.android")
                includeGroupAndSubgroups("com.google")
            }
        }
        mavenCentral()
        maven ("https://repo.huaweicloud.com/repository/maven/")
        maven ("https://jitpack.io")
    }
}
​
rootProject.name = "AndroidKMPDemo"
enableFeaturePreview("TYPESAFE_PROJECT_ACCESSORS")
include(":androidApp")
include(":desktopApp")
include(":shared")
include(":webApp")

9.项目测试代码:

9.1 主界面:

分为横向瀑布流和竖向瀑布流

scss 复制代码
package com.staggeredgrid.kmpdemo
​
import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.background
import androidx.compose.foundation.gestures.ScrollableDefaults
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.lazy.staggeredgrid.*
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
import kotlin.random.Random
​
data class ListItem(
    val height: Dp,
    val color: Color
)
​
fun generateItems(count: Int = 100): List<ListItem> {
    return (1..count).map {
        ListItem(
            height = Random.nextInt(100, 300).dp,
            color = Color(Random.nextLong(0XFFFFFF)).copy(alpha = 1f)
        )
    }
}
​
@OptIn(ExperimentalFoundationApi::class)
@Composable
fun LazyVerticalStaggeredGridDemo() {
    val items = generateItems()
    LazyVerticalStaggeredGrid(
        columns = StaggeredGridCells.Adaptive(110.dp),
        modifier = Modifier.fillMaxSize(),
        state = rememberLazyStaggeredGridState(),
        contentPadding = PaddingValues(16.dp),
        verticalItemSpacing = 16.dp,
        horizontalArrangement = Arrangement.spacedBy(16.dp),
        flingBehavior = ScrollableDefaults.flingBehavior(),
        userScrollEnabled = true
    ) {
        items(items) { item ->
            VerticalRandomColorBox(item)
        }
    }
}
​
@OptIn(ExperimentalFoundationApi::class)
@Composable
fun LazyHorizontalStaggeredGridDemo() {
    val items = generateItems()
    LazyHorizontalStaggeredGrid(
        rows = StaggeredGridCells.Adaptive(110.dp),
        modifier = Modifier.fillMaxSize(),
        state = rememberLazyStaggeredGridState(),
        contentPadding = PaddingValues(16.dp),
        verticalArrangement = Arrangement.spacedBy(16.dp),
        horizontalItemSpacing = 16.dp,
        flingBehavior = ScrollableDefaults.flingBehavior(),
        userScrollEnabled = true
    ) {
        items(items) { item ->
            HorizontalRandomColorBox(item)
        }
    }
}
​
@Composable
fun VerticalRandomColorBox(item: ListItem) {
    Box(
        modifier = Modifier
            .fillMaxWidth()
            .height(item.height)
            .clip(RoundedCornerShape(10.dp))
            .background(item.color)
    )
}
​
@Composable
fun HorizontalRandomColorBox(item: ListItem) {
    Box(
        modifier = Modifier
            .width(200.dp)
            .fillMaxHeight()
            .clip(RoundedCornerShape(10.dp))
            .background(item.color)
    )
}

9.2 调用代码:

kotlin 复制代码
package com.staggeredgrid.kmpdemo
​
import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.runtime.Composable
import androidx.compose.ui.tooling.preview.Preview
​
class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
            setContent(){
                LazyVerticalStaggeredGridDemo()
            }
    }
}
​
@Preview
@Composable
fun AppAndroidPreview() {
    LazyVerticalStaggeredGridDemo()
}

10.实现效果:

ios由于没有环境暂时不运行了有设备的同学可以试试,后面有设备了在运行一下。

Android端:

桌面:

web:

11.项目源码地址:

gitee.com/jackning_ad...

相关推荐
RainyJiang2 小时前
AI时代下,Android的边界正在消失
android·openai·ai编程
Android-Flutter2 小时前
android 内存抖动详解
android
方白羽4 小时前
为什么 Android 非要用 Intent 传值?
android·ios·harmonyos
方白羽6 小时前
Android17 重写 MessageQueue,解决 Handler 隐性卡顿
android·app
蜡台7 小时前
Kotlin 零基础完整版实战教程|从语法入门到Android工程实战
android·开发语言·kotlin
律宏阔7 小时前
Android 车载 USB 开发笔记:USB Host、USB 串口、USB-CAN、HID 与系统 API
android
律宏阔8 小时前
Android 车载串口开发笔记:UART、RS232、RS485、串口配置与数据通信
android
YF02118 小时前
遥控器APP端自动重连方案
android
执明wa9 小时前
Android Studio 打包 APK
android·ide·android studio