التعامل مع إجراءات لوحة المفاتيح

عندما يركّز المستخدم على مكوّن نصي قابل للتعديل، مثل TextField، وكان الجهاز مزوّدًا بلوحة مفاتيح، يتولّى النظام معالجة جميع الإدخالات. يمكنك توفير اختصارات لوحة المفاتيح من خلال معالجة أحداث المفاتيح.

اختصارات لوحة المفاتيح التلقائية

تتوفّر اختصارات لوحة المفاتيح التالية تلقائيًا.

اختصارات لوحة المفاتيح الإجراء الدوال المركّبة التي تتيح استخدام الاختصار
Shift+Ctrl+السهم المتّجه لليسار/السهم المتّجه لليمين اختيار النص حتى بداية الكلمة أو نهايتها BasicTextField، TextField
‫Shift+Ctrl+السهم المتّجه للأعلى/السهم المتّجه للأسفل اختيار النص حتى بداية الفقرة أو نهايتها BasicTextField، TextField
‫Shift+Alt+السهم المتّجه للأعلى/السهم المتّجه للأسفل أو Shift+Meta+السهم المتّجه لليسار/السهم المتّجه لليمين تحديد النص حتى بداية النص أو نهايته BasicTextField، TextField
‫Shift+السهم المتّجه لليسار/السهم المتّجه لليمين اختيار الشخصيات BasicTextField، TextField
Ctrl+A اختيار الكل BasicTextField، TextField
‫Ctrl+C/Ctrl+X/Ctrl+V النسخ/القص/اللصق BasicTextField، TextField
‫Ctrl+Z/Ctrl+Shift+Z التراجع/الإعادة BasicTextField، TextField
PageDown/PageUp التمرير ‫LazyColumn، عنصر التعديل verticalScroll، عنصر التعديل scrollable

الأحداث المهمة

في Compose، يمكنك التعامل مع ضغطة مفتاح فردية باستخدام المعدِّل onKeyEvent. يقبل المُعدِّل دالة lambda التي يتم استدعاؤها عندما يتلقّى المكوّن المُعدَّل حدثًا رئيسيًا. يتم وصف الحدث الرئيسي كعنصر KeyEvent. يمكنك الحصول على معلومات كل حدث رئيسي من خلال الرجوع إلى العنصر في دالة lambda التي تم تمريرها إلى المُعدِّل onKeyEvent.

تؤدي ضغطة المفتاح إلى إرسال حدثَين رئيسيَين. يتم تشغيل أحدهما عندما يضغط المستخدم على المفتاح؛ يتم تشغيل الآخر عند تحرير المفتاح. يمكنك التمييز بين الحدثَين الرئيسيَين من خلال الرجوع إلى السمة type للعنصر KeyEvent.

تشير القيمة المعروضة من دالة lambda onKeyEvent إلى ما إذا تم التعامل مع الحدث الرئيسي أم لا. يجب عرض القيمة true إذا كان تطبيقك يعالج الحدث الرئيسي، ما يؤدي إلى إيقاف انتشار الحدث.

يوضّح المقتطف التالي كيفية استدعاء دالة doSomething() عندما يحرّر المستخدم مفتاح S في المكوّن Box:

Box(
    modifier = Modifier
        .onKeyEvent {
            if (
                it.type == KeyEventType.KeyUp &&
                it.key == Key.S
            ) {
                doSomething()
                true
            } else {
                false
            }
        }
        .focusable()
) {
    Text("Press S key")
}

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

يحتوي الكائن KeyEvent على السمات التالية التي تشير إلى ما إذا تم الضغط على مفاتيح التعديل أم لا:

يجب أن تكون دقيقًا في وصف الأحداث الرئيسية التي يتعامل معها تطبيقك. يستدعي المقتطف التالي دالة doSomething() فقط إذا حرّر المستخدم مفتاح S فقط. إذا ضغط المستخدم على أي مفتاح تعديل، مثل مفتاح Shift، لن يستدعي التطبيق الدالة.

Box(
    modifier = Modifier
        .focusable()
        .onKeyEvent {
            if (
                it.type == KeyEventType.KeyUp &&
                it.key == Key.S &&
                !it.isAltPressed &&
                !it.isCtrlPressed &&
                !it.isMetaPressed &&
                !it.isShiftPressed
            ) {
                doSomething()
                true
            } else {
                false
            }
        }
) {
    Text("Press S key with a modifier key")
}

أحداث النقر على مفتاحَي المسافة وEnter

يؤدي الضغط على مفتاح المسافة ومفتاح Enter إلى تفعيل الأحداث الناتجة عن النقر أيضًا. على سبيل المثال، يمكن للمستخدمين تبديل تشغيل الوسائط (تشغيل أو إيقاف مؤقت) باستخدام مفتاح المسافة أو مفتاح Enter من خلال معالجة الأحداث الناتجة عن النقر على النحو التالي:

MediaPlayer(modifier = Modifier.clickable { togglePausePlay() })

يعترض المُعدِّل clickable أحداث المفاتيح ويستدعي دالة الاستدعاء onClick() عند الضغط على مفتاح المسافة أو Enter. لهذا السبب، يتم استدعاء الدالة togglePausePlay() بالضغط على مفتاح المسافة أو Enter في المقتطف.

الأحداث الرئيسية غير المستهلكة

يتم نشر أحداث المفاتيح غير المستهلكة من المكوّن الذي وقع فيه الحدث إلى المكوّن الخارجي المرفق. في المثال أدناه، يستهلك InnerComponent الأحداث الرئيسية عند تحرير المفتاح S، وبالتالي لا يتلقّى OuterComponent أي أحداث رئيسية يتم تشغيلها عن طريق تحرير المفتاح S. لهذا السبب، لا يتم استدعاء الدالة actionB() أبدًا.

يمكن معالجة الأحداث الرئيسية الأخرى على InnerComponent، مثل تحرير المفتاح D، بواسطة OuterComponent. يتم استدعاء الدالة actionC() لأنّ الحدث الرئيسي لتحرير مفتاح D يتم نشره في OuterComponent.

OuterComponent(
    modifier = Modifier.onKeyEvent {
        when {
            it.type == KeyEventType.KeyUp && it.key == Key.S -> {
                actionB() // This function is never called.
                true
            }

            it.type == KeyEventType.KeyUp && it.key == Key.D -> {
                actionC()
                true
            }

            else -> false
        }
    }
) {
    InnerComponent(
        modifier = Modifier.onKeyEvent {
            if (it.type == KeyEventType.KeyUp && it.key == Key.S) {
                actionA()
                true
            } else {
                false
            }
        }
    )
}

معدِّل واحد (onKeyPreviewEvent)

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

val focusManager = LocalFocusManager.current
var textFieldValue by remember { mutableStateOf(TextFieldValue()) }

TextField(
    textFieldValue,
    onValueChange = {
        textFieldValue = it
    },
    modifier = Modifier.onPreviewKeyEvent {
        if (it.type == KeyEventType.KeyUp && it.key == Key.Tab) {
            focusManager.moveFocus(FocusDirection.Next)
            true
        } else {
            false
        }
    }
)

يضيف مكوّن TextField تلقائيًا حرف علامة تبويب في كل مرة يضغط فيها المستخدمون على مفتاح Tab، حتى إذا تم التعامل مع حدث المفتاح باستخدام المُعدِّل onKeyEvent. لنقل تركيز لوحة المفاتيح بدون إضافة أي أحرف جدولة، يجب معالجة حدث المفتاح قبل بدء الإجراءات المرتبطة به، كما هو موضّح في المقتطف. تعترض دالة lambda onKeyPreviewEvent() الحدث الرئيسي من خلال عرض true.

يمكن للمكوّن الرئيسي اعتراض حدث المفتاح الذي يحدث على مكوّناته الفرعية. في المقتطف التالي، يتم استدعاء الدالة previewSKey() عندما يضغط المستخدمون على مفتاح S، بدلاً من استدعاء الدالة actionForPreview().

Column(
    modifier = Modifier.onPreviewKeyEvent {
        if (it.key == Key.S) {
            previewSKey()
            true
        } else {
            false
        }
    }
) {
    Box(
        modifier = Modifier
            .focusable()
            .onPreviewKeyEvent {
                actionForPreview(it)
                false
            }
            .onKeyEvent {
                actionForKeyEvent(it)
                true
            }
    ) {
        Text("Press any key")
    }
}

لا يتم تشغيل دالة onPreviewKeyEvent() Lambda للمكوّن Box عندما يضغط المستخدمون على مفتاح Tab أيضًا. يتم استدعاء دالة lambda onPreviewKeyEvent() في المكوّن الرئيسي أولاً، ثم يتم استدعاء onPreviewKeyEvent() في المكوّن الفرعي. يمكنك تنفيذ اختصارات لوحة المفاتيح على مستوى الشاشة من خلال الاستفادة من هذا السلوك.

مراجع إضافية