استخدام طرق العرض في "إنشاء"

يمكنك تضمين بنية Android View الهرمية في واجهة مستخدم Compose. ويكون هذا الأسلوب مفيدًا بشكل خاص إذا كنت تريد استخدام عناصر واجهة مستخدم غير متاحة بعد في Compose، مثل AdView. يتيح لك هذا الأسلوب أيضًا إعادة استخدام طرق العرض المخصّصة التي صمّمتها.

لتضمين عنصر عرض أو بنية هرمية، استخدِم الدالة البرمجية القابلة للإنشاء AndroidView . يتم تمرير دالة lambda إلى AndroidView تعرض View. توفّر AndroidView أيضًا update دالة ردّ اتصال يتم استدعاؤها عند توسيع طريقة العرض. تتم إعادة إنشاء AndroidView كلما تغيّرت قيمة State التي تم قراءتها في الدالة التنفيذية. تتلقّى الدالة AndroidView، مثل العديد من الدوال البرمجية الأخرى القابلة للإنشاء المضمّنة، المَعلمة Modifier التي يمكن استخدامها، على سبيل المثال، لضبط موضعها في الدالة البرمجية القابلة للإنشاء الرئيسية.

@Composable
fun CustomView() {
    var selectedItem by remember { mutableIntStateOf(0) }

    // Adds view to Compose
    AndroidView(
        modifier = Modifier.fillMaxSize(), // Occupy the max size in the Compose UI tree
        factory = { context ->
            // Creates view
            MyView(context).apply {
                // Sets up listeners for View -> Compose communication
                setOnClickListener {
                    selectedItem = 1
                }
            }
        },
        update = { view ->
            // View's been inflated or state read in this block has been updated
            // Add logic here if necessary

            // As selectedItem is read here, AndroidView will recompose
            // whenever the state changes
            // Example of Compose -> View communication
            view.selectedItem = selectedItem
        }
    )
}

@Composable
fun ContentExample() {
    Column(Modifier.fillMaxSize()) {
        Text("Look at this CustomView!")
        CustomView()
    }
}

AndroidView مع ربط طرق العرض

لتضمين تصميم XML، استخدِم واجهة برمجة التطبيقات AndroidViewBinding التي توفّرها مكتبة androidx.compose.ui:ui-viewbinding. لإجراء ذلك، يجب أن يفعّل مشروعك ربط العرض.

@Composable
fun AndroidViewBindingExample() {
    AndroidViewBinding(ExampleLayoutBinding::inflate) {
        exampleView.setBackgroundColor(Color.GRAY)
    }
}

AndroidView في القوائم الكسولة

إذا كنت تستخدم AndroidView في قائمة Lazy (LazyColumn أو LazyRow أو Pager أو غير ذلك)، ننصحك باستخدام AndroidView الذي تم تقديمه في الإصدار 1.4.0-rc01. تتيح عملية التحميل الزائد هذه لـ Compose إعادة استخدام مثيل View الأساسي عند إعادة استخدام التركيب الحاوي كما هو، كما هو الحال مع القوائم الكسولة.

تضيف هذه الزيادة في التحميل الزائد AndroidView مَعلمتَين إضافيتَين:

  • onReset: دالة يتم استدعاؤها للإشارة إلى أنّ View سيتم إعادة استخدامه قريبًا. يجب أن تكون هذه القيمة غير فارغة لتفعيل إعادة استخدام العرض.
  • onRelease (اختياري): دالة ردّ يتم استدعاؤها للإشارة إلى أنّ View قد خرج من التركيب ولن تتم إعادة استخدامه مرة أخرى.

@Composable
fun AndroidViewInLazyList() {
    LazyColumn {
        items(100) { index ->
            AndroidView(
                modifier = Modifier.fillMaxSize(), // Occupy the max size in the Compose UI tree
                factory = { context ->
                    MyView(context)
                },
                update = { view ->
                    view.selectedItem = index
                },
                onReset = { view ->
                    view.clear()
                }
            )
        }
    }
}

الأجزاء في Compose

استخدِم العنصر القابل للإنشاء AndroidViewBinding لإضافة Fragment في Compose. يتضمّن AndroidViewBinding معالجة خاصة بالجزء، مثل إزالة الجزء عندما يغادر العنصر القابل للإنشاء التركيب.

يمكنك إجراء ذلك من خلال توسيع ملف XML يحتوي على FragmentContainerView كعنصر نائب عن Fragment.

على سبيل المثال، إذا كان لديك my_fragment_layout.xml محدّد، يمكنك استخدام رمز مشابه لما يلي مع استبدال سمة XML android:name باسم فئة Fragment:

<androidx.fragment.app.FragmentContainerView xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/fragment_container_view"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:name="com.example.compose.snippets.interop.MyFragment" />

يمكنك توسيع هذا الجزء في Compose على النحو التالي:

@Composable
fun FragmentInComposeExample() {
    AndroidViewBinding(MyFragmentLayoutBinding::inflate) {
        val myFragment = fragmentContainerView.getFragment<MyFragment>()
        // ...
    }
}

إذا كنت بحاجة إلى استخدام عدة أجزاء في التصميم نفسه، تأكَّد من تحديد معرّف فريد لكل FragmentContainerView.

استدعاء إطار عمل Android من Compose

تعمل Compose ضمن فئات إطار عمل Android. على سبيل المثال، تتم استضافته على فئات Android View، مثل Activity أو Fragment، وقد يستخدم فئات إطار عمل Android، مثل Context أو موارد النظام Service أو BroadcastReceiver.

لمزيد من المعلومات حول موارد النظام، يمكنك الاطّلاع على الموارد في Compose.

Composition Locals

تسمح فئات CompositionLocal بتمرير البيانات ضمنيًا من خلال الدوال القابلة للإنشاء. ويتم عادةً توفيرها بقيمة في عقدة معيّنة من شجرة واجهة المستخدم. ويمكن أن تستخدم العناصر التابعة القابلة للإنشاء هذه القيمة بدون تعريف CompositionLocal كمعلَمة في الدالة القابلة للإنشاء.

يُستخدَم CompositionLocal لنقل قيم أنواع إطار عمل Android في Compose، مثل Context أو Configuration أو View التي يتم فيها استضافة رمز Compose مع LocalContext أو LocalConfiguration أو LocalView المقابل. يُرجى العِلم أنّ فئات CompositionLocal يتم تحديد بادئتها بـ Local لتسهيل العثور عليها باستخدام ميزة الإكمال التلقائي في بيئة التطوير المتكاملة.

يمكنك الوصول إلى القيمة الحالية لـ CompositionLocal باستخدام السمة current. على سبيل المثال، يعرض الرمز أدناه رسالة إشعار مؤقت من خلال توفير LocalContext.current في طريقة Toast.makeToast.

@Composable
fun ToastGreetingButton(greeting: String) {
    val context = LocalContext.current
    Button(onClick = {
        Toast.makeText(context, greeting, Toast.LENGTH_SHORT).show()
    }) {
        Text("Greet")
    }
}

للاطّلاع على مثال أكثر اكتمالاً، راجِع قسم دراسة حالة: BroadcastReceivers في نهاية هذا المستند.

تفاعلات أخرى

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

class OtherInteractionsActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        // get data from savedInstanceState
        setContent {
            MaterialTheme {
                ExampleComposable(data, onButtonClick = {
                    startActivity(Intent(this, MyActivity::class.java))
                })
            }
        }
    }
}

@Composable
fun ExampleComposable(data: DataExample, onButtonClick: () -> Unit) {
    Button(onClick = onButtonClick) {
        Text(data.title)
    }
}

دراسة حالة: مستقبِلات البث

للحصول على مثال أكثر واقعية للميزات التي قد تحتاج إلى نقلها أو تنفيذها في Compose، ولعرض CompositionLocal والتأثيرات الجانبية، لنفترض أنّه يجب تسجيل BroadcastReceiver من دالة قابلة للإنشاء.

يستفيد الحلّ من LocalContext لاستخدام السياق الحالي، ومن التأثيرات الجانبية rememberUpdatedState وDisposableEffect.

@Composable
fun SystemBroadcastReceiver(
    systemAction: String,
    onSystemEvent: (intent: Intent?) -> Unit
) {
    // Grab the current context in this part of the UI tree
    val context = LocalContext.current

    // Safely use the latest onSystemEvent lambda passed to the function
    val currentOnSystemEvent by rememberUpdatedState(onSystemEvent)

    // If either context or systemAction changes, unregister and register again
    DisposableEffect(context, systemAction) {
        val intentFilter = IntentFilter(systemAction)
        val broadcast = object : BroadcastReceiver() {
            override fun onReceive(context: Context?, intent: Intent?) {
                currentOnSystemEvent(intent)
            }
        }

        context.registerReceiver(broadcast, intentFilter)

        // When the effect leaves the Composition, remove the callback
        onDispose {
            context.unregisterReceiver(broadcast)
        }
    }
}

@Composable
fun HomeScreen() {

    SystemBroadcastReceiver(Intent.ACTION_BATTERY_CHANGED) { batteryStatus ->
        val isCharging = /* Get from batteryStatus ... */ true
        /* Do something if the device is charging */
    }

    /* Rest of the HomeScreen */
}

الخطوات التالية

بعد التعرّف على واجهات برمجة التطبيقات الخاصة بالتوافق عند استخدام Compose في "طرق العرض" والعكس، يمكنك الاطّلاع على صفحة اعتبارات أخرى لمعرفة المزيد.