سازگاری ورودی در صفحه نمایش های بزرگ

در دستگاه‌های با صفحه نمایش بزرگ، کاربران اغلب با استفاده از صفحه کلید، ماوس، ترک‌پد، قلم یا دسته بازی با برنامه‌ها تعامل دارند. برای فعال کردن برنامه خود برای پذیرش ورودی از دستگاه‌های خارجی، موارد زیر را انجام دهید:

  • پشتیبانی از صفحه‌کلید پایه ، مانند Ctrl+Z برای لغو عملیات، Ctrl+C برای کپی و Ctrl+S برای ذخیره را آزمایش کنید. برای مشاهده فهرستی از میانبرهای پیش‌فرض صفحه‌کلید، به بخش «مدیریت عملیات صفحه‌کلید» مراجعه کنید.
  • پشتیبانی از صفحه‌کلید پیشرفته ، مثلاً پیمایش صفحه‌کلید با کلید Tab و جهت‌نما، تأیید ورود متن با کلید Enter و پخش و مکث با کلید Space در برنامه‌های رسانه‌ای را آزمایش کنید.
  • تعاملات اولیه ماوس، از جمله کلیک راست برای منوی زمینه، تغییر آیکون هنگام حرکت ماوس روی صفحه، و رویدادهای اسکرول ماوس یا ترک‌پد را روی اجزای سفارشی آزمایش کنید .
  • دستگاه‌های ورودی مخصوص برنامه مانند قلم، دسته‌های بازی و دسته‌های MIDI برنامه موسیقی را آزمایش کنید .
  • پشتیبانی از ورودی‌های پیشرفته را در نظر بگیرید که می‌تواند برنامه را در محیط‌های دسکتاپ متمایز کند، برای مثال، صفحه لمسی به عنوان یک فیدکننده متقاطع برای برنامه‌های دی‌جی، ضبط ماوس برای بازی‌ها و میانبرهای صفحه کلید برای کاربرانی که بیشتر با صفحه کلید سروکار دارند.

کیبورد

نحوه‌ی پاسخ برنامه‌ی شما به ورودی صفحه‌کلید، به تجربه‌ی کاربری در صفحه‌ی نمایش بزرگ کمک می‌کند. سه نوع ورودی صفحه‌کلید وجود دارد: ناوبری ، کلیدهای فشرده‌شده و میانبرها .

ناوبری صفحه کلید به ندرت در برنامه‌های لمسی پیاده‌سازی می‌شود، اما کاربران وقتی از یک برنامه استفاده می‌کنند و دستشان روی صفحه کلید است، انتظار آن را دارند. ناوبری صفحه کلید می‌تواند در تلفن‌ها، تبلت‌ها، دستگاه‌های تاشو و دستگاه‌های رومیزی برای کاربرانی که نیازهای دسترسی دارند، ضروری باشد.

برای بسیاری از برنامه‌ها، پیمایش با کلید جهت‌نما و کلید Tab به طور خودکار توسط چارچوب اندروید مدیریت می‌شود. برای مثال، برخی از composableها به طور پیش‌فرض قابل فوکوس هستند، مانند یک Button یا یک composable با اصلاح‌کننده clickable ؛ پیمایش با صفحه‌کلید معمولاً باید بدون هیچ کد اضافی کار کند. برای فعال کردن پیمایش با صفحه‌کلید برای composableهای سفارشی که به طور پیش‌فرض قابل فوکوس نیستند، اصلاح‌کننده focusable را اضافه کنید:

var color by remember { mutableStateOf(Green) }
Box(
    Modifier
        .background(color)
        .onFocusChanged { color = if (it.isFocused) Blue else Green }
        .focusable()
) {
    Text("Focusable 1")
}

برای اطلاعات بیشتر، به «قابل‌توجه کردن یک عنصر ترکیبی» مراجعه کنید.

وقتی فوکوس فعال می‌شود، چارچوب اندروید بر اساس موقعیت تمام اجزای قابل فوکوس، یک نگاشت ناوبری ایجاد می‌کند. این معمولاً طبق انتظار کار می‌کند و نیازی به توسعه بیشتر نیست.

با این حال، Compose همیشه مورد بعدی صحیح برای پیمایش تب‌بندی شده را برای composableهای پیچیده مانند تب‌ها و لیست‌ها تعیین نمی‌کند، به عنوان مثال، وقتی یکی از composableها یک پیمایش افقی است که به طور کامل قابل مشاهده نیست.

برای کنترل رفتار focus، اصلاحگر focusGroup را به کامپوننت والد مجموعه‌ای از کامپوننت‌ها اضافه کنید. Focus به گروه مورد نظر منتقل می‌شود، سپس قبل از رفتن به کامپوننت focusable بعدی، از میان گروه عبور می‌کند، برای مثال:

Row {
    Column(Modifier.focusGroup()) {
        Button({}) { Text("Row1 Col1") }
        Button({}) { Text("Row2 Col1") }
        Button({}) { Text("Row3 Col1") }
    }
    Column(Modifier.focusGroup()) {
        Button({}) { Text("Row1 Col2") }
        Button({}) { Text("Row2 Col2") }
        Button({}) { Text("Row3 Col2") }
    }
}

برای اطلاعات بیشتر، به «ارائه ناوبری منسجم با گروه‌های کانونی» مراجعه کنید.

دسترسی به هر عنصر رابط کاربری برنامه خود را فقط با استفاده از صفحه کلید آزمایش کنید. عناصر پرکاربرد باید بدون ماوس یا لمس صفحه قابل دسترسی باشند.

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

ضربه‌های کلید

برای ورودی متنی که توسط یک صفحه کلید مجازی روی صفحه ( IME ) مدیریت می‌شود، مانندیک TextField، برنامه‌ها باید بدون هیچ کار توسعه اضافی، همانطور که انتظار می‌رود در دستگاه‌های با صفحه نمایش بزرگ رفتار کنند. برای کلیدهای فشرده شده‌ای که توسط چارچوب قابل پیش‌بینی نیستند، برنامه‌ها باید خودشان این رفتار را مدیریت کنند. این امر به ویژه در مورد برنامه‌هایی با نماهای سفارشی صادق است.

برخی از مثال‌ها عبارتند از برنامه‌های چت که از کلید Enter برای ارسال پیام استفاده می‌کنند، برنامه‌های رسانه‌ای که پخش را با کلید Spacebar شروع و متوقف می‌کنند، و بازی‌هایی که حرکت را با کلیدهای w ، a ، s و d کنترل می‌کنند.

شما می‌توانید با استفاده از اصلاحگر onKeyEvent ، ضربات کلید را به صورت جداگانه مدیریت کنید. این اصلاحگر یک لامبدا را می‌پذیرد که هنگام دریافت یک رویداد کلیدی توسط کامپوننت اصلاح‌شده، فراخوانی می‌شود. ویژگی KeyEvent#type به شما این امکان را می‌دهد که تعیین کنید آیا این رویداد، فشردن کلید ( KeyDown ) یا رها کردن کلید ( KeyUp ) است:

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

به عنوان یک روش جایگزین، می‌توانید تابع فراخوانی onKeyUp() را بازنویسی کرده و رفتار مورد انتظار برای هر کد کلید دریافتی را اضافه کنید:

kotlin override fun onKeyUp(keyCode: Int, event: KeyEvent): Boolean { return when (keyCode) { KeyEvent.KEYCODE_ENTER -> { sendChatMessage() true } KeyEvent.KEYCODE_SPACE -> { playOrPauseMedia() true } else -> super.onKeyUp(keyCode, event) } }

رویداد onKeyUp زمانی رخ می‌دهد که یک کلید رها شود. استفاده از فراخوانی، مانع از آن می‌شود که برنامه‌ها در صورت نگه داشتن یا رها کردن آهسته یک کلید، نیاز به پردازش چندین رویداد onKeyDown داشته باشند. بازی‌ها و برنامه‌هایی که نیاز به تشخیص لحظه فشرده شدن یک کلید یا اینکه آیا کاربر کلیدی را نگه داشته است یا خیر، می‌توانند به رویداد onKeyDown گوش دهند و خودشان رویدادهای مکرر onKeyDown مدیریت کنند.

برای اطلاعات بیشتر، به مدیریت عملکردهای صفحه کلید مراجعه کنید.

میانبرها

هنگام استفاده از صفحه‌کلید سخت‌افزاری، انتظار می‌رود از کلیدهای میانبر رایج مانند Ctrl ، Alt ، Shift و Meta استفاده شود. اگر برنامه‌ای کلیدهای میانبر نداشته باشد، این تجربه می‌تواند برای کاربران خسته‌کننده باشد. کاربران حرفه‌ای همچنین از کلیدهای میانبر برای کارهای خاص برنامه که اغلب استفاده می‌شوند، قدردانی می‌کنند. کلیدهای میانبر، استفاده از یک برنامه را آسان‌تر می‌کنند و آن را از برنامه‌هایی که کلید میانبر ندارند، متمایز می‌کنند.

برخی از کلیدهای میانبر رایج عبارتند از Ctrl+S (ذخیره)، Ctrl+Z (لغو عملیات) و Ctrl+Shift+Z (انجام مجدد عملیات). برای مشاهده فهرستی از کلیدهای میانبر پیش‌فرض، به بخش «مدیریت عملیات صفحه‌کلید» مراجعه کنید.

یک شیء KeyEvent دارای ویژگی‌های زیر است که نشان می‌دهد آیا کلیدهای اصلاح‌کننده فشرده شده‌اند یا خیر:

برای مثال:

Box(
    Modifier.onKeyEvent {
        if (it.isAltPressed && it.key == Key.A) {
            println("Alt + A is pressed")
            true
        } else {
            false
        }
    }
    .focusable()
)

برای اطلاعات بیشتر، به موارد زیر مراجعه کنید:

قلم

بسیاری از دستگاه‌های صفحه بزرگ با قلم عرضه می‌شوند. برنامه‌های اندروید از قلم‌ها به عنوان ورودی صفحه لمسی استفاده می‌کنند. برخی از دستگاه‌ها ممکن است دارای میز طراحی USB یا بلوتوث نیز باشند، مانند Wacom Intuos . برنامه‌های اندروید می‌توانند ورودی بلوتوث را دریافت کنند اما ورودی USB را دریافت نمی‌کنند.

برای دسترسی به اشیاء قلم MotionEvent ، اصلاحگر pointerInteropFilter را به یک سطح ترسیم اضافه کنید. یک کلاس ViewModel با متدی که رویدادهای حرکتی را پردازش می‌کند، پیاده‌سازی کنید؛ متد را به عنوان لامبدا onTouchEvent از اصلاحگر pointerInteropFilter ارسال کنید:

@Composable
@OptIn(ExperimentalComposeUiApi::class)
fun DrawArea(modifier: Modifier = Modifier) {
   Canvas(modifier = modifier
       .clipToBounds()
       .pointerInteropFilter {
           viewModel.processMotionEvent(it)
       }

   ) {
       // Drawing code here.
   }
}

شیء MotionEvent حاوی اطلاعاتی در مورد رویداد است:

نکات تاریخی

اندروید رویدادهای ورودی را دسته بندی کرده و آنها را یک بار در هر فریم ارائه می‌دهد. یک قلم می‌تواند رویدادها را با فرکانس‌های بسیار بالاتری نسبت به صفحه نمایش گزارش دهد. هنگام ایجاد برنامه‌های طراحی، با استفاده از APIهای getHistorical ، رویدادهایی را که ممکن است در گذشته نزدیک باشند، بررسی کنید:

رد نخل

وقتی کاربران با استفاده از قلم نوری (stylus) نقاشی می‌کشند، می‌نویسند یا با برنامه شما تعامل می‌کنند، گاهی اوقات صفحه را با کف دست خود لمس می‌کنند. رویداد لمس (که روی ACTION_DOWN یا ACTION_POINTER_DOWN تنظیم شده است) می‌تواند قبل از اینکه سیستم لمس غیرعمدی کف دست را تشخیص داده و نادیده بگیرد، به برنامه شما گزارش شود.

اندروید با ارسال یک MotionEvent ، رویدادهای لمس کف دست را لغو می‌کند. اگر برنامه شما ACTION_CANCEL را دریافت کرد، ژست حرکتی را لغو کنید. اگر برنامه شما ACTION_POINTER_UP را دریافت کرد، بررسی کنید که آیا FLAG_CANCELED تنظیم شده است یا خیر. در این صورت، ژست حرکتی را لغو کنید.

فقط FLAG_CANCELED را بررسی نکنید. در اندروید ۱۳ (سطح API ۳۳) و بالاتر، سیستم FLAG_CANCELED برای رویدادهای ACTION_CANCEL تنظیم می‌کند، اما سیستم در نسخه‌های پایین‌تر اندروید این پرچم را تنظیم نمی‌کند.

اندروید ۱۲

در اندروید ۱۲ (سطح API 32) و پایین‌تر، تشخیص رد کف دست فقط برای رویدادهای لمس تک اشاره‌گر امکان‌پذیر است. اگر لمس کف دست تنها اشاره‌گر باشد، سیستم با تنظیم ACTION_CANCEL روی شیء رویداد حرکت، رویداد را لغو می‌کند. اگر سایر اشاره‌گرها غیرفعال باشند، سیستم ACTION_POINTER_UP را تنظیم می‌کند که برای تشخیص رد کف دست کافی نیست.

اندروید ۱۳

در اندروید ۱۳ (سطح API ۳۳) و بالاتر، اگر لمس کف دست تنها اشاره‌گر باشد، سیستم با تنظیم ACTION_CANCEL و FLAG_CANCELED روی شیء رویداد حرکت، رویداد را لغو می‌کند. اگر سایر اشاره‌گرها غیرفعال باشند، سیستم ACTION_POINTER_UP و FLAG_CANCELED را تنظیم می‌کند.

هر زمان که برنامه شما یک رویداد حرکتی با ACTION_POINTER_UP دریافت می‌کند، FLAG_CANCELED را بررسی کنید تا مشخص شود که آیا این رویداد نشان‌دهنده‌ی عدم پذیرش کف دست (یا لغو رویداد دیگری) است یا خیر.

اپلیکیشن‌های یادداشت‌برداری

ChromeOS قصد ویژه‌ای دارد که برنامه‌های یادداشت‌برداری ثبت‌شده را برای کاربران نمایش دهد. برای ثبت یک برنامه به عنوان برنامه یادداشت‌برداری، موارد زیر را به مانیفست برنامه خود اضافه کنید:

<intent-filter>
    <action android:name="org.chromium.arc.intent.action.CREATE_NOTE" />
    <category android:name="android.intent.category.DEFAULT" />
</intent-filter>

وقتی یک برنامه در سیستم ثبت می‌شود، کاربر می‌تواند آن را به عنوان برنامه یادداشت‌برداری پیش‌فرض انتخاب کند. وقتی یک یادداشت جدید درخواست می‌شود، برنامه باید یک یادداشت خالی آماده برای ورودی قلم ایجاد کند. وقتی کاربر می‌خواهد روی یک تصویر (مانند یک اسکرین‌شات یا تصویر دانلود شده) حاشیه‌نویسی کند، برنامه با ClipData حاوی یک یا چند مورد با content:// URI اجرا می‌شود. برنامه باید یادداشتی ایجاد کند که از اولین تصویر پیوست شده به عنوان تصویر پس‌زمینه استفاده کند و وارد حالتی شود که کاربر بتواند با قلم روی صفحه نقاشی کند.

آزمایش اهداف یادداشت‌برداری بدون قلم

برای آزمایش اینکه آیا یک برنامه بدون قلم فعال به درستی به اهداف یادداشت‌برداری پاسخ می‌دهد یا خیر، از روش زیر برای نمایش گزینه‌های یادداشت‌برداری در ChromeOS استفاده کنید:

  1. به حالت توسعه (dev mode) بروید و دستگاه را قابل نوشتن کنید
  2. برای باز کردن ترمینال ، Ctrl+Alt+F2 را فشار دهید.
  3. دستور sudo vi /etc/chrome_dev.conf را اجرا کنید.
  4. برای ویرایش و اضافه کردن --ash-enable-palette به یک خط جدید در انتهای فایل، i را فشار دهید.
  5. با فشردن Esc و سپس تایپ کردن : ، w ، q و فشردن Enter، ذخیره کنید.
  6. برای بازگشت به رابط کاربری معمولی ChromeOS، کلیدهای Ctrl+Alt+F1 را فشار دهید.
  7. از سیستم خارج شوید، سپس دوباره وارد شوید

اکنون یک منوی قلم باید روی قفسه باشد:

  • روی دکمه قلم روی قفسه ضربه بزنید و یادداشت جدید را انتخاب کنید. این باید یک یادداشت طراحی خالی باز کند.
  • از صفحه نمایش اسکرین‌شات بگیرید. از قفسه، دکمه قلم > ضبط صفحه یا دانلود تصویر را انتخاب کنید. باید گزینه حاشیه‌نویسی تصویر در اعلان وجود داشته باشد. با این کار، برنامه با تصویر آماده حاشیه‌نویسی اجرا می‌شود.

پشتیبانی از ماوس و تاچ‌پد

اکثر برنامه‌ها معمولاً فقط باید سه رویداد متمرکز بر صفحه نمایش بزرگ را مدیریت کنند: کلیک راست ، نگه داشتن ماوس روی صفحه و کشیدن و رها کردن .

کلیک راست

هر عملی که باعث می‌شود یک برنامه منوی زمینه را نشان دهد، مانند لمس و نگه داشتن انگشت روی یک مورد از لیست، باید به رویدادهای کلیک راست نیز واکنش نشان دهد.

برای مدیریت رویدادهای کلیک راست، برنامه‌ها باید یک View.OnContextClickListener ثبت کنند:

Box(modifier = Modifier.fillMaxSize()) {
    AndroidView(
        modifier = Modifier.fillMaxSize(),
        factory = { context ->
            val rootView = FrameLayout(context)
            val onContextClickListener =
                View.OnContextClickListener { view ->
                    showContextMenu()
                    true
                }
            rootView.setOnContextClickListener(onContextClickListener)
            rootView
        },
    )
}

برای جزئیات بیشتر در مورد ساخت منوهای زمینه، به ایجاد یک منوی زمینه مراجعه کنید.

شناور

شما می‌توانید با مدیریت رویدادهای شناور، طرح‌بندی برنامه خود را مرتب و استفاده از آن را آسان‌تر کنید. این امر به ویژه در مورد برنامه‌های سفارشی صادق است.اجزا:

دو نمونه رایج از این موارد عبارتند از:

  • با تغییر آیکون اشاره‌گر ماوس، به کاربران نشان می‌دهد که آیا یک عنصر رفتار تعاملی دارد، مانند قابل کلیک بودن یا قابل ویرایش بودن.
  • افزودن بازخورد بصری به موارد موجود در یک لیست یا شبکه بزرگ هنگامی که نشانگر ماوس روی آنها قرار دارد

بکشید و رها کنید

در یک محیط چند پنجره‌ای، کاربران انتظار دارند که بتوانند آیتم‌ها را بین برنامه‌ها بکشند و رها کنند. این موضوع در مورد دستگاه‌های رومیزی و همچنین تبلت‌ها، تلفن‌ها و دستگاه‌های تاشو در حالت تقسیم صفحه صادق است.

در نظر بگیرید که آیا کاربران احتمالاً مواردی را به برنامه شما می‌کشند یا خیر. به عنوان مثال، ویرایشگرهای عکس باید انتظار دریافت عکس، پخش‌کننده‌های صدا باید انتظار دریافت فایل‌های صوتی و برنامه‌های نقاشی باید انتظار دریافت عکس را داشته باشند.

برای افزودن پشتیبانی از کشیدن و رها کردن، بهبکشید و رها کنیدو نگاهی به پست وبلاگ «اندروید روی ChromeOS - پیاده‌سازی کشیدن و رها کردن» بیندازید.

ملاحظات ویژه برای ChromeOS

  • به یاد داشته باشید که برای دسترسی به مواردی که از خارج از برنامه کشیده می‌شوند، با استفاده از requestDragAndDropPermissions() درخواست مجوز کنید.
  • یک آیتم برای اینکه بتواند به برنامه‌های دیگر کشیده شود، باید دارای پرچم View.DRAG_FLAG_GLOBAL باشد.

    به شروع رویداد کشیدن مراجعه کنید

پشتیبانی از اشاره‌گر پیشرفته

برنامه‌هایی که مدیریت پیشرفته ورودی ماوس و تاچ‌پد را انجام می‌دهند، باید یکاصلاح‌کننده‌ی pointerInput برای دریافت PointerEvent :

@Composable
private fun LogPointerEvents(filter: PointerEventType? = null) {
    var log by remember { mutableStateOf("") }
    Column {
        Text(log)
        Box(
            Modifier
                .size(100.dp)
                .background(Color.Red)
                .pointerInput(filter) {
                    awaitPointerEventScope {
                        while (true) {
                            val event = awaitPointerEvent()
                            // handle pointer event
                            if (filter == null || event.type == filter) {
                                log = "${event.type}, ${event.changes.first().position}"
                            }
                        }
                    }
                }
        )
    }
}

شیء PointerEvent را بررسی کنید تا موارد زیر را مشخص کنید:

کنترل‌کننده‌های بازی

برخی از دستگاه‌های اندروید با صفحه نمایش بزرگ، تا چهار دسته بازی را پشتیبانی می‌کنند. برای مدیریت دسته‌های بازی از APIهای استاندارد دسته‌های بازی اندروید استفاده کنید (به بخش پشتیبانی از دسته‌های بازی مراجعه کنید).

دکمه‌های کنترلر بازی با پیروی از یک نگاشت مشترک، به مقادیر مشترک نگاشت می‌شوند. اما همه تولیدکنندگان کنترلر بازی از قراردادهای نگاشت یکسانی پیروی نمی‌کنند. اگر به کاربران اجازه دهید نگاشت‌های کنترلر محبوب مختلفی را انتخاب کنند، می‌توانید تجربه بسیار بهتری ارائه دهید. برای اطلاعات بیشتر به بخش پردازش فشردن دکمه‌های گیم‌پد مراجعه کنید.

حالت ترجمه ورودی

سیستم عامل کروم به طور پیش‌فرض حالت ترجمه ورودی را فعال می‌کند. برای اکثر برنامه‌های اندروید، این حالت به برنامه‌ها کمک می‌کند تا همانطور که در محیط دسکتاپ انتظار می‌رود، کار کنند. برخی از نمونه‌ها شامل فعال کردن خودکار پیمایش دو انگشتی روی تاچ‌پد، پیمایش چرخ ماوس و نگاشت مختصات خام نمایشگر به مختصات پنجره است. به طور کلی، توسعه‌دهندگان برنامه نیازی به پیاده‌سازی هیچ یک از این رفتارها ندارند.

اگر یک برنامه رفتار ورودی سفارشی را پیاده‌سازی می‌کند، برای مثال یک عمل نیشگون گرفتن دو انگشتی سفارشی از صفحه لمسی تعریف می‌کند، یا این ترجمه‌های ورودی رویدادهای ورودی مورد انتظار برنامه را ارائه نمی‌دهند، می‌توانید حالت ترجمه ورودی را با اضافه کردن برچسب زیر به مانیفست اندروید غیرفعال کنید:

<uses-feature
    android:name="android.hardware.type.pc"
    android:required="false" />

منابع اضافی