نمای کلی رویدادهای ورودی

امتحان کردن روش Compose
Jetpack Compose جعبه‌ابزار واسط کاربر توصیه‌شده برای Android است. با نحوه استفاده از لمس و ورودی در Compose آشنا شوید.

در Android، بیش‌از یک روش برای رهگیری رویدادهای ناشی از تعامل کاربر با برنامه شما وجود دارد. هنگام درنظر گرفتن رویدادها در میانای کاربر، رویکرد این است که رویدادها را از شیء «نمای» خاصی که کاربر با آن تعامل دارد ضبط کنید. کلاس View ابزار لازم برای انجام این کار را فراهم می‌کند.

در کلاس‌های مختلف View که برای ترکیب‌بندی چیدمانتان استفاده خواهید کرد، ممکن است متوجه چندین روش فراخوانی برگشتی عمومی شوید که برای رویدادهای میانای کاربر مفید به‌نظر می‌رسند. وقتی کنش مربوطه در آن شیء رخ می‌دهد، چارچوب Android این روش‌ها را فراخوانی می‌کند. برای مثال، وقتی «نمایی» (مثل «دکمه») لمس می‌شود، روش onTouchEvent() در آن شیء فراخوانی می‌شود. بااین‌حال، برای رهگیری این مورد، باید کلاس را گسترش دهید و روش را لغو کنید. بااین‌حال، گسترش دادن هر شیء View برای رسیدگی به چنین رویدادی عملی نخواهد بود. به‌همین دلیل است که کلاس View همچنین شامل مجموعه‌ای از میاناهای تودرتو با callback است که می‌توانید بسیار راحت‌تر تعریف کنید. این میاناهای موسوم به شنوندگان رویداد، بلیت شما برای ضبط تعامل کاربر با واسط کاربری شما هستند.

درحالی‌که معمولاً از شنوندگان رویداد برای گوش دادن به تعامل کاربر استفاده می‌کنید، ممکن است زمانی برسد که بخواهید کلاس View را گسترش دهید تا یک مؤلفه سفارشی بسازید. شاید بخواهید کلاس Button را گسترش دهید تا چیزی فانتزی‌تر بسازید. دراین‌صورت، می‌توانید بااستفاده از گرداننده‌های رویداد کلاس، رفتارهای پیش‌فرض رویداد را برای کلاستان تعریف کنید.

شنوندگان رویداد

شنونده رویداد رابطی در کلاس View است که حاوی یک روش فراخوانی است. وقتی «نمایی» که شنونده در آن ثبت شده است با تعامل کاربر با مورد در میانای کاربر فعال شود، این روش‌ها توسط چارچوب Android فراخوانده می‌شوند.

روش‌های فراخوانی زیر در میانجی‌های شنونده رویداد گنجانده شده‌اند:

onClick()
از View.OnClickListener. این زمانی فراخوانی می‌شود که کاربر یا مورد را لمس کند (وقتی در حالت لمسی است)، یا با کلیدهای پیمایش یا گوی پیمایش روی مورد تمرکز کند و کلید «ورود» مناسب را فشار دهد یا گوی پیمایش را فشار دهد.
onLongClick()
از View.OnLongClickListener. این زمانی فراخوانی می‌شود که کاربر یا مورد را لمس می‌کند و نگه می‌دارد (وقتی در حالت لمسی است)، یا با کلیدهای پیمایش یا گوی پیمایش روی مورد تمرکز می‌کند و کلید «ورود» مناسب را فشار می‌دهد و نگه می‌دارد یا گوی پیمایش را فشار می‌دهد و نگه می‌دارد (به‌مدت یک ثانیه).
onFocusChange()
از View.OnFocusChangeListener. این زمانی فراخوانی می‌شود که کاربر بااستفاده از کلیدهای پیمایش یا گوی پیمایش به آن مورد پیمایش می‌کند یا از آن دور می‌شود.
onKey()
از View.OnKeyListener. این زمانی فراخوانی می‌شود که کاربر روی مورد متمرکز شده است و کلید سخت‌افزاری را در دستگاه فشار می‌دهد یا رها می‌کند.
onTouch()
از View.OnTouchListener. این زمانی فراخوانی می‌شود که کاربر کنشی را انجام دهد که به‌عنوان رویداد لمسی واجدشرایط باشد، ازجمله فشار دادن، رها کردن، یا هر اشاره حرکتی روی صفحه (در محدوده مورد).
onCreateContextMenu()
از View.OnCreateContextMenuListener. این مورد زمانی فراخوانی می‌شود که «منوِ بافت» درحال ساخته شدن باشد (درنتیجه «کلیک طولانی» ممتد). بحث درباره منوهای زمینه‌ای را در راهنمای توسعه‌دهندگان منوها ببینید.

این روش‌ها تنها ساکنان میانای مربوطه خود هستند. برای تعریف یکی از این روش‌ها و مدیریت رویدادهایتان، میانای تودرتو را در «فعالیت» خود پیاده‌سازی کنید یا آن را به‌عنوان کلاس ناشناس تعریف کنید. سپس، نمونه‌ای از پیاده‌سازی‌تان را به روش View.set...Listener() مربوطه ارسال کنید. (برای مثال، setOnClickListener() را فراخوانی کنید و پیاده‌سازی OnClickListener را به آن ارسال کنید.)

مثال زیر نحوه ثبت شنونده کلیک برای دکمه را نشان می‌دهد.

کاتلین

protected void onCreate(savedValues: Bundle) {
    ...
    val button: Button = findViewById(R.id.corky)
    // Register the onClick listener with the implementation above
    button.setOnClickListener { view ->
        // do something when the button is clicked
    }
    ...
}

جاوا

// Create an anonymous implementation of OnClickListener
private OnClickListener corkyListener = new OnClickListener() {
    public void onClick(View v) {
      // do something when the button is clicked
    }
};

protected void onCreate(Bundle savedValues) {
    ...
    // Capture our button from layout
    Button button = (Button)findViewById(R.id.corky);
    // Register the onClick listener with the implementation above
    button.setOnClickListener(corkyListener);
    ...
}

همچنین ممکن است پیاده‌سازی OnClickListener به‌عنوان بخشی از Activity برایتان راحت‌تر باشد. این کار از بارگذاری کلاس اضافی و تخصیص شیء جلوگیری می‌کند. برای مثال:

کاتلین

class ExampleActivity : Activity(), OnClickListener {
  
    protected fun onCreate(savedValues: Bundle) {
        val button: Button = findViewById(R.id.corky)
        button.setOnClickListener(this)
    }

    // Implement the OnClickListener callback
    fun onClick(v: View) {
        // do something when the button is clicked
    }
}

جاوا

public class ExampleActivity extends Activity implements OnClickListener {
    protected void onCreate(Bundle savedValues) {
        ...
        Button button = (Button)findViewById(R.id.corky);
        button.setOnClickListener(this);
    }

    // Implement the OnClickListener callback
    public void onClick(View v) {
      // do something when the button is clicked
    }
    ...
}

توجه داشته باشید که فراخوانی برگشتی onClick() در مثال بالا مقدار برگشتی ندارد، اما برخی‌از روش‌های شنونده رویداد دیگر باید مقدار بولی برگردانند. دلیل آن به رویداد بستگی دارد. برای آن تعداد کمی که این کار را می‌کنند، دلیلش این است:

  • onLongClick() - این مقدار بولی را برمی‌گرداند تا نشان دهد آیا رویداد را مصرف کرده‌اید و نباید بیشتر ادامه یابد. یعنی true را برگردانید تا نشان دهید که رویداد را مدیریت کرده‌اید و باید در اینجا متوقف شود؛ اگر آن را مدیریت نکرده‌اید و/یا رویداد باید به شنوندگان کلیک دیگر ادامه یابد، false را برگردانید.
  • onKey() - این مقدار بولی را برمی‌گرداند تا نشان دهد آیا رویداد را مصرف کرده‌اید و نباید بیشتر ادامه یابد. یعنی اگر رویداد را مدیریت کرده‌اید و باید در اینجا متوقف شود، true را برگردانید؛ اگر آن را مدیریت نکرده‌اید و/یا رویداد باید به شنوندگان on-key دیگر ادامه یابد، false را برگردانید.
  • onTouch() - این مقدار بولی را برمی‌گرداند تا نشان دهد آیا شنونده شما این رویداد را مصرف می‌کند یا نه. نکته مهم این است که این رویداد می‌تواند چندین کنش داشته باشد که به‌دنبال یکدیگر انجام می‌شوند. بنابراین، اگر هنگام دریافت رویداد کنش پایین، false را برگردانید، نشان می‌دهید که رویداد را مصرف نکرده‌اید و همچنین به کنش‌های بعدی از این رویداد علاقه‌مند نیستید. بنابراین، برای هیچ کنش دیگری در رویداد، مثل اشاره انگشت یا رویداد کنش نهایی بالا، فراخوانده نخواهید شد.

به‌خاطر داشته باشید که رویدادهای کلید سخت‌افزاری همیشه به View که درحال‌حاضر در کانون توجه است ارسال می‌شوند. این رویدادها از بالای سلسله‌مراتب View ارسال می‌شوند و سپس به‌سمت پایین حرکت می‌کنند تا به مقصد مناسب برسند. اگر «نمای» شما (یا فرزند «نمای» شما) درحال‌حاضر فوکوس داشته باشد، می‌توانید رویداد را ببینید که ازطریق روش dispatchKeyEvent() منتقل می‌شود. به‌عنوان جایگزینی برای ضبط رویدادهای کلیدی ازطریق «نمای» خود، می‌توانید همه رویدادهای داخل «فعالیت» خود را نیز با onKeyDown() و onKeyUp() دریافت کنید.

همچنین، هنگام فکر کردن درباره ورودی نوشتاری برای برنامه‌تان، به‌خاطر داشته باشید که بسیاری از دستگاه‌ها فقط روش‌های ورودی نرم‌افزاری دارند. این روش‌ها لزوماً کلیدمحور نیستند؛ برخی‌از آن‌ها ممکن است از ورودی گفتاری، دست‌نویس، و غیره استفاده کنند. حتی اگر یک روش ورودی واسطی شبیه صفحه‌کلید ارائه دهد، معمولاً خانواده رویدادهای onKeyDown() را راه‌اندازی نمی‌کند. هرگز نباید واسط کاربری‌ای بسازید که برای کنترل شدن به فشار دادن کلیدهای خاصی نیاز داشته باشد، مگراینکه بخواهید برنامه‌تان را به دستگاه‌های دارای صفحه‌کلید سخت‌افزاری محدود کنید. به‌ویژه، وقتی کاربر کلید بازگشت را فشار می‌دهد، برای اعتبارسنجی ورودی به این روش‌ها تکیه نکنید؛ درعوض، از کنش‌هایی مثل IME_ACTION_DONE استفاده کنید تا به روش ورودی نشان دهید که برنامه‌تان انتظار دارد چگونه واکنش نشان دهد، تا بتواند میانای کاربری خود را به‌شیوه‌ای معنادار تغییر دهد. از پیش‌فرض‌ها درباره نحوه عملکرد روش ورودی نرم‌افزار اجتناب کنید و فقط به آن اعتماد کنید تا نوشتار ازپیش قالب‌بندی‌شده را به برنامه شما ارائه دهد.

توجه: Android ابتدا کنترل‌کننده‌های رویداد را فراخوانی می‌کند و سپس کنترل‌کننده‌های پیش‌فرض مناسب را از تعریف کلاس فراخوانی می‌کند. به این ترتیب، برگرداندن true از این شنوندگان رویداد، انتشار رویداد به دیگر شنوندگان رویداد را متوقف می‌کند و همچنین فراخوانی برگشتی به کنترل‌کننده رویداد پیش‌فرض در «نما» را مسدود می‌کند. بنابراین مطمئن شوید که می‌خواهید رویداد را هنگام برگرداندن true خاتمه دهید.

مدیریت‌کننده‌های رویداد

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

روش‌های دیگری وجود دارد که باید از آن‌ها مطلع باشید، که بخشی از کلاس View نیستند، اما می‌توانند مستقیماً بر نحوه مدیریت رویدادها تأثیر بگذارند. بنابراین، هنگام مدیریت رویدادهای پیچیده‌تر در یک چیدمان، این روش‌های دیگر را درنظر بگیرید:

حالت لمسی

وقتی کاربر با کلیدهای جهت‌دار یا گوی پیمایش در میانای کاربر پیمایش می‌کند، لازم است که تمرکز به موارد کنش‌پذیر (مانند دکمه‌ها) داده شود تا کاربر بتواند ببیند چه چیزی ورودی را می‌پذیرد. اما اگر دستگاه قابلیت لمسی داشته باشد و کاربر با لمس کردن واسط شروع به تعامل با آن کند، دیگر لازم نیست موارد برجسته شوند یا «نمای» خاصی در کانون توجه قرار گیرد. بنابراین، حالتی برای تعامل وجود دارد که «حالت لمسی» نامیده می‌شود.

برای دستگاهی که قابلیت لمس دارد، وقتی کاربر صفحه‌نمایش را لمس می‌کند، دستگاه وارد حالت لمس می‌شود. از این نقطه به بعد، فقط «نماهایی» که isFocusableInTouchMode() برای آن‌ها درست باشد قابل‌تمرکز خواهند بود، مثل ابزاره‌های ویرایش نوشتار. سایر «نماها» که لمس‌کردنی هستند، مثل دکمه‌ها، هنگام لمس شدن فوکوس را دریافت نمی‌کنند؛ آن‌ها صرفاً هنگام فشرده شدن، شنوندگان رویداد کلیک خود را فعال می‌کنند.

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

وضعیت حالت لمسی در سراسر سیستم (همه پنجره‌ها و فعالیت‌ها) حفظ می‌شود. برای پرس‌وجو درباره وضعیت فعلی، می‌توانید isInTouchMode() را فراخوانی کنید تا ببینید آیا دستگاه درحال‌حاضر در حالت لمسی است یا نه.

مدیریت فوکوس

چارچوب، حرکت تمرکز معمول را در پاسخ به ورودی کاربر مدیریت خواهد کرد. این شامل تغییر تمرکز با برداشته شدن یا پنهان شدن «نماها» یا دردسترس قرار گرفتن «نماهای» جدید می‌شود. نماها تمایل خود را برای تمرکز کردن ازطریق روش isFocusable() نشان می‌دهند. برای تغییر اینکه آیا «نما» می‌تواند تمرکز را بگیرد، با setFocusable() تماس بگیرید. وقتی در حالت لمسی هستید، می‌توانید پرس‌وجو کنید که آیا View اجازه تمرکز با isFocusableInTouchMode() را می‌دهد یا نه. می‌توانید این را با setFocusableInTouchMode() تغییر دهید.

در دستگاه‌هایی که Android 9 (سطح میانای برنامه‌سازی کاربردی ۲۸) یا بالاتر را اجرا می‌کنند، فعالیت‌ها تمرکز اولیه را اختصاص نمی‌دهند. درعوض، درصورت تمایل باید به‌طور صریح درخواست تمرکز اولیه کنید.

حرکت تمرکز براساس الگوریتمی است که نزدیک‌ترین همسایه را در جهت مشخص‌شده پیدا می‌کند. در موارد نادر، ممکن است الگوریتم پیش‌فرض با رفتار موردنظر توسعه‌دهنده مطابقت نداشته باشد. در این شرایط، می‌توانید با مشخصه‌های XML زیر در فایل چیدمان، لغوهای صریح ارائه دهید: nextFocusDown، nextFocusLeft، nextFocusRight، و nextFocusUp. یکی از این مشخصه‌ها را به View from که تمرکز از آن خارج می‌شود اضافه کنید. مقدار مشخصه را به‌عنوان شناسه «نمایی» تعریف کنید که باید روی آن تمرکز شود. برای مثال:

<LinearLayout
    android:orientation="vertical"
    ... >
  <Button android:id="@+id/top"
          android:nextFocusUp="@+id/bottom"
          ... />
  <Button android:id="@+id/bottom"
          android:nextFocusDown="@+id/top"
          ... />
</LinearLayout>

معمولاً در این چیدمان عمودی، پیمایش به‌بالا از «دکمه» اول به جایی نمی‌رود، و پیمایش به‌پایین از «دکمه» دوم هم به جایی نمی‌رود. اکنون که دکمه بالایی دکمه پایینی را به‌عنوان nextFocusUp تعریف کرده است (و برعکس)، کانون پیمایش به‌صورت چرخه‌ای از بالا به پایین و از پایین به بالا حرکت خواهد کرد.

اگر می‌خواهید «نمایی» را در میانای کاربری‌تان به‌عنوان قابل‌تمرکز اعلام کنید (درحالی‌که به‌طور سنتی این‌طور نیست)، ویژگی XML android:focusable را به «نما» در اعلامیه چیدمانتان اضافه کنید. مقدار true را تنظیم کنید. همچنین می‌توانید بااستفاده از android:focusableInTouchMode، یک View را در «حالت لمسی» به‌عنوان قابل‌تمرکز اعلام کنید.

برای درخواست از View خاصی برای گرفتن تمرکز، requestFocus() را فراخوانی کنید.

برای گوش دادن به رویدادهای تمرکز (دریافت اعلان وقتی View تمرکز را دریافت یا ازدست می‌دهد)، از onFocusChange() استفاده کنید، همان‌طور که در بخش شنوندگان رویداد توضیح داده شده است.