Android UI 转鸿蒙 ArkUI 开发范式对照文档
本文档面向有 Android 开发经验的工程师,帮助快速理解并迁移到 HarmonyOS ArkUI 声明式开发范式。
一、架构理念对照
| 维度 | Android (XML/Compose) | HarmonyOS (ArkUI) |
|---|
| UI 范式 | XML 命令式 / Compose 声明式 | ArkUI 声明式 |
| 编程语言 | Java / Kotlin | ArkTS (TypeScript 子集) |
| 页面容器 | Activity | UIAbility |
| 组件化 | Fragment / @Composable | @Component / @ComponentV2 |
| 状态管理 | ViewModel + LiveData / remember + State | @State/@Local + @Observed/@ObservedV2 |
| 导航 | Intent / Navigation Compose | Navigation + NavPathStack |
| 资源引用 | R.string.xxx / R.drawable.xxx | $r(‘app.string.xxx’) / $r(‘app.media.xxx’) |
| 构建 | Gradle | Hvigor |
| 包管理 | Maven / Gradle | OHP |
二、生命周期对照
2.1 页面生命周期
Android Activity HarmonyOS UIAbility
───────────────── ──────────────────
onCreate() → onCreate()
onStart() → onWindowStageCreate()
onResume() → onForeground()
onPause() → onBackground()
onStop() → (无直接对应)
onDestroy() → onDestroy()
2.2 组件生命周期
Android Fragment HarmonyOS @Component
───────────────── ──────────────────
onAttach() → (无对应)
onCreateView() → build()
onViewCreated() → (在build中完成)
onStart() / onResume() → aboutToAppear()
onPause() / onStop() → aboutToDisappear()
onDestroyView() → (无对应)
onDetach() → (无对应)
注意:@Component 没有 onResume/onPause 概念。如需感知页面可见性,在页面的 onPageShow/onPageHide 中通过状态变量通知组件。
三、布局组件映射
3.1 XML 布局 → ArkUI 布局
| Android XML | HarmonyOS ArkUI | 说明 |
|---|
LinearLayout(vertical) | Column | 垂直线性排列 |
LinearLayout(horizontal) | Row | 水平线性排列 |
RelativeLayout | RelativeContainer | 相对定位布局 |
FrameLayout | Stack | 层叠布局 |
ConstraintLayout | RelativeContainer / Flex | 复杂约束用 RelativeContainer,弹性用 Flex |
ScrollView | Scroll | 可滚动容器 |
NestedScrollView | Scroll + List | 嵌套滚动 |
ViewPager / ViewPager2 | Swiper | 轮播/页面切换 |
TabLayout + ViewPager | Tabs + TabContent | 选项卡 |
RecyclerView | List + LazyForEach / Repeat | 高性能列表 |
GridView | Grid + GridItem | 网格布局 |
FlexboxLayout | Flex | 弹性布局 |
GridLayout | GridRow + GridCol | 栅格布局(响应式) |
DrawerLayout | Panel / 自定义 Stack | 抽屉布局 |
CoordinatorLayout | Column + 手势/动画 | 协调布局(需自行实现) |
3.2 关键属性映射
通用属性
| Android XML 属性 | ArkUI 属性 | 示例 |
|---|
layout_width="match_parent" | .width('100%') 或 .width('100%') | |
layout_height="wrap_content" | .height('auto') 或不设置 | |
layout_margin="16dp" | .margin(16) | 支持 vp/px 单位 |
layout_padding="16dp" | .padding(16) | |
layout_gravity="center" | .alignSelf(ItemAlign.Center) | 父容器交叉轴对齐 |
gravity="center" | .justifyContent(FlexAlign.Center) + .alignItems(HorizontalAlign.Center) | 内容对齐 |
visibility="gone" | .visibility(Visibility.None) | |
visibility="invisible" | .visibility(Visibility.Hidden) | |
visibility="visible" | .visibility(Visibility.Visible) | |
background="@color/xxx" | .backgroundColor($r('app.color.xxx')) | |
elevation="4dp" | .shadow({ radius: 4 }) | |
alpha="0.5" | .opacity(0.5) | |
clickable="true" | .onClick(() => {}) | |
enabled="false" | .enabled(false) | |
LinearLayout → Column/Row
<LinearLayout
android:orientation="vertical"
android:gravity="center"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView android:layout_weight="1" />
<TextView android:layout_weight="2" />
</LinearLayout>
Column() {
Text('item1').layoutWeight(1)
Text('item2').layoutWeight(2)
}
.width('100%')
.height('100%')
.justifyContent(FlexAlign.Center)
.alignItems(HorizontalAlign.Center)
| XML 属性 | ArkUI 属性 |
|---|
orientation="vertical" | 使用 Column |
orientation="horizontal" | 使用 Row |
layout_weight="1" | .layoutWeight(1) |
gravity="center_vertical" | Column: .justifyContent(FlexAlign.Center) |
gravity="center_horizontal" | Column: .alignItems(HorizontalAlign.Center) |
ConstraintLayout → RelativeContainer
RelativeContainer() {
Text('Title')
.id('title')
.alignRules({ top: { anchor: '__container__', align: VerticalAlign.Top } })
Text('Subtitle')
.id('subtitle')
.alignRules({
top: { anchor: 'title', align: VerticalAlign.Bottom },
left: { anchor: 'title', align: HorizontalAlign.Start }
})
}
四、UI 组件映射
4.1 基础组件
| Android (XML/Compose) | HarmonyOS ArkUI | 说明 |
|---|
TextView / Text() | Text | 文本显示 |
EditText / TextField() | TextInput | 单行输入 |
EditText(multiline) / TextField(maxLines) | TextArea | 多行输入 |
Button / Button() | Button | 按钮 |
ImageView / Image() | Image | 图片显示 |
ProgressBar(horizon) / LinearProgressIndicator | Progress(Linear) | 线性进度条 |
ProgressBar(circle) / CircularProgressIndicator | Progress(Ring) / LoadingProgress | 圆形进度 |
CheckBox / Checkbox() | Checkbox | 复选框 |
RadioButton / RadioButton() | Radio + RadioContainer | 单选按钮 |
Switch / Switch() | Toggle(type: SwitchType) | 开关 |
SeekBar / Slider() | Slider | 滑块 |
RatingBar / RatingBar() | Rating | 评分 |
Spinner / 下拉选择 | Select | 下拉选择 |
SearchView / SearchBar() | Search | 搜索框 |
WebView / WebView() | Web | 网页视图 |
VideoView / VideoPlayer() | Video | 视频播放 |
4.2 对话框与弹窗
| Android | HarmonyOS ArkUI | 说明 |
|---|
AlertDialog.Builder | AlertDialog / promptAction.showDialog | 提示对话框 |
BottomSheetDialog | bindSheet | 半模态弹窗 |
DialogFragment | CustomDialog / @CustomDialog | 自定义弹窗 |
Toast.makeText | promptAction.showToast | 轻提示 |
PopupMenu | bindMenu | 菜单弹窗 |
DatePickerDialog | DatePickerDialog | 日期选择 |
TimePickerDialog | TimePickerDialog | 时间选择 |
Snackbar | promptAction.showToast / 自定义 | 无直接对应 |
4.3 列表组件
| Android | HarmonyOS ArkUI | 说明 |
|---|
ListView + Adapter | List + ForEach | 简单列表 |
RecyclerView + Adapter + ViewHolder | List + LazyForEach | 高性能列表 |
RecyclerView + GridLayoutManager | Grid + LazyForEach | 网格列表 |
RecyclerView + StaggeredGridLayoutManager | WaterFlow + LazyForEach | 瀑布流 |
ListAdapter | BasicDataSource / IDataSource | 数据源抽象 |
DiffUtil | IDataSource 的 notifyDataChange | 数据变更通知 |
ItemDecoration | List 的 .divider() / 自定义 ListItem 间距 | 列表分割线 |
ItemAnimator | animateItem / 属性动画 | 列表动画 |
RecyclerView → List + LazyForEach 对照
LazyColumn {
items(courseList, key = { it.id }) { course ->
CourseCard(course = course)
}
}
List() {
LazyForEach(this.dataSource, (item: CourseItem) => {
ListItem() {
CourseCard({ course: item })
}
}, (item: CourseItem) => item.id.toString())
}
.cachedCount(5)
.divider({ strokeWidth: 1, color: '#E0E0E0' })
关键差异:
- 无需 Adapter/ViewHolder,组件本身即 ViewHolder,框架自动复用
LazyForEach 第三个参数是 key 生成函数,必须返回唯一字符串cachedCount 替代 RecyclerView 的缓存策略
五、Compose → ArkUI 范式对照
5.1 核心概念映射
| Jetpack Compose | ArkUI | 说明 |
|---|
@Composable | @Component / @ComponentV2 | 组件声明 |
remember | @Local (V2) / @State (V1) | 组件内部状态 |
mutableStateOf | @Local (V2) / @State (V1) | 可变状态 |
State<T> | @Local (V2) / @State (V1) | 状态包装 |
Modifier | 链式属性方法 | 样式修饰 |
LaunchedEffect | aboutToAppear + 异步任务 | 副作用 |
DisposableEffect | aboutToAppear + aboutToDisappear | 生命周期副作用 |
SideEffect | 直接在事件回调中处理 | 同步副作用 |
rememberCoroutineScope | 直接使用 async/await | 协程 |
CompositionLocal | @Provider / @Consumer (V2) | 作用域传递 |
NavHost / NavGraph | Navigation + NavPathStack | 导航图 |
5.2 状态管理对照
| Jetpack Compose | ArkUI V2 (推荐) | ArkUI V1 |
|---|
var text by remember { mutableStateOf("") } | @Local text: string = '' | @State text: string = '' |
传参 (text: String) | @Param text: string = '' | @Prop text: string = '' |
回调 (onChange: (String) -> Unit) | @Event onChange: (s: string) => void | 回调函数属性 |
ViewModel + StateFlow | @ObservedV2 + @Trace | @Observed + @ObjectLink |
MutableLiveData | @Trace 属性 | @Watch 监听 |
5.3 Modifier → 链式属性
Text(
text = "Hello",
modifier = Modifier
.fillMaxWidth()
.padding(16.dp)
.background(Color.Blue)
.clickable { },
color = Color.White,
fontSize = 16.sp
)
Text('Hello')
.width('100%')
.padding(16)
.backgroundColor(Color.Blue)
.onClick(() => { })
.fontColor(Color.White)
.fontSize(16)
区别:Compose 用 Modifier 参数集中传递样式;ArkUI 用链式方法调用。功能等价,表达方式不同。
5.4 常见组件对照
| Compose | ArkUI |
|---|
Column { } | Column() { } |
Row { } | Row() { } |
Box { } | Stack() { } |
LazyColumn { } | List() { LazyForEach(...) } |
LazyRow { } | List({ listDirection: Axis.Horizontal }) { } |
Grid { } | Grid() { } |
Scaffold { } | 无直接对应,用 Column + 自定义 TitleBar + Tabs 组合 |
TopAppBar | Navigation 的 title 属性 / 自定义 TitleBar |
BottomNavigation | Tabs 的底部栏模式 |
FloatingActionButton | 自定义 Button + Stack 定位 |
Card { } | Column + .borderRadius() + .shadow() |
Chip / FilterChip | 自定义组件 / Toggle |
Divider() | Divider() |
Spacer(modifier = Modifier.height(8.dp)) | Blank().height(8) |
Surface { } | Column().borderRadius().backgroundColor() |
六、导航路由对照
6.1 页面跳转
val intent = Intent(this, DetailActivity::class.java)
intent.putExtra("courseId", courseId)
startActivity(intent)
import { router } from '@kit.ArkUI'
router.pushUrl({
url: 'pages/DetailPage',
params: { courseId: this.courseId }
})
this.navPathStack.pushPath({ name: 'DetailPage', param: { courseId: this.courseId } })
6.2 导航对照表
| Android | HarmonyOS ArkUI |
|---|
startActivity() | router.pushUrl() / navPathStack.pushPath() |
finish() | router.back() / navPathStack.pop() |
startActivityForResult() | navPathStack.pushPath() + 回调 |
Intent.putExtra() | params: { key: value } |
getIntent().getExtra() | router.getParams()['key'] |
Navigation Compose NavHost | Navigation { NavPathStack } |
NavGraph | navDestination 路由表 |
BottomNavigationView | Tabs 组件 |
七、资源管理对照
| Android | HarmonyOS ArkUI | 说明 |
|---|
R.string.app_name | $r('app.string.app_name') | 字符串 |
R.drawable.icon | $r('app.media.icon') | 图片 |
R.color.primary | $r('app.color.primary') | 颜色 |
R.dimen.spacing | $r('app.float.spacing') | 尺寸 |
R.raw.video | $rawfile('video.mp4') | 原始文件 |
getString(R.string.xxx) | getContext().resourceManager.getStringSync($r('app.string.xxx')) | 代码中获取 |
dp 单位 | vp 单位 (1:1) | 密度无关像素 |
sp 单位 | fp 单位 | 字体缩放像素 |
资源目录结构:
Android HarmonyOS
───────── ─────────
res/ resources/
values/ base/
strings.xml element/
colors.xml string.json
dimens.xml color.json
drawable/ float.json
mipmap-hdpi/ media/
raw/ icon.png
rawfile/
video.mp4
八、数据存储对照
| Android | HarmonyOS | 说明 |
|---|
SharedPreferences | @ohos.data.preferences | 轻量 KV 存储 |
Room / SQLite | @ohos.data.relationalStore | 关系型数据库 |
DataStore | @ohos.data.preferences | 偏好存储 |
File (内部存储) | @ohos.file.fs | 文件操作 |
ContentProvider | DataShareExtension | 数据共享 |
GreenDAO | @ohos.data.relationalStore | ORM 需自行封装 |
SharedPreferences 迁移示例
val sp = getSharedPreferences("config", MODE_PRIVATE)
sp.edit().putString("token", token).apply()
val token = sp.getString("token", "")
import { preferences } from '@kit.ArkData'
let prefs = await preferences.getPreferences(getContext(this), 'config')
await prefs.put('token', token)
await prefs.flush()
let token = await prefs.get('token', '')
注意:鸿蒙 preferences 全部是异步操作,必须用 await;flush() 必须显式调用才会持久化。
九、事件交互对照
| Android (XML/Compose) | HarmonyOS ArkUI |
|---|
setOnClickListener / .clickable { } | .onClick(() => {}) |
setOnLongClickListener / .longClickable { } | .onLongPress(() => {}) / 手势绑定 |
setOnTouchListener | .onTouch((e) => {}) |
setOnKeyListener | .onKeyEvent((e) => {}) |
addTextChangedListener | .onChange((value) => {}) |
setOnFocusChangeListener | .onFocus(() => {}) / .onBlur(() => {}) |
SwipeRefreshLayout | Refresh 组件 |
onTouch 手势识别 | GestureGroup / PanGesture / TapGesture |
onScrollListener | .onScrollFrameBegin() / .onScroll() |
ItemTouchHelper (拖拽/滑动) | List 的 .onDragStart() / .onSwipe() |
手势体系对照
Modifier.pointerInput(Unit) {
detectTapGestures(onLongPress = { })
detectDragGestures { change, dragAmount -> }
}
.gesture(
GestureGroup(GestureMode.Exclusive,
LongPressGesture().onAction(() => { }),
PanGesture().onActionUpdate((e: GestureEvent) => { })
)
)
十、动画对照
| Android (XML/Compose) | HarmonyOS ArkUI |
|---|
ObjectAnimator | animateTo() 显式动画 |
ValueAnimator | animateTo() + 闭包 |
Transition / AnimatedVisibility | transition() 转场动画 |
animateFloatAsState | animateTo() / animationDuration |
Animatable | @AnimatableExtend 自定义动画属性 |
Lottie | @ohos.multimedia.media / 自定义 Canvas 动画 |
SharedElement | geometryTransition() 共享元素转场 |
MotionLayout | 无直接对应,组合属性动画实现 |
animateTo({ duration: 300, curve: Curve.EaseInOut }, () => {
this.width = 200
this.opacity = 1.0
})
Text('Hello')
.width(this.width)
.animation({ duration: 300, curve: Curve.EaseInOut })
十一、MVVM 架构对照
11.1 架构映射
| Android MVVM | HarmonyOS MVVM |
|---|
ViewModel | @ObservedV2 class (ViewModel) |
MutableLiveData<T> | @Trace 属性 |
LiveData.observe() | @Watch / 自动 UI 刷新 |
Repository | Service / Repository (同概念) |
DataBinding | 自动状态驱动 UI (无需绑定) |
11.2 目录结构
Android HarmonyOS
───────── ─────────
app/ entry/src/main/ets/
model/ model/
User.kt User.ets
viewmodel/ viewmodel/
UserViewModel.kt UserViewModel.ets
view/ view/
Activity/ pages/
Fragment/ components/
Adapter/ (无 Adapter)
repository/ service/
UserRepository.kt UserRepository.ets
11.3 代码对照
class UserViewModel : ViewModel() {
private val _userName = MutableLiveData("")
val userName: LiveData<String> = _userName
fun loadUser() {
viewModelScope.launch {
_userName.value = repository.getUserName()
}
}
}
@ObservedV2
class UserViewModel {
@Trace userName: string = ''
async loadUser() {
this.userName = await repository.getUserName()
}
}
@Entry
@ComponentV2
struct UserPage {
@Local viewModel: UserViewModel = new UserViewModel()
aboutToAppear() {
this.viewModel.loadUser()
}
build() {
Column() {
Text(this.viewModel.userName)
}
}
}
十二、常见踩坑与注意事项
12.1 ArkTS 语法限制
| 限制项 | 说明 | 迁移策略 |
|---|
禁止 any 类型 | ArkTS 不允许 any/unknown | 定义具体接口/类 |
| 禁止结构化类型 | 不支持鸭子类型,只支持名义类型 | 使用 extends / implements |
禁止 as 类型断言 | 不允许强制类型转换 | 使用类型守卫或正确声明类型 |
| 禁止动态属性访问 | obj[key] 不允许 | 使用 Map 或 Record |
| 对象字面量需类型上下文 | { name: 'test' } 必须有类型 | 指定变量类型或函数参数类型 |
禁止 for...in | 不允许 for…in 遍历 | 使用 Object.keys() + for...of |
12.2 状态管理常见问题
| 问题 | 原因 | 解决 |
|---|
| 修改对象属性 UI 不刷新 | @State 对对象是浅比较 | 使用 @ObservedV2 + @Trace,或重新赋值整个对象 |
| 数组元素修改不刷新 | 直接 arr[i] = newVal 不触发 | 使用 arr.splice(i, 1, newVal) 或 arr = [...arr] |
| 子组件状态丢失 | 页面重新渲染时子组件被重建 | 使用 @Reusable 装饰器标记可复用组件 |
| LazyForEach 更新异常 | key 生成函数返回重复值 | 确保第三个参数返回唯一稳定的字符串 |
12.3 性能优化对照
| Android 优化 | HarmonyOS 优化 |
|---|
| RecyclerView.setHasFixedSize(true) | List 设置固定宽高 |
| RecyclerView.setRecycledViewPool() | cachedCount 设置预加载 |
| ViewHolder 复用 | @Reusable 组件复用 |
| Glide 图片缓存 | Image 组件内置三级缓存 |
| 避免嵌套层级过深 | 同理,扁平化布局 |
| DiffUtil 精确更新 | LazyForEach + IDataSource 的 notifyDataChange |
setOnClickListener(null) | .hitTestBehavior(HitTestMode.None) |
| ViewStub 延迟加载 | if 条件渲染 |
十三、快速迁移检查清单
页面迁移
状态迁移
导航迁移
资源迁移
数据迁移
所有评论(0)