رسیدگی به کنش‌های صفحه‌کلید

وقتی کاربر روی مؤلفه نوشتاری قابل‌ویرایش، مثل TextField، تمرکز می‌کند، و صفحه‌کلید سخت‌افزاری به دستگاه متصل است، همه ورودی‌ها توسط سیستم مدیریت می‌شود. می‌توانید با مدیریت رویدادهای کلیدی، میان‌برهای صفحه‌کلید ارائه دهید.

میان‌برهای پیش‌فرض صفحه‌کلید

میان‌برهای صفحه‌کلید زیر به‌طور پیش‌فرض دردسترس است.

میان‌بر صفحه‌کلید کنش ترکیب‌پذیرهایی که از میان‌بر پشتیبانی می‌کنند
تبدیل+مهار+کلید چپ‌بَر/کلید راست‌بَر انتخاب نوشتار تا ابتدا/انتهای کلمه BasicTextField، TextField
تبدیل+مهار+کلید بالا/کلید پایین انتخاب نوشتار تا ابتدا/انتهای پاراگراف BasicTextField، TextField
تبدیل+دگرساز+کلید بالابر/کلید پایین‌بر یا تبدیل+متا+کلید چپ‌بَر/کلید راست‌بَر انتخاب نوشتار تا ابتدا/انتهای نوشتار BasicTextField، TextField
تبدیل+کلید چپ‌بَر/کلید راست‌بَر انتخاب نویسه‌ها 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، یک کلیدزنی تکی را مدیریت کنید. اصلاح‌کننده یک لامبدا را می‌پذیرد که وقتی مؤلفه اصلاح‌شده رویداد کلیدی را دریافت می‌کند، فراخوانی می‌شود. رویداد کلیدی به‌عنوان شیء KeyEvent توصیف می‌شود. با ارجاع به شیء در لامبدا که به اصلاح‌کننده onKeyEvent ارسال شده است، می‌توانید اطلاعات هر رویداد کلیدی را دریافت کنید.

یک ضربه کلید دو رویداد کلیدی ارسال می‌کند. یکی وقتی کاربر کلید را فشار می‌دهد راه‌اندازی می‌شود؛ دیگری وقتی کلید رها می‌شود راه‌اندازی می‌شود. با مراجعه به مشخصه type شیء KeyEvent می‌توانید دو رویداد کلیدی را از هم تشخیص دهید.

مقدار برگشتی onKeyEvent lambda نشان می‌دهد که آیا رویداد کلیدی مدیریت می‌شود یا نه. اگر برنامه‌تان رویداد کلیدی را مدیریت می‌کند، 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 را رها کند. اگر کاربر کلید اصلاح‌گری را فشار دهد، مثلاً کلید تبدیل، برنامه تابع را فراخوانی نمی‌کند.

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

کلیدهای فاصله و ورود نیز رویدادهای کلیک را راه‌اندازی می‌کنند. برای مثال، کاربران می‌توانند با مدیریت رویدادهای کلیک به‌صورت زیر، بازپخش رسانه را با کلید فاصله یا کلید ورود تغییر دهند (پخش یا مکث کنند):

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

اصلاح‌کننده clickable رویدادهای کلیدی را رهگیری می‌کند و وقتی کلید کلید فاصله یا ورود فشار داده می‌شود، فراخوانی برگشتی onClick() را فراخوانی می‌کند. به‌همین دلیل است که تابع togglePausePlay() با فشار دادن کلید فاصله یا ورود در قطعه کد فراخوانی می‌شود.

رویدادهای کلیدی مصرف‌نشده

رویدادهای کلیدی مصرف‌نشده از مؤلفه‌ای که رویداد در آن رخ داده است به مؤلفه بیرونی دربرگیرنده منتشر می‌شوند. در مثال زیر، 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 یکی از این موارد است. قطعه زیر به کاربران امکان می‌دهد با فشار دادن کلید جهش به مؤلفه قابل‌تمرکز بعدی بروند.

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 هربار که کاربران کلید جهش را فشار می‌دهند، یک نویسه جهش اضافه می‌کند، حتی اگر رویداد کلید با اصلاح‌کننده onKeyEvent مدیریت شود. برای جابه‌جا کردن تمرکز صفحه‌کلید بدون افزودن نویسه‌های برگه، رویداد کلید را قبل‌از راه‌اندازی کنش‌های مرتبط با رویداد کلید، مانند قطعه کد، مدیریت کنید. onKeyPreviewEvent() lambda با برگرداندن true، رویداد کلیدی را رهگیری می‌کند.

مؤلفه والد می‌تواند رویداد کلیدی را که در فرزندانش اتفاق می‌افتد رهگیری کند. در قطعه کد زیر، وقتی کاربران کلید S را فشار می‌دهند، تابع previewSKey() فراخوانی می‌شود و تابع 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")
    }
}

وقتی کاربران کلید Tab را فشار می‌دهند، onPreviewKeyEvent() lambda برای مؤلفه Box نیز راه‌اندازی نمی‌شود. ابتدا onPreviewKeyEvent() لامبدا در مؤلفه والد فراخوانی می‌شود، سپس onPreviewKeyEvent() در مؤلفه فرزند فراخوانی می‌شود. بااستفاده از این رفتار می‌توانید میان‌برهای صفحه‌کلید سراسری را پیاده‌سازی کنید.

منابع بیشتر