سازگاری ورودی

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

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

  • پشتیبانی اولیه از صفحه‌کلید، مانند پیمایش صفحه‌کلید با استفاده از کلیدهای جهت‌نما و تب، کلید Enter برای تأیید ورود متن و کلید Space برای پخش/مکث در برنامه‌های رسانه‌ای را اضافه و آزمایش کنید.
  • در صورت لزوم، میانبرهای صفحه‌کلید استاندارد را اضافه کنید، برای مثال ctrl + z برای لغو عملیات، ctrl + s برای ذخیره عملیات.
  • تعاملات اولیه ماوس را به روش کلیک راست برای منوی زمینه، تغییر آیکون هنگام نگه داشتن ماوس روی صفحه و رویدادهای اسکرول چرخان/ترک‌پد ماوس در نماهای سفارشی آزمایش کنید.
  • دستگاه‌های ورودی مخصوص برنامه مانند قلم برای برنامه‌های طراحی، دسته‌های بازی برای بازی‌ها و دسته‌های MIDI برای برنامه‌های موسیقی را آزمایش کنید.
  • پشتیبانی از ورودی‌های پیشرفته را در نظر بگیرید که می‌تواند برنامه را در محیط‌های دسکتاپ متمایز کند: صفحه لمسی به عنوان یک فیدر متقاطع برای برنامه‌های دی‌جی، ضبط ماوس برای بازی‌ها و میانبرهای صفحه کلید گسترده برای کاربران حرفه‌ای.

کیبورد

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

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

برای بسیاری از برنامه‌ها، پیمایش با کلید جهت‌نما و تب تنها چیزی است که مورد نیاز است و عمدتاً توسط چارچوب اندروید به طور خودکار مدیریت می‌شود. به عنوان مثال، نمای یک Button به طور پیش‌فرض قابل فوکوس است و پیمایش صفحه کلید معمولاً باید بدون هیچ کد اضافی کار کند. برای فعال کردن پیمایش صفحه کلید برای نماهایی که به طور پیش‌فرض قابل فوکوس نیستند، توسعه‌دهندگان باید آنها را به عنوان قابل فوکوس علامت‌گذاری کنند. این کار را می‌توان به صورت برنامه‌نویسی یا در XML، به شرح زیر انجام داد. برای اطلاعات بیشتر به مستندات Focus Handling مراجعه کنید.

yourView.isFocusable = true

به عنوان یک روش جایگزین، می‌توانید ویژگی focusable را در فایل layout خود تنظیم کنید:

android:focusable="true"

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

// Arrow keys
yourView.nextFocusLeftId = R.id.view_to_left
yourView.nextFocusRightId = R.id.view_to_right
yourView.nextFocusTopId = R.id.view_above
yourView.nextFocusBottomId = R.id.view_below

// Tab key
yourView.nextFocusForwardId = R.id.next_view

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

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

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

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

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

اکثر برنامه‌ها رویداد onKeyUp را نادیده می‌گیرند و رفتار مورد انتظار برای هر کد کلید دریافتی را به صورت زیر اضافه می‌کنند.

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 را جستجو کنند.

بسته به نیازهای یک برنامه، معمولاً override کردن onKeyUp برای کل Activity رفتار مورد نیاز را فراهم می‌کند. در صورت نیاز، می‌توان به جای آن یک onKeyListener به یک view خاص اضافه کرد. برای مثال، یک برنامه ممکن است فقط به کلید Enter در EditText خاص گوش دهد و نه Activity، تا بتواند قابلیت ارسال را فقط زمانی که کاربر در حال تایپ در یک کادر چت است، پیاده‌سازی کند.

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

میانبرها

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

برخی از کلیدهای میانبر رایج شامل ذخیره ( ctrl + s )، لغو ( ctrl + z ) و انجام مجدد ( ctrl + shift + z ) هستند. برای مثالی از برخی کلیدهای میانبر پیشرفته‌تر، به فهرست کلیدهای میانبر VLC Media Player مراجعه کنید.

میانبرها را می‌توان با استفاده از dispatchKeyShortcutEvent پیاده‌سازی کرد. این تابع تمام ترکیبات متا-کلید ( alt ، ctrl و shift ) را برای یک کد کلید مشخص، رهگیری می‌کند. برای بررسی یک متا-کلید خاص، KeyEvent.isCtrlPressed() ، KeyEvent.isShiftPressed() ، KeyEvent.isAltPressed() یا KeyEvent.hasModifiers() استفاده کنید.

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

override fun dispatchKeyShortcutEvent(event: KeyEvent): Boolean {
  return when (event.keyCode) {
    KeyEvent.KEYCODE_O -> {
      openFile() // Ctrl+O, Shift+O, Alt+O
      true
    }
    KeyEvent.KEYCODE_Z-> {
      if (event.isCtrlPressed) {
        if (event.isShiftPressed) {
          redoLastAction() // Ctrl+Shift+Z pressed
          true
        } else {
          undoLastAction() // Ctrl+Z pressed
          true
        }
      }
    }
    else -> {
      return super.dispatchKeyShortcutEvent(event)
    }
  }
}

همچنین می‌توانید با بررسی KeyEvent.isCtrlPressed() ، KeyEvent.isShiftPressed() یا KeyEvent.isAltPressed() به همان روش، میانبرها را در onKeyUp پیاده‌سازی کنید. اگر رفتار متا بیشتر یک تغییر در رفتار برنامه باشد تا یک میانبر، حفظ این امر می‌تواند آسان‌تر باشد. برای مثال، وقتی w به معنای "قدم زدن به جلو" است و shift+w به معنای "دویدن به جلو" است.

override fun onKeyUp(keyCode: Int, event: KeyEvent): Boolean {
  return when(keyCode) {
    KeyEvent.KEYCODE_W-> {
      if (event.isShiftPressed) {
        if (event.isCtrlPressed) {
          flyForward() // Ctrl+Shift+W pressed
          true
        } else {
          runForward() // Shift+W pressed
          true
        }
      } else {
        walkForward() // W pressed
      }
    }
    else -> super.onKeyUp(keyCode, event)
  }
}

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

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

کلیک راست

هر عملی که باعث می‌شود یک برنامه منوی زمینه را نشان دهد، مانند لمس طولانی روی یک آیتم لیست، باید به رویدادهای کلیک راست نیز واکنش نشان دهد. برای مدیریت رویدادهای کلیک راست، برنامه‌ها باید یک View.OnContextClickListener ثبت کنند. برای جزئیات بیشتر در مورد ساخت منوی زمینه، به مستندات منوی زمینه اندروید مراجعه کنید.

yourView.setOnContextClickListener { view ->
  showContextMenu()
  true
}

نکته: هر ویو (view) که با استفاده از Activity.registerForContextMenu() برای یک منوی زمینه ثبت شده باشد، باید به طور خودکار با هر دو حالت فشار طولانی و کلیک راست کار کند، بدون اینکه نیازی به ثبت یک شنونده کلیک زمینه باشد.

شناور

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

  • با تغییر آیکون اشاره‌گر ماوس، به کاربران نشان می‌دهد که آیا یک عنصر رفتار تعاملی دارد، مانند قابل کلیک یا قابل ویرایش بودن.
  • افزودن بازخورد بصری به موارد موجود در یک لیست یا شبکه بزرگ هنگامی که نشانگر ماوس روی آنها قرار دارد
// Change the icon to a "hand" pointer on hover,
// Highlight the view by changing the background.
yourView.setOnHoverListener { view, _ ->
  addVisualHighlighting(true)
  view.pointerIcon =
    PointerIcon.getSystemIcon(applicationContext,
    PointerIcon.TYPE_HAND)
  false // listener did not consume the event.
}

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

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

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

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

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

  • برای مدیریت فایل‌ها از برنامه ChromeOS Files، به دنبال نوع MIME application/x-arc-uri-list باشید.
  • به یاد داشته باشید که برای دسترسی به مواردی که از خارج از برنامه کشیده می‌شوند، با استفاده از requestDragAndDropPermissions درخواست مجوز کنید.
  • یک آیتم برای اینکه بتواند به برنامه‌های دیگر کشیده شود، باید دارای پرچم View.DRAG_FLAG_GLOBAL باشد.

پشتیبانی از چند انتخاب

اگر برنامه شما شامل لیست‌ها یا شبکه‌ها است، در نظر بگیرید که آیا کاربران شما از پشتیبانی از چند انتخاب بهره‌مند می‌شوند یا خیر. یک تجربه چند انتخاب با کیفیت بالا با ماوس و ترک‌پد اغلب شامل ویژگی‌هایی مانند انتخاب باند است. پیاده‌سازی این امر به تنهایی می‌تواند چالش برانگیز باشد، اما می‌توانید از کتابخانه Recyclerview Selection استفاده کنید.

نمونه‌ای از انتخاب چندگانه باند با ماوس و اشاره‌گر.

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

برنامه‌هایی که مدیریت پیشرفته ورودی ماوس و تاچ‌پد را انجام می‌دهند، باید از مستندات اندروید برای View.onGenericMotionEvent() پیروی کنند و MotionEvent.getSource() برای تمایز بین SOURCE_MOUSE و SOURCE_TOUCHSCREEN استفاده کنند.

MotionEvent را برای پیاده‌سازی رفتار مورد نیاز بررسی کنید:

  • حرکت، رویدادهای ACTION_HOVER_MOVE را ایجاد می‌کند.
  • دکمه‌ها رویدادهای ACTION_BUTTON_PRESS و ACTION_BUTTON_RELEASE را ایجاد می‌کنند. همچنین می‌توانید وضعیت فعلی تمام دکمه‌های ماوس/ترک‌پد را با استفاده getButtonState() بررسی کنید.
  • پیمایش چرخ ماوس رویدادهای ACTION_SCROLL را ایجاد می‌کند

قلم

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

یک رویداد قلم به عنوان یک رویداد صفحه لمسی با استفاده از View.onTouchEvent() یا View.onGenericMotionEvent() گزارش می‌شود و شامل MotionEvent.getSource() از نوع SOURCE_STYLUS است. MotionEvent همچنین شامل داده‌های اضافی خواهد بود:

نکات تاریخی

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

  • MotionEvent.getHistoricalX()
  • MotionEvent.getHistoricalY()
  • MotionEvent.getHistoricalPressure()
  • MotionEvent.getHistoricalAxisValue()

رد نخل

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

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

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

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

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

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

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

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

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

  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 را فشار دهید.

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

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

دسته‌های بازی

کروم‌بوک‌ها تا چهار دسته بازی را پشتیبانی می‌کنند. توسعه‌دهندگان باید از APIهای استاندارد دسته‌های بازی اندروید برای مدیریت آنها استفاده کنند.

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

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

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

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

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