0Pricing
Android Academy · درس

تحميل الصور باستخدام Coil

تحميل الصور بشكل غير متزامن في Compose

تحميل الصور باستخدام Coil درس مجاني في Android Academy على CoddyKit. هذا هو الدرس 1 من أصل 4. يمكنك قراءة الدرس كاملاً أدناه مجاناً — ثم تمرن عليه مباشرة في المتصفح باستخدام محرر أكواد مدمج ومدرس ذكاء اصطناعي متاح 24/7. هذا الدرس جزء من مسار التعلم في Android Academy، وتقدمك يتزامن عبر الويب وتطبيق CoddyKit. تتضمن دورة Android Academy 4 دروس في المجموع.

لماذا تحتاج إلى محمّل للصور

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

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

يتولى Coil (محمّل الصور باستخدام Coroutine) كل ذلك نيابةً عنك من خلال واجهة برمجة تطبيقات صغيرة ومتوافقة مع Compose. في هذا الدرس ستحمّل أول صورة بعيدة لك.

إضافة Coil إلى مشروعك

يتوفر Coil على هيئة تبعية لـ Gradle. بالنسبة إلى Jetpack Compose، تحتاج إلى العنصر coil-compose الذي يوفر العنصر القابل للتركيب AsyncImage.

أضفه إلى ملف build.gradle.kts الخاص بالوحدة. يُعد Coil 3 الإصدار الرئيسي الحالي، ويعمل عبر الأنظمة الأساسية؛ أما تكامل Compose فهو ما نستخدمه هنا.

// build.gradle.kts (module level)
dependencies {
    implementation("io.coil-kt.coil3:coil-compose:3.0.4")
    implementation("io.coil-kt.coil3:coil-network-okhttp:3.0.4")
}

إذن الإنترنت

لتنزيل الصور من عنوان URL، يحتاج تطبيقك إلى إذن INTERNET. هذا إذن عادي يُمنح وقت التثبيت، لذا لا تحتاج إلا إلى التصريح به في البيان. ولا يظهر طلب وقت التشغيل.

من دونه، يفشل كل طلب شبكة باستثناء أمني.

<!-- AndroidManifest.xml -->
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
    <uses-permission android:name="android.permission.INTERNET" />
    <application ...>
        ...
    </application>
</manifest>

أول استخدام لـ AsyncImage

أبسط طريقة لعرض صورة بعيدة في Compose هي استخدام AsyncImage. مرر إليه model، وهو عادةً سلسلة عنوان URL، وcontentDescription، وسيتولى Coil الباقي: الجلب في الخلفية وفك الترميز والعرض.

يستخدم تلقائيًا coroutines مرتبطة بالتركيب، لذلك يُلغى الطلب إذا غادر العنصر القابل للتركيب الشاشة.

import androidx.compose.foundation.layout.size
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import coil3.compose.AsyncImage

@Composable
fun Avatar() {
    AsyncImage(
        model = "https://example.com/avatar.png",
        contentDescription = "User avatar",
        modifier = Modifier.size(96.dp)
    )
}

contentScale: كيفية احتواء الصورة

نادرًا ما تطابق الصور الحجم الدقيق للمساحة المخصصة لها في التخطيط. يتحكم ContentScale في كيفية تغيير حجم الصورة النقطية داخل حدودها.

  • يملأ ContentScale.Crop المربع ويقص الجزء الزائد، وهو مناسب جدًا للصور الرمزية والمصغرات.
  • يحافظ ContentScale.Fit على ظهور الصورة كاملة، وقد يترك مساحة فارغة.
  • يمدد ContentScale.FillBounds الصورة لملء الحدود، وقد يؤدي ذلك إلى تشويهها.

يُعد Crop الخيار الأكثر شيوعًا للمساحات ذات الحجم الثابت.

import androidx.compose.foundation.layout.size
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.unit.dp
import coil3.compose.AsyncImage

@Composable
fun CircleAvatar(url: String) {
    AsyncImage(
        model = url,
        contentDescription = "Avatar",
        contentScale = ContentScale.Crop,
        modifier = Modifier
            .size(80.dp)
            .clip(CircleShape)
    )
}

إنشاء طلب باستخدام ImageRequest

يمثل تمرير سلسلة عنوان URL الطريقة السريعة. ولمزيد من التحكم، أنشئ ImageRequest. يتيح لك ذلك ضبط خيارات مثل التلاشي المتقاطع والرؤوس والتحويلات ومفاتيح التخزين المؤقت.

تحتاج إلى Context، ويوفره Compose من خلال LocalContext.current.

import androidx.compose.runtime.Composable
import androidx.compose.ui.platform.LocalContext
import coil3.compose.AsyncImage
import coil3.request.ImageRequest
import coil3.request.crossfade

@Composable
fun ProductImage(url: String) {
    val context = LocalContext.current
    val request = ImageRequest.Builder(context)
        .data(url)
        .crossfade(true)
        .build()

    AsyncImage(
        model = request,
        contentDescription = "Product photo"
    )
}

التلاشي المتقاطع للتحميل السلس

تظهر الصورة افتراضيًا فور انتهاء تحميلها، وقد يبدو ذلك مفاجئًا. يجعل crossfade(true) الصورة تتلاشى تدريجيًا خلال حركة قصيرة.

يمكنك أيضًا تمرير مدة بالميلي ثانية لجعل التلاشي أبطأ أو أسرع. تضفي هذه اللمسة الصغيرة مظهرًا أكثر صقلًا على القوائم.

import coil3.request.ImageRequest
import coil3.request.crossfade
import android.content.Context

fun smoothRequest(context: Context, url: String) =
    ImageRequest.Builder(context)
        .data(url)
        .crossfade(durationMillis = 300)
        .build()

التحميل من مصادر مختلفة

إن model في AsyncImage مرن. يستطيع Coil التحميل من أنواع مصادر متعددة، وليس من عناوين URL البعيدة فقط:

  • كائن String أو Uri للشبكة أو الملفات المحلية.
  • معرّف مورد قابل للرسم (R.drawable.logo).
  • كائن File من الجهاز.
  • كائن Uri للمحتوى من منتقي الصور.

وهذا يعني أن العنصر القابل للتركيب نفسه يتعامل بسلاسة مع الصور البعيدة والمحلية.

import android.net.Uri
import androidx.compose.runtime.Composable
import coil3.compose.AsyncImage

@Composable
fun FlexibleImage(source: Any) {
    // source can be a URL String, a Uri, a File, or a resource id
    AsyncImage(
        model = source,
        contentDescription = null
    )
}

// Examples:
// FlexibleImage("https://example.com/pic.jpg")
// FlexibleImage(Uri.parse("content://media/external/images/1"))

التفاعل مع حالة التحميل

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

سنتعمق في العناصر النائبة في الدرس التالي؛ أما هنا فسنوضح شكل التحميل الذي يراعي الحالة.

import androidx.compose.material3.CircularProgressIndicator
import androidx.compose.runtime.Composable
import coil3.compose.SubcomposeAsyncImage

@Composable
fun StatefulImage(url: String) {
    SubcomposeAsyncImage(
        model = url,
        contentDescription = "Photo",
        loading = { CircularProgressIndicator() }
    )
}

الإلغاء متاح تلقائيًا

من أكبر مزايا Coil الإلغاء التلقائي. بما أن AsyncImage يطلق طلبه داخل التركيب، يُلغى الطلب الجاري عندما يغادر العنصر القابل للتركيب الشاشة، مثلما يحدث عند خروج صف من LazyColumn أثناء التمرير.

يوفر ذلك النطاق الترددي ووحدة المعالجة المركزية ويمنع العمل المهدَر. ولا تحتاج إلى كتابة أي تعليمات للإلغاء بنفسك، ولهذا يتوسع Coil بكفاءة في القوائم الطويلة.

import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import coil3.compose.AsyncImage

@Composable
fun PhotoFeed(urls: List<String>) {
    LazyColumn {
        items(urls) { url ->
            AsyncImage(
                model = url,
                contentDescription = null,
                modifier = Modifier
                    .fillMaxWidth()
                    .height(200.dp)
            )
        }
    }
}

بطاقة صور مكتملة

لنجمع ما تعلمناه: بطاقة تحتوي على صورة بعيدة تملأ الجزء العلوي، وتستخدم التلاشي المتقاطع، وتقص الصورة لتناسب ارتفاعًا ثابتًا. هذا نمط أساسي ستعيد استخدامه باستمرار.

import androidx.compose.foundation.layout.*
import androidx.compose.material3.Card
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.unit.dp
import coil3.compose.AsyncImage
import coil3.request.ImageRequest
import coil3.request.crossfade

@Composable
fun ImageCard(title: String, imageUrl: String) {
    val context = LocalContext.current
    Card(modifier = Modifier.fillMaxWidth().padding(16.dp)) {
        Column {
            AsyncImage(
                model = ImageRequest.Builder(context)
                    .data(imageUrl)
                    .crossfade(true)
                    .build(),
                contentDescription = title,
                contentScale = ContentScale.Crop,
                modifier = Modifier.fillMaxWidth().height(180.dp)
            )
            Text(text = title, modifier = Modifier.padding(16.dp))
        }
    }
}

تحقق سريع

ما العنصر القابل للتركيب من coil-compose الذي يمثل أبسط طريقة لعرض صورة بعيدة باستخدام عنوان URL في Jetpack Compose؟

مراجعة

لقد حمّلت أول صور بعيدة لك باستخدام Coil. أهم النقاط:

  • أضف coil-compose وتبعية شبكة OkHttp، بالإضافة إلى إذن INTERNET.
  • يمثل AsyncImage(model = url, contentDescription = ...) الطريقة السريعة.
  • استخدم ContentScale.Crop مع حجم ثابت للصور الرمزية والمصغرات.
  • أنشئ ImageRequest للتلاشي المتقاطع ولمزيد من التحكم.
  • يلغي Coil تلقائيًا الطلبات الجارية عندما تغادر العناصر القابلة للتركيب الشاشة.

التالي: جعل التحميل سلسًا باستخدام العناصر النائبة وحالات الخطأ.

الأسئلة الشائعة

هل درس «تحميل الصور باستخدام Coil» مجاني؟

نعم — نص درس «تحميل الصور باستخدام Coil» كامل متاح مجاناً هنا على الويب. لتمرينه بشكل تفاعلي (محرر أكواد مدمج ومدرس ذكاء اصطناعي متاح 24/7) وفتح باقي دورة Android Academy، انتقل إلى CoddyKit PRO. تتضمن دورة Android Academy 4 دروس في المجموع.

ماذا ستتعلم في «تحميل الصور باستخدام Coil»؟

تحميل الصور بشكل غير متزامن في Compose تتمرن على Android Academy مع أكواد عملية تشغلها مباشرة في المتصفح، ومدرس ذكاء اصطناعي متاح 24/7 يجيب على أسئلتك أثناء عملك.

هل أحتاج إلى خبرة سابقة لأبدأ Android Academy؟

لا تُشترط خبرة سابقة. Android Academy على CoddyKit منظم للمبتدئين حتى المتقدمين، لذا يمكنك البدء من هنا أو من البداية والتقدم بسرعتك الخاصة. هذا هو الدرس 1 من أصل 4.

كم من الوقت يستغرق درس «تحميل الصور باستخدام Coil»؟

معظم دروس CoddyKit تستغرق حوالي 5–10 دقائق. كل منها موجز وتفاعلي، لذا تحرز تقدماً مستمراً وتستأنف من حيث توقفت عبر الويب والتطبيق.

هل يمكنني كتابة وتشغيل أكواد في درس Android Academy هذا؟

نعم. كل درس في Android Academy يتضمن محرر أكواد مدمج، لذا تكتب وتشغل أكواداً حقيقية مباشرة في متصفحك وتحصل على تعليقات فورية من الذكاء الاصطناعي — بدون إعداد محلي.

جميع الدروس في هذه الدورة

  1. تحميل الصور باستخدام Coil
  2. العناصر النائبة وحالات الخطأ
  3. التخزين المؤقت والأداء
  4. تشغيل الصوت والفيديو
← العودة إلى Android Academy