تسلط بر TouchEvent ها در اندورید

در این مقاله سعی کردم بصورت مفصل درباره TouchEvent ها در اندروید توضیح بدم و نکات لازم رو بنویسم. سوالی که همیشه هنگام کار با کاستوم ویو ها پیش میاد اینه که از onTouchEvent استفاده کنیم یا onInterceptTouchEvent ؟ اصلا چه فرقی دارن و چیکار میکنن؟ ?



چرخه Touch:

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


وقتی که کاربر ویوی از صفحه نمایش رو لمس میکنه متد onInterceptTouch طبق سناریوی زیر صدا زده میشه. کار این متد مطلع کردن لایه های پایین تر از اکشن های تاچ هست. متد onInterceptTouch رو فقط ViewGroup ها دارن. میتوان برای مطلع شدن از Touch در اکتیویتی از متد dispatchTouchEvent و برای مطلع شدن از Touch در view ها از onTouchEvent استفاده کرد. هر سه متد مقدار بازگشتیشان از نوع Boolean هست و اگر به متد true برگردد یعنی Touch هندل شده و به لایه بعدی منتقل نکن، ولی اگر false برگردد یعنی Touch هندل نشده و event به لایه بعدی منتقل بشه.

سناریو و ترتیب صدا شدن متد ها: با لمس کردن صفحه ابتدا اکتیویتی این نوتیفای رو دریافت میکنه و متد dispatchTouchEvent صدا زده میشه. در صورت false بودن event به Window فرستاده میشه. window یک abstract class هست که پیاده سازی این کلاس PhoneWindow هست. بعد dispatchTouchEvent برای DecorView صدا زده میشه که این کلاس از یک FrameLayout ارث برده شده، در این کلاس استاتوس بار و باتوم نویگیشن هندل میشن. این کلاس چون یک ViewGroup هست پس متد onInterceptTouchEvent هم داره که صدا زده میشه. ما چون دسترسی ای به این دو کلاس نداریم پس کاری هم باهاشون نداریم و فقط اینجا آوردم تا سلسله مراتب کامل گفته بشه.

مرحله بعد نوبت به ViewGroup هامون میرسه. ViewGroup اول rootView هستش. همون ViewGroup که تو لایه xml برای اکتیویتی در نظر میگیریم. ViewGroup دوم هم child اولی هست. همونطوری که بالا گفته شد ترتیب صدا شدن متد ها تو ViewGroup ها اول dispatchTouchEvent هست. بعد onInterceptTouchEvent. در آخر درصورت false بودن مقدار بازگشتی همه این ها که تا اینجا اومدی متد onTouchEvent در View صدا زده میشه. اگه به view اینترفیس OnTouchListener داده شده باشه متد OnTouchListener.onTouch و onTouchEvent هر دو صدا زده میشن، اگر null بود فقط onTouchEvent صدا زده میشه.

ازینجا به بعد چی میشه؟ پس onTouchEvent های لایه های بالاتر چی؟ چرا اونا صدا زده نشدن؟؟؟ اول کمی بهش فکر کنید بعد به تصویر پایین نگاه کنید.

چرخه Touch در اندروید
چرخه Touch در اندروید

در تصویر بالا کلاس های PhoneWindow و DecorView رو چون بهشون دسترسی نداریم نیاوردم. مسیر گیج کننده ای که بالا بهتون توضیح دادم رومیتونید تو عکس بالا بررسی کنید و با مسیر حرکت کنید تا براتون شفاف بشه. dispatchTouchEvent و onInterceptTouchEvent از بالا ترین لایه میان تا پایین ترین لایه. به همه لایه ها نوتیفای میکنن که Touch رخ داده و آبجکتی از نوع MotionEvent با خودشون حمل می کنند.

متد onTouchEvent از پایین ترین لایه شروع به حرکت می کنه و اگر کسی true برنگردونه تا بالاترین لایه یعنی اکتیویتی میره.

این رو بصورت یک چرخه دائم و حرکت در نظر بگیرید تا بتونه اولین متدی که true برگردونه رو پیدا کنه. این لووپ اینقدر تکرار میشه تا onTouchEvent ای رو پیدا کنه که true شده. فرض کنید از لایه بالا سروع به حرکت میکنه تا به ViewGroup B برسه و در اینجا متد onInterceptTouchEvent مقدار true بر میگردونه. این true برگردوندن یعنی من تاچ رو هندل میکنم و به لایه های پایینتر نده. بعد onTouchEvent از ViewGroup B صدا زده میشه. اگه true باشه هنگام انتشار اکشن های بعدی این چرخه دیگه تکرار نمیشه و مستقیم فقط متد onTouchEvent از ViewGroup B صدا زده میشه. اگه هم false برگردونه که چرخه باز تکرار میشه ولی لایه های پایین تر دیگه نمیرسه.



تا اینجای کار چرخه Touch رو سعی کردم کامل توضیح بدم. و الان وقتشه تا با اکشن ها آشنا شیم.

انواع اکشن ها:

آبجکت MotionEvent به ما چهار اکشن مهم میده. اکشن های دیگه ای هم هست که اینجا گفته نمیشن.

  • ACTION_DOWN
  • ACTION_UP
  • ACTION_MOVE
  • ACTION_CANCEL

سری اول که صفحه رو لمس می کنیم ACTION_DOWN داده میشه. یعنی کاربر انگشتش رو گذاشت رو صفحه. سری های بعدی ACTION_MOVE که یعنی کاربر داره انگشتش رو روی صفحه میکشه و در آخر ACTION_UP که یعنی کاربر انگشتش رو از روی صفحه برداشت ?. حالا ACTION_CANCEL پس چیه؟ این اکشن زمانی داده میشه که onInterceptTouchEvent میاد Touch رو هندل میکنه و به لایه پایین تر خودش ACTION_CANCEL رو میده.

متد requestDisallowTouchInterecept کارش چیه؟ ?

فرض کنید یک کاستوم ویو دارید که داخل یک اسکرول ویو هست. و میخوایید داخل این کاستوم ویو یک مربع رو جابجا کنید. در حالت عادی اگه اینکار رو کنید اسکرول ویو هم جابجا میشه ?. اینجا تو متد onTouchEvent کاستوم ویو باید متد requestDisallowTouchInterecept رو صدا بزنیم تا به لایه های بالاش بگه که دیگه onInterceptTouchEvent صدا زده نشه? اینجوری مشکل حل میشه و دیگه اسکرول نمیشه. فقط مربع تکون میخوره.


اینم از چرخه Touch در اندروید ? اگه جاییش مشکل داشت لطفا راهنمایی کنید تا اصلاحش کنیم.