Kotlin: شرح أساسيات أندرويد بكوتلن

آخر تحديث: 2026-08-26

تطوير أندرويد اعتمد كوتلن بالكامل — Jetpack Compose يبني واجهات تصريحية بـ Kotlin DSL، وViewModel + StateFlow يدير الحالة التفاعلية، وRoom + الكوروتينات تبسّط طبقة البيانات. Charlie يبني لوحة طلبات أندرويد.

1. ما ستتعلمه


2. قصة مطور حقيقي

(1) المشكلة: جحيم مزامنة الحالة في الواجهة الحتمية

استخدمت Alice وضع XML + RecyclerView لبناء قائمة الطلبات. كل تغيير حالة يتطلب تحديث واجهة يدوي: عند تغيير البيانات، استدعِ notifyDataSetChanged()؛ عند التحميل، أظهر/أخفِ شريط التقدم — 10 حالات تحتاج 20+ سطر كود مزامنة واجهة.

(2) حل واجهة Compose التصريحية

KOTLIN
// XML + RecyclerView: حتمية، عرضة للأخطاء
// recyclerView.adapter.notifyDataSetChanged()

// Compose: تصريحية، تلقائية
@Composable
fun OrderList(orders: List<Order>) {
    LazyColumn {
        items(orders) { order ->
            OrderCard(order)  // واجهة المستخدم تعكس تغييرات البيانات تلقائيًا
        }
    }
}

Compose تصريحية — واجهة المستخدم هي دالة الحالة. عند تغيير البيانات، تتحدث الواجهة تلقائيًا بدون مزامنة يدوية.


3. دورة حياة Activity

(1) استدعاءات دورة الحياة

KOTLIN
class OrderActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            OrderScreen()
        }
    }

    override fun onResume() {
        super.onResume()
        // تحديث البيانات عندما تعود Activity إلى المقدمة
    }

    override fun onPause() {
        super.onPause()
        // حفظ بيانات المسودة
    }
}

(2) مخطط حالات دورة الحياة

100%
stateDiagram-v2
    [*] --> Created: onCreate()
    Created --> Started: onStart()
    Started --> Resumed: onResume()
    Resumed --> Started: onPause()
    Started --> Created: onStop()
    Created --> [*]: onDestroy()
    Started --> Resumed: onResume()

(3) مقارنة استدعاءات دورة الحياة

الاستدعاء المحفز العملية النموذجية
onCreate(bundle) إنشاء Activity تهيئة واجهة المستخدم، ViewModel
onStart() مرئي لكن غير تفاعلي تسجيل المستمعين
onResume() يكتسب التركيز بدء الرسوم المتحركة، تحديث البيانات
onPause() يفقد التركيز إيقاف الرسوم المتحركة، حفظ المسودة
onStop() لم يعد مرئيًا تحرير الموارد
onDestroy() جارٍ التدمير تنظيف الموارد

4. Jetpack Compose

(1) الدوال القابلة للتأليف

KOTLIN
@Composable
fun OrderCard(order: Order) {
    Card(
        modifier = Modifier.fillMaxWidth().padding(8.dp),
        elevation = CardDefaults.cardElevation(defaultElevation = 2.dp)
    ) {
        Row(
            modifier = Modifier.padding(16.dp),
            horizontalArrangement = Arrangement.SpaceBetween
        ) {
            Column {
                Text(text = order.id, style = MaterialTheme.typography.titleMedium)
                Text(text = order.customer, style = MaterialTheme.typography.bodyMedium)
            }
            Text(
                text = "\$${order.total} USD",
                style = MaterialTheme.typography.titleLarge
            )
        }
    }
}

(2) القوائم والحالة

KOTLIN
@Composable
fun OrderListScreen(viewModel: OrderViewModel = viewModel()) {
    val orders by viewModel.orders.collectAsState()
    val isLoading by viewModel.isLoading.collectAsState()

    if (isLoading) {
        CircularProgressIndicator()
    } else {
        LazyColumn {
            items(orders) { order ->
                OrderCard(order)
            }
        }
    }
}

(3) خط أنابيب عرض Compose

100%
flowchart TD
    A[تغيير الحالة] --> B[التأليف<br/>تشغيل دوال @Composable]
    B --> C[التخطيط<br/>القياس والتموضع]
    C --> D[الرسم<br/>العرض على اللوحة]
    D --> E[تحديث واجهة المستخدم]

(4) مقارنة XML مقابل Compose

البُعد XML + View Jetpack Compose
النموذج حتمي تصريحي
اللغة XML + كوتلن كوتلن بحت
مزامنة الحالة يدوية تلقائية
حجم الكود كبير (XML + Adapter + ViewHolder) صغير (@Composable)
المعاينة يتطلب التشغيل معاينة فورية في IDE
منحنى التعلم متوسط متوسط (نموذج جديد)

5. ViewModel + StateFlow

(1) ViewModel

KOTLIN
class OrderViewModel : ViewModel() {
    private val _orders = MutableStateFlow<List<Order>>(emptyList())
    val orders: StateFlow<List<Order>> = _orders.asStateFlow()

    private val _isLoading = MutableStateFlow(false)
    val isLoading: StateFlow<Boolean> = _isLoading.asStateFlow()

    fun loadOrders() {
        viewModelScope.launch {
            _isLoading.value = true
            try {
                _orders.value = orderRepository.findAll()
            } finally {
                _isLoading.value = false
            }
        }
    }
}

(2) StateFlow مقابل LiveData

البُعد LiveData StateFlow
القيمة الأولية غير مطلوبة مطلوبة
دعم الكوروتينات يحتاج محولات أصلي
الخيط الخيط الرئيسي أي خيط
دورة الحياة واعي يحتاج repeatOnLifecycle
التوصية الكود القديم المشاريع الجديدة

6. قاعدة بيانات Room

(1) Entity + Dao

KOTLIN
@Entity(tableName = "orders")
data class OrderEntity(
    @PrimaryKey val id: String,
    val total: Double,
    val status: String,
    val customer: String,
    val createdAt: Long = System.currentTimeMillis()
)

@Dao
interface OrderDao {
    @Query("SELECT * FROM orders ORDER BY createdAt DESC")
    fun getAllOrders(): Flow<List<OrderEntity>>

    @Query("SELECT * FROM orders WHERE id = :id")
    suspend fun getById(id: String): OrderEntity?

    @Insert(onConflict = OnConflictStrategy.REPLACE)
    suspend fun insert(order: OrderEntity)

    @Delete
    suspend fun delete(order: OrderEntity)
}

(2) مكونات Room

المكون التوضيح المسؤولية
Entity @Entity تعيين جدول قاعدة البيانات
Dao @Dao عمليات الوصول للبيانات
Database @Database نقطة دخول قاعدة البيانات

7. مثال كامل: لوحة طلبات أندرويد

KOTLIN
// ============================================
// OrderProcessor - لوحة طلبات أندرويد
// الميزة: واجهة Compose + ViewModel + StateFlow
// ============================================

// --- النطاق ---
data class Order(val id: String, val total: Double, val status: String, val customer: String)

// --- ViewModel ---
class OrderViewModel : ViewModel() {
    private val _orders = MutableStateFlow<List<Order>>(emptyList())
    val orders: StateFlow<List<Order>> = _orders.asStateFlow()

    private val _isLoading = MutableStateFlow(false)
    val isLoading: StateFlow<Boolean> = _isLoading.asStateFlow()

    init {
        loadOrders()
    }

    fun loadOrders() {
        viewModelScope.launch {
            _isLoading.value = true
            delay(500)  // محاكاة الشبكة
            _orders.value = listOf(
                Order("ORD-001", 299.99, "CONFIRMED", "Alice"),
                Order("ORD-002", 15_000.00, "PENDING", "Bob"),
                Order("ORD-003", 2_500.00, "SHIPPED", "Charlie"),
                Order("ORD-004", 8_900.00, "CONFIRMED", "Bob"),
                Order("ORD-005", 45.50, "CANCELLED", "Alice")
            )
            _isLoading.value = false
        }
    }

    fun totalRevenue(): Double = _orders.value
        .filter { it.status != "CANCELLED" }
        .sumOf { it.total }
}

// --- واجهة Compose (مفاهيمية - تتطلب وقت تشغيل أندرويد) ---
// @Composable
// fun OrderScreen(viewModel: OrderViewModel = viewModel()) {
//     val orders by viewModel.orders.collectAsState()
//     val isLoading by viewModel.isLoading.collectAsState()
//
//     Column(modifier = Modifier.fillMaxSize().padding(16.dp)) {
//         Text("لوحة الطلبات", style = MaterialTheme.typography.headlineMedium)
//         Text("الإيرادات: \$${viewModel.totalRevenue()} USD", style = MaterialTheme.typography.titleLarge)
//
//         if (isLoading) {
//             CircularProgressIndicator()
//         } else {
//             LazyColumn {
//                 items(orders) { order -> OrderCard(order) }
//             }
//         }
//     }
// }

// --- عرض JVM (محاكاة منطق ViewModel) ---
fun main() {
    val viewModel = OrderViewModel()

    // محاكاة مراقبة الحالة
    Thread.sleep(1000)  // انتظار التحميل

    println("=== لوحة طلبات أندرويد ===")
    println("الإيرادات: \$${viewModel.totalRevenue()} USD")
    println("جارٍ التحميل: ${viewModel.isLoading.value}")

    viewModel.orders.value.forEach { order ->
        val icon = when (order.status) {
            "CONFIRMED" -> "✅"
            "PENDING" -> "⏳"
            "SHIPPED" -> "📦"
            "CANCELLED" -> "❌"
            else -> "?"
        }
        println("  $icon ${order.id}: \$${order.total} USD (${order.customer}) [${order.status}]")
    }
}

الإخراج:

TEXT 📖 للعرض فقط
=== لوحة طلبات أندرويد ===
الإيرادات: $26699.99 USD
جارٍ التحميل: false
  ✅ ORD-001: $299.99 USD (Alice) [CONFIRMED]
  ⏳ ORD-002: $15000.0 USD (Bob) [PENDING]
  📦 ORD-003: $2500.0 USD (Charlie) [SHIPPED]
  ✅ ORD-004: $8900.0 USD (Bob) [CONFIRMED]
  ❌ ORD-005: $45.5 USD (Alice) [CANCELLED]

8. أمثلة عملية سريعة

▶ مثال: Activity مع Kotlin

KOTLIN
import android.os.Bundle
import android.widget.TextView
import androidx.appcompat.app.AppCompatActivity

class MainActivity : AppCompatActivity() {
    private lateinit var messageText: TextView

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        messageText = findViewById(R.id.message_text)
        messageText.text = buildString {
            appendLine("Welcome to Android with Kotlin!")
            appendLine("Device: ${android.os.Build.MODEL}")
            appendLine("Android version: ${android.os.Build.VERSION.RELEASE}")
        }
    }

    override fun onResume() {
        super.onResume()
        println("Activity resumed")
    }

    override fun onPause() {
        super.onPause()
        println("Activity paused")
    }
}

**الإخراج:

TEXT 📖 للعرض فقط
Welcome to Android with Kotlin!
Device: Pixel 6
Android version: 14

▶ مثال: Jetpack Compose أساسي

KOTLIN
import androidx.compose.foundation.layout.*
import androidx.compose.material3.*
import androidx.compose.runtime.*
import androidx.compose.ui.Modifier

@Composable
fun SimpleScreen(name: String) {
    var count by remember { mutableStateOf(0) }

    Column(
        modifier = Modifier.fillMaxSize().padding(16.dp),
        horizontalAlignment = androidx.compose.ui.Alignment.CenterHorizontally,
        verticalArrangement = Arrangement.Center
    ) {
        Text("Hello, $name!", style = MaterialTheme.typography.headlineMedium)
        Spacer(Modifier.height(16.dp))
        Text("Count: $count", style = MaterialTheme.typography.bodyLarge)
        Spacer(Modifier.height(16.dp))
        Button(onClick = { count++ }) {
            Text("Increment")
        }
    }
}

@Composable
fun CounterScreen() {
    var count by rememberSaveable { mutableStateOf(0) }

    Column(modifier = Modifier.padding(16.dp)) {
        Text("Counter: $count")
        Row {
            Button(onClick = { count++ }) { Text("+") }
            Spacer(Modifier.width(8.dp))
            Button(onClick = { count-- }) { Text("-") }
        }
    }
}

**الإخراج:

TEXT 📖 للعرض فقط
Hello, Kotlin!
Counter: 0
[+] [-]

▶ مثال: ViewModel مع StateFlow

KOTLIN
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import kotlinx.coroutines.flow.*
import kotlinx.coroutines.launch

data class OrderUiState(
    val orders: List<Order> = emptyList(),
    val isLoading: Boolean = false,
    val error: String? = null
)

class OrderViewModel : ViewModel() {
    private val _uiState = MutableStateFlow(OrderUiState())
    val uiState: StateFlow<OrderUiState> = _uiState.asStateFlow()

    fun loadOrders() {
        viewModelScope.launch {
            _uiState.value = _uiState.value.copy(isLoading = true)

            try {
                val orders = fetchOrders()
                _uiState.value = OrderUiState(orders = orders, isLoading = false)
            } catch (e: Exception) {
                _uiState.value = _uiState.value.copy(
                    isLoading = false,
                    error = e.message
                )
            }
        }
    }

    fun cancelOrder(orderId: String) {
        viewModelScope.launch {
            try {
                // استدعاء API للإلغاء
                _uiState.value = _uiState.value.copy(
                    orders = _uiState.value.orders.filter { it.id != orderId }
                )
            } catch (e: Exception) {
                _uiState.value = _uiState.value.copy(error = e.message)
            }
        }
    }

    private suspend fun fetchOrders(): List<Order> {
        // محاكاة API call
        delay(1000)
        return listOf(
            Order("ORD-001", 1500.0),
            Order("ORD-002", 500.0)
        )
    }
}

class Order(val id: String, val total: Double)

**الإخراج:

TEXT 📖 للعرض فقط

▶ مثال: Room Database

KOTLIN
import androidx.room.*

@Entity
data class UserEntity(
    @PrimaryKey val id: Int,
    val name: String,
    val email: String
)

@Dao
interface UserDao {
    @Query("SELECT * FROM UserEntity")
    suspend fun getAll(): List<UserEntity>

    @Query("SELECT * FROM UserEntity WHERE id = :id")
    suspend fun findById(id: Int): UserEntity?

    @Insert(onConflict = OnConflictStrategy.REPLACE)
    suspend fun insert(user: UserEntity)

    @Update
    suspend fun update(user: UserEntity)

    @Delete
    suspend fun delete(user: UserEntity)
}

@Database(entities = [UserEntity::class], version = 1)
abstract class AppDatabase : RoomDatabase() {
    abstract fun userDao(): UserDao
}

// الاستخدام
class UserRepository(private val dao: UserDao) {
    suspend fun getAllUsers(): List<UserEntity> = dao.getAll()
    suspend fun saveUser(user: UserEntity) = dao.insert(user)
}

// إنشاء قاعدة البيانات
val db = Room.databaseBuilder(
    context,
    AppDatabase::class.java,
    "app-db"
).build()

val userDao = db.userDao()

**الإخراج:

TEXT 📖 للعرض فقط

▶ مثال: Navigation في Compose

KOTLIN
import androidx.navigation.compose.*

sealed class Screen(val route: String) {
    object Home : Screen("home")
    object Detail : Screen("detail/{itemId}") {
        fun create(itemId: Int) = "detail/$itemId"
    }
    object Settings : Screen("settings")
}

@Composable
fun AppNavigation() {
    val navController = rememberNavController()

    NavHost(navController = navController, startDestination = Screen.Home.route) {
        composable(Screen.Home.route) {
            HomeScreen(
                onItemClick = { id ->
                    navController.navigate(Screen.Detail.create(id))
                },
                onSettingsClick = {
                    navController.navigate(Screen.Settings.route)
                }
            )
        }

        composable(Screen.Detail.route) { backStack ->
            val itemId = backStack.arguments?.getInt("itemId") ?: 0
            DetailScreen(itemId = itemId, onBack = {
                navController.popBackStack()
            })
        }

        composable(Screen.Settings.route) {
            SettingsScreen(onBack = {
                navController.popBackStack()
            })
        }
    }
}

@Composable
fun HomeScreen(onItemClick: (Int) -> Unit, onSettingsClick: () -> Unit) {
    Column {
        Text("Home Screen")
        Button(onClick = { onItemClick(42) }) { Text("Open Detail") }
        Button(onClick = onSettingsClick) { Text("Settings") }
    }
}

@Composable
fun DetailScreen(itemId: Int, onBack: () -> Unit) {
    Column {
        Text("Detail for item $itemId")
        Button(onClick = onBack) { Text("Back") }
    }
}

@Composable
fun SettingsScreen(onBack: () -> Unit) {
    Column {
        Text("Settings")
        Button(onClick = onBack) { Text("Back") }
    }
}

**الإخراج:

TEXT 📖 للعرض فقط

▶ مثال: ListAdapter مع RecyclerView

KOTLIN
import androidx.recyclerview.widget.RecyclerView
import androidx.recyclerview.widget.LinearLayoutManager

data class OrderItem(val id: String, val title: String, val subtitle: String)

class OrderAdapter : RecyclerView.Adapter<OrderAdapter.OrderViewHolder>() {
    private val items = mutableListOf<OrderItem>()

    fun submitList(newItems: List<OrderItem>) {
        items.clear()
        items.addAll(newItems)
        notifyDataSetChanged()
    }

    class OrderViewHolder(val view: android.view.View) : RecyclerView.ViewHolder(view) {
        fun bind(item: OrderItem) {
            view.findViewById<android.widget.TextView>(R.id.title).text = item.title
            view.findViewById<android.widget.TextView>(R.id.subtitle).text = item.subtitle
        }
    }

    override fun onCreateViewHolder(parent: android.view.ViewGroup, viewType: Int): OrderViewHolder {
        val view = android.view.LayoutInflater.from(parent.context)
            .inflate(R.layout.item_order, parent, false)
        return OrderViewHolder(view)
    }

    override fun onBindViewHolder(holder: OrderViewHolder, position: Int) {
        holder.bind(items[position])
    }

    override fun getItemCount(): Int = items.size
}

// الاستخدام
val recyclerView: RecyclerView = findViewById(R.id.orders_recycler)
recyclerView.layoutManager = LinearLayoutManager(this)
val adapter = OrderAdapter()
recyclerView.adapter = adapter

adapter.submitList(listOf(
    OrderItem("1", "ORD-001", "$1500.00"),
    OrderItem("2", "ORD-002", "$500.00")
))

**الإخراج:

TEXT 📖 للعرض فقط

▶ مثال: Coroutines + Lifecycle

KOTLIN
import androidx.lifecycle.*
import kotlinx.coroutines.*

class MyViewModel : ViewModel() {
    private val _data = MutableLiveData<String>()
    val data: LiveData<String> = _data

    fun loadData() {
        // viewModelScope يُلغى عند onCleared()
        viewModelScope.launch {
            try {
                val result = withContext(Dispatchers.IO) {
                    // محاكاة استدعاء API
                    delay(1000)
                    "Data loaded at ${System.currentTimeMillis()}"
                }
                _data.value = result
            } catch (e: Exception) {
                _data.value = "Error: ${e.message}"
            }
        }
    }
}

class MyActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        val viewModel = ViewModelProvider(this)[MyViewModel::class.java]
        viewModel.data.observe(this) { data ->
            // التحديث في الـ main thread
            findViewById<TextView>(R.id.data_text).text = data
        }

        // launchWhenStarted - يبدأ عند STARTED، يُلغى عند STOPPED
        lifecycleScope.launchWhenStarted {
            viewModel.loadData()
        }

        // repeatOnLifecycle - أنظف lifecycle-aware coroutine
        lifecycleScope.launch {
            repeatOnLifecycle(Lifecycle.State.STARTED) {
                viewModel.loadData()
            }
        }
    }
}

**الإخراج:

TEXT 📖 للعرض فقط

❓ أسئلة شائعة

س هل يجب عليّ استخدام Compose؟ هل يمكنني الاستمرار في استخدام XML؟
ج يمكنك الاستمرار في استخدام XML، لكن Google توصي بـ Compose كخيار افتراضي للمشاريع الجديدة. يمكن لـ Compose و XML التعايش (استخدم ComposeView لتضمين Compose في XML).
س ما الفرق بين ViewModel و Presenter؟
ج ViewModel لا يتدمّر عند تغييرات التهيئة (دوران الشاشة) — البيانات تُحفظ تلقائيًا. Presenter يتطلب حفظ/استعادة يدوي عند تغييرات التهيئة.
س كيف أختار بين StateFlow و SharedFlow؟
ج StateFlow لحمل الحالة (يجب أن يكون له قيمة أولية، يزيل التكرار). SharedFlow لتدفقات الأحداث (بث متعدد، بدون قيمة أولية). استخدم StateFlow لحالة واجهة المستخدم، وSharedFlow للأحداث اللحظية.
س هل يدعم Room الكوروتينات؟
ج نعم. دوال Dao suspend تُنفّذ تلقائيًا على تجمع خيوط Room. الاستعلامات التي تُرجع Flow تُعيد البث تلقائيًا عند تغيير البيانات.
س كيف هو أداء Compose؟
ج Compose يستخدم إعادة التأليف الذكية (يعيد رسم Composables المتغيرة فقط)، بأداء قريب من Views المكتوبة يدويًا. تجنب الحسابات داخل Composable — استخدم remember و derivedStateOf للتخزين المؤقت.
س كيف أتعامل مع التنقل في Compose؟
ج استخدم NavHost + composable() لتعريف رسم تنقل، navController.navigate() للتنقل. التنقل الآمن الأنواع متاح في Compose 1.0+.

📖 ملخص


📝 تمارين

  1. مبتدئ (⭐): أنشئ دالة Compose Greeting تعرض "مرحبًا، $name!". تلميح: @Composable fun Greeting(name: String) { Text("مرحبًا، $name!") }
  2. متوسط (⭐⭐): استخدم ViewModel + StateFlow لتنفيذ عداد مع واجهة Compose تعرض العدد في الوقت الفعلي. تلميح: MutableStateFlow(0) + collectAsState()
  3. متقدم (⭐⭐⭐): نفّذ واجهة Compose كاملة لقائمة طلبات: LazyColumn تعرض الطلبات، سحب للتحديث، نقر للتنقل إلى التفاصيل. تلميح: LazyColumn + pullRefresh + NavController

← السابق | التالي →

Web-Tutorial.com

فريق Web-Tutorial التقني

منصة دروس برمجية يديرها عدة مطورين. كل درس يتم كتابته ومراجعته بواسطة مطورين متخصصين في المجال. نعمل على ضمان دقة وموثوقية المحتوى — إذا لاحظت أي مشكلة، فيرجى إخبارنا.

100%