Android 開發基礎
學習使用 Kotlin 進行 Android 開發,與 JavaScript 移動開發模式和 React Native 概念進行對比
Android 開發基礎
歡迎來到 JavaScript 到 Kotlin 轉換的第六個模組!在本模組中,我們將探索使用 Kotlin 進行 Android 開發,並了解它與 JavaScript 移動開發模式(如 React Native)的對比。我們將學習 Android 項目結構、Activity 生命週期、UI 組件和現代 Android 開發實踐。
學習目標
通過本模組的學習,你將能夠:
- 理解 Android 項目結構和架構
- 對比 Android 開發與 React Native 模式
- 實現 Activity 生命週期管理
- 有效創建和管理 Fragment
- 使用 XML 和 Compose 設計響應式佈局
- 處理用戶交互和事件
- 管理 Android 資源和資產
- 應用現代 Android 開發最佳實踐
Android 項目結構
項目組織
Android 項目具有特定的結構,與 JavaScript/React Native 項目有顯著差異。讓我們探索關鍵差異。
目錄結構對比
React Native 結構:
MyApp/├── App.js # 主應用入口├── package.json # 依賴管理├── src/│ ├── screens/ # 屏幕組件│ ├── components/ # 可重用組件│ ├── navigation/ # 導航配置│ └── utils/ # 工具函數└── assets/ # 靜態資源
Android 結構:
MyApp/├── app/│ ├── src/│ │ ├── main/│ │ │ ├── java/ # Kotlin/Java 源碼│ │ │ ├── res/ # 資源文件│ │ │ │ ├── layout/ # XML 佈局文件│ │ │ │ ├── values/ # 字符串、顏色等│ │ │ │ ├── drawable/ # 圖片資源│ │ │ │ └── menu/ # 菜單文件│ │ │ └── AndroidManifest.xml│ │ └── test/ # 測試代碼│ └── build.gradle # 模組級構建配置├── gradle/ # Gradle 包裝器└── build.gradle # 項目級構建配置
Activity 生命週期管理
生命週期概念
Android Activity 有明確的生命週期,與 React 組件的生命週期概念相似但更複雜:
生命週期最佳實踐
React Native 方法:
// 使用 useEffect 管理生命週期const useLifecycle = (onMount, onUnmount) => {useEffect(() => {onMount?.();return onUnmount;}, []);};// 使用自定義 Hookconst useDataFetching = (url) => {const [data, setData] = useState(null);const [loading, setLoading] = useState(true);useEffect(() => {let isMounted = true;const fetchData = async () => {try {const response = await fetch(url);const result = await response.json();if (isMounted) {setData(result);setLoading(false);}} catch (error) {if (isMounted) {setLoading(false);}}};fetchData();return () => {isMounted = false;};}, [url]);return { data, loading };};
Android 方法:
// 使用 ViewModel 管理生命週期class DataViewModel : ViewModel() {private val _data = MutableLiveData<String>()val data: LiveData<String> = _dataprivate val _loading = MutableLiveData<Boolean>()val loading: LiveData<Boolean> = _loadinginit {fetchData()}private fun fetchData() {viewModelScope.launch {_loading.value = truetry {val result = withContext(Dispatchers.IO) {URL("https://api.example.com/data").readText()}_data.value = result} catch (error: Exception) {// 處理錯誤} finally {_loading.value = false}}}override fun onCleared() {super.onCleared()// 清理資源}}// 在 Activity 中使用class MainActivity : AppCompatActivity() {private val viewModel: DataViewModel by viewModels()override fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)setContentView(R.layout.activity_main)// 觀察數據變化viewModel.data.observe(this) { data ->// 更新 UI}viewModel.loading.observe(this) { loading ->// 顯示/隱藏加載指示器}}}
Fragment 管理
Fragment 概念
Fragment 是 Android 中可重用的 UI 組件,類似於 React 中的組件:
UI 開發與佈局
XML 佈局 vs React Native StyleSheet
React Native 方法:
// React Native 樣式const styles = StyleSheet.create({container: {flex: 1,backgroundColor: '#f5f5f5',padding: 20,},header: {fontSize: 24,fontWeight: 'bold',color: '#333',marginBottom: 20,},card: {backgroundColor: 'white',borderRadius: 8,padding: 16,marginBottom: 12,shadowColor: '#000',shadowOffset: { width: 0, height: 2 },shadowOpacity: 0.1,shadowRadius: 4,elevation: 3,},button: {backgroundColor: '#007AFF',paddingHorizontal: 20,paddingVertical: 12,borderRadius: 6,alignItems: 'center',},buttonText: {color: 'white',fontSize: 16,fontWeight: '600',},});const ProfileScreen = () => {return (<View style={styles.container}><Text style={styles.header}>用戶資料</Text><View style={styles.card}><Text style={styles.cardTitle}>基本信息</Text><Text>姓名: 張三</Text></View><TouchableOpacity style={styles.button}><Text style={styles.buttonText}>編輯資料</Text></TouchableOpacity></View>);};
Android XML 佈局:
<!-- activity_profile.xml --><?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="match_parent"android:layout_height="match_parent"android:orientation="vertical"android:background="#f5f5f5"android:padding="20dp"><TextViewandroid:id="@+id/header"android:layout_width="match_parent"android:layout_height="wrap_content"android:text="用戶資料"android:textSize="24sp"android:textStyle="bold"android:textColor="#333"android:layout_marginBottom="20dp" /><androidx.cardview.widget.CardViewandroid:layout_width="match_parent"android:layout_height="wrap_content"android:layout_marginBottom="12dp"app:cardCornerRadius="8dp"app:cardElevation="3dp"><LinearLayoutandroid:layout_width="match_parent"android:layout_height="wrap_content"android:orientation="vertical"android:padding="16dp"><TextViewandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:text="基本信息"android:textStyle="bold"android:layout_marginBottom="8dp" /><TextViewandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:text="姓名: 張三" /><TextViewandroid:layout_width="wrap_content"android:layout_height="wrap_content"</LinearLayout></androidx.cardview.widget.CardView><Buttonandroid:id="@+id/edit_button"android:layout_width="match_parent"android:layout_height="wrap_content"android:text="編輯資料"android:background="@drawable/button_background"android:textColor="#FFFFFF" /></LinearLayout>
Jetpack Compose 現代 UI
// 使用 Compose 創建現代 UI@Composablefun ProfileScreen() {Column(modifier = Modifier.fillMaxSize().background(Color(0xFFF5F5F5)).padding(20.dp)) {Text(text = "用戶資料",style = MaterialTheme.typography.headlineMedium,color = Color(0xFF333333),modifier = Modifier.padding(bottom = 20.dp))Card(modifier = Modifier.fillMaxWidth().padding(bottom = 12.dp),elevation = CardDefaults.cardElevation(defaultElevation = 4.dp),shape = RoundedCornerShape(8.dp)) {Column(modifier = Modifier.padding(16.dp)) {Text(text = "基本信息",style = MaterialTheme.typography.titleMedium,fontWeight = FontWeight.Bold,modifier = Modifier.padding(bottom = 8.dp))Text(text = "姓名: 張三",style = MaterialTheme.typography.bodyMedium)Text(style = MaterialTheme.typography.bodyMedium)}}Button(onClick = { /* 編輯資料 */ },modifier = Modifier.fillMaxWidth(),colors = ButtonDefaults.buttonColors(containerColor = Color(0xFF007AFF))) {Text(text = "編輯資料",color = Color.White,style = MaterialTheme.typography.bodyLarge,fontWeight = FontWeight.SemiBold)}}}
事件處理
觸摸事件處理
React Native 方法:
import { TouchableOpacity, Alert } from 'react-native';const EventHandlingExample = () => {const handlePress = () => {Alert.alert('提示', '按鈕被點擊了!');};const handleLongPress = () => {Alert.alert('提示', '長按事件觸發!');};return (<TouchableOpacityonPress={handlePress}onLongPress={handleLongPress}style={styles.button}><Text style={styles.buttonText}>點擊我</Text></TouchableOpacity>);};
Android 方法:
// XML 佈局中的點擊處理<Buttonandroid:id="@+id/click_button"android:layout_width="wrap_content"android:layout_height="wrap_content"android:text="點擊我"android:onClick="onButtonClick" />// Activity 中的處理方法class MainActivity : AppCompatActivity() {override fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)setContentView(R.layout.activity_main)// 使用 OnClickListenerfindViewById<Button>(R.id.click_button).setOnClickListener {Toast.makeText(this, "按鈕被點擊了!", Toast.LENGTH_SHORT).show()}// 長按事件findViewById<Button>(R.id.click_button).setOnLongClickListener {Toast.makeText(this, "長按事件觸發!", Toast.LENGTH_SHORT).show()true // 返回 true 表示已處理事件}}// XML 中定義的點擊方法fun onButtonClick(view: View) {Toast.makeText(this, "XML 點擊事件", Toast.LENGTH_SHORT).show()}}// Compose 中的事件處理@Composablefun EventHandlingExample() {var clickCount by remember { mutableStateOf(0) }Button(onClick = {clickCount++// 顯示 Toast 或其他反饋},modifier = Modifier.padding(16.dp)) {Text("點擊次數: $clickCount")}// 長按手勢Box(modifier = Modifier.fillMaxWidth().height(100.dp).background(Color.Gray).pointerInput(Unit) {detectTapGestures(onTap = { /* 點擊處理 */ },onLongPress = { /* 長按處理 */ })}) {Text(text = "長按區域",modifier = Modifier.align(Alignment.Center),color = Color.White)}}
資源管理
字符串資源
React Native 方法:
// 使用常量或配置文件const strings = {welcome: '歡迎使用應用',login: '登錄',logout: '登出',settings: '設置',};// 或使用 i18n 庫import i18n from 'i18next';const App = () => {return (<View><Text>{i18n.t('welcome')}</Text><Text>{i18n.t('login')}</Text></View>);};
Android 方法:
<!-- res/values/strings.xml --><?xml version="1.0" encoding="utf-8"?><resources><string name="app_name">我的應用</string><string name="welcome">歡迎使用應用</string><string name="login">登錄</string><string name="logout">登出</string><string name="settings">設置</string></resources><!-- res/values-zh/strings.xml (簡體中文) --><resources><string name="welcome">歡迎使用應用</string><string name="login">登錄</string></resources><!-- res/values-zh-rTW/strings.xml (繁體中文) --><resources><string name="welcome">歡迎使用應用</string><string name="login">登錄</string></resources>
// 在代碼中使用字符串資源class MainActivity : AppCompatActivity() {override fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)setContentView(R.layout.activity_main)// 獲取字符串資源val welcomeText = getString(R.string.welcome)val loginText = getString(R.string.login)findViewById<TextView>(R.id.welcome_text).text = welcomeTextfindViewById<Button>(R.id.login_button).text = loginText}}// Compose 中使用@Composablefun WelcomeScreen() {val context = LocalContext.currentColumn {Text(text = context.getString(R.string.welcome))Button(onClick = { /* 登錄 */ }) {Text(text = context.getString(R.string.login))}}}
圖片資源管理
React Native 方法:
// 直接引用圖片import profileImage from './assets/profile.png';const ProfileComponent = () => {return (<Imagesource={profileImage}style={styles.profileImage}/>);};// 或使用 require<Imagesource={require('./assets/profile.png')}style={styles.profileImage}/>
Android 方法:
<!-- 在佈局中使用圖片 --><ImageViewandroid:id="@+id/profile_image"android:layout_width="100dp"android:layout_height="100dp"android:src="@drawable/profile_image"android:contentDescription="用戶頭像" /><!-- res/drawable/profile_image.xml --><vector xmlns:android="http://schemas.android.com/apk/res/android"android:width="24dp"android:height="24dp"android:viewportWidth="24"android:viewportHeight="24"><pathandroid:fillColor="#FF000000"android:pathData="M12,12c2.21,0 4,-1.79 4,-4s-1.79,-4 -4,-4 -4,1.79 -4,4 1.79,4 4,4zM12,14c-2.67,0 -8,1.34 -8,4v2h16v-2c0,-2.66 -5.33,-4 -8,-4z"/></vector>
// 在代碼中動態設置圖片class MainActivity : AppCompatActivity() {override fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)setContentView(R.layout.activity_main)val imageView = findViewById<ImageView>(R.id.profile_image)// 設置圖片資源imageView.setImageResource(R.drawable.profile_image)// 或從網絡加載Glide.with(this).load("https://example.com/profile.jpg").placeholder(R.drawable.placeholder_image).error(R.drawable.error_image).into(imageView)}}// Compose 中使用圖片@Composablefun ProfileImage() {Image(painter = painterResource(id = R.drawable.profile_image),contentDescription = "用戶頭像",modifier = Modifier.size(100.dp).clip(CircleShape))}
國際化支持
多語言支持
React Native 方法:
// 使用 react-native-localizeimport * as RNLocalize from 'react-native-localize';const translations = {'zh-CN': {welcome: '歡迎',login: '登錄',},'zh-TW': {welcome: '歡迎',login: '登錄',},'en': {welcome: 'Welcome',login: 'Login',},};const getCurrentLanguage = () => {const locales = RNLocalize.getLocales();return locales[0].languageCode;};const t = (key) => {const language = getCurrentLanguage();return translations[language]?.[key] || translations['en'][key];};
Android 方法:
// 語言管理器object LanguageManager {fun setLocale(context: Context, languageCode: String) {val locale = Locale(languageCode)Locale.setDefault(locale)val config = context.resources.configurationconfig.setLocale(locale)context.createConfigurationContext(config)}}// 在 Activity 中使用class MainActivity : AppCompatActivity() {override fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)// 設置語言val language = getSharedPreferences("settings", MODE_PRIVATE).getString("language", "zh-CN") ?: "zh-CN"LanguageManager.setLocale(this, language)setContentView(R.layout.activity_main)}}
現代 Android 開發最佳實踐
Jetpack Compose 优势
Jetpack Compose 是 Android 的现代 UI 工具包,提供了声明式 UI 开发方式:
// Compose 最佳实践@Composablefun ModernAndroidApp() {var currentScreen by remember { mutableStateOf("home") }MaterialTheme {Scaffold(topBar = {TopAppBar(title = { Text("现代 Android 应用") },actions = {IconButton(onClick = { /* 设置 */ }) {Icon(Icons.Default.Settings, "设置")}})},bottomBar = {BottomNavigation {BottomNavigationItem(icon = { Icon(Icons.Default.Home, "首页") },label = { Text("首页") },selected = currentScreen == "home",onClick = { currentScreen = "home" })BottomNavigationItem(icon = { Icon(Icons.Default.Person, "个人") },label = { Text("个人") },selected = currentScreen == "profile",onClick = { currentScreen = "profile" })}}) { paddingValues ->when (currentScreen) {"home" -> HomeScreen(Modifier.padding(paddingValues))"profile" -> ProfileScreen(Modifier.padding(paddingValues))}}}}@Composablefun HomeScreen(modifier: Modifier = Modifier) {LazyColumn(modifier = modifier.fillMaxSize()) {item {Text(text = "欢迎使用现代 Android 开发",style = MaterialTheme.typography.headlineMedium,modifier = Modifier.padding(16.dp))}items(10) { index ->Card(modifier = Modifier.fillMaxWidth().padding(horizontal = 16.dp, vertical = 4.dp),elevation = CardDefaults.cardElevation(defaultElevation = 4.dp)) {Text(text = "项目 ${index + 1}",modifier = Modifier.padding(16.dp))}}}}
总结
在本模块中,我们探索了使用 Kotlin 进行 Android 开发的基础知识,并将其与 JavaScript/React Native 模式进行了对比。以下是关键要点:
涵盖的核心概念:
- 项目结构:Android 的有组织目录结构 vs React Native 的简化方法
- 生命周期管理:Activity 和 Fragment 生命周期 vs React 组件生命周期
- UI 开发:XML 布局和 Compose vs React Native 的 StyleSheet 方法
- 事件处理:Android 的全面触摸系统 vs React Native 的手势处理
- 资源管理:Android 的资源系统 vs React Native 的资产方法
- 现代实践:Jetpack Compose 的声明式 UI vs React Native 的基于组件的方法
Android 开发优势:
- 类型安全:Kotlin 的强类型系统防止运行时错误
- 性能:原生 Android 性能 vs JavaScript 桥接开销
- 平台集成:直接访问 Android API 和系统功能
- 工具支持:Android Studio 提供优秀的 IDE 支持
- 生态系统:丰富的 Android 特定库和框架
最佳实践:
- 使用 Jetpack Compose 进行现代 UI 开发
- 实现适当的生命周期管理 以防止内存泄漏
- 遵循 Material Design 指南 以获得一致的用户体验
- 使用 ViewModel 进行状态管理和配置更改
- 实现适当的错误处理 和用户反馈
- 优化性能 使用 RecyclerView 和高效布局
下一步:
在下一个模块中,我们将探索使用 Kotlin 进行 Web 开发,包括 Spring Boot 框架和后端服务,继续我们从 JavaScript 到 Kotlin 开发模式的旅程。