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)