ترسیم UI تنها بخشی از ایجاد نمای سفارشی است. همچنین باید کاری کنید که نمای شما به ورودی کاربر به گونهای پاسخ دهد که شباهت زیادی به عملی که در دنیای واقعی تقلید میکنید باشد.
کاری کنید که اشیاء در برنامه شما مانند اشیاء واقعی عمل کنند. به عنوان مثال، اجازه ندهید تصاویر موجود در برنامه شما از وجود خارج شوند و دوباره در جای دیگری ظاهر شوند، زیرا اشیاء در دنیای واقعی این کار را نمی کنند. در عوض، تصاویر خود را از یک مکان به مکان دیگر منتقل کنید.
کاربران حتی رفتار یا احساس ظریف را در یک رابط حس می کنند و بهترین واکنش را نسبت به ظرافت هایی که دنیای واقعی را تقلید می کنند نشان می دهند. به عنوان مثال، هنگامی که کاربران یک شی UI را پرت می کنند، در ابتدا به آنها احساس اینرسی بدهید که حرکت را به تاخیر می اندازد. در پایان حرکت، به آنها حس حرکتی بدهید که جسم را فراتر از پرت کردن می برد.
این صفحه نحوه استفاده از ویژگیهای چارچوب Android را برای افزودن این رفتارهای دنیای واقعی به نمای سفارشی خود نشان میدهد.
میتوانید اطلاعات مرتبط بیشتری را در نمای کلی رویدادهای ورودی و نمای کلی انیمیشن ویژگی پیدا کنید.
کنترل حرکات ورودی
مانند بسیاری دیگر از فریم ورک های رابط کاربری، اندروید از مدل رویداد ورودی پشتیبانی می کند. کنشهای کاربر به رویدادهایی تبدیل میشوند که پاسخهای تماس را راهاندازی میکنند، و میتوانید پاسخهای تماس را لغو کنید تا نحوه پاسخ برنامهتان به کاربر را سفارشی کنید. متداول ترین رویداد ورودی در سیستم اندروید لمس است که onTouchEvent(android.view.MotionEvent)
را فعال می کند. این روش را برای مدیریت رویداد به صورت زیر لغو کنید:
کاتلین
override fun onTouchEvent(event: MotionEvent): Boolean { return super.onTouchEvent(event) }
جاوا
@Override public boolean onTouchEvent(MotionEvent event) { return super.onTouchEvent(event); }
رویدادهای لمسی به خودی خود مفید نیستند. رابطهای کاربری لمسی مدرن تعاملات را بر حسب حرکاتی مانند ضربه زدن، کشیدن، هل دادن، پرت کردن و بزرگنمایی تعریف میکنند. برای تبدیل رویدادهای لمس خام به ژستها، Android GestureDetector
ارائه میکند.
با ارسال نمونه ای از کلاسی که GestureDetector.OnGestureListener
را پیاده سازی می کند، یک GestureDetector
بسازید. اگر میخواهید فقط چند حرکت را پردازش کنید، میتوانید به جای اجرای رابط GestureDetector.OnGestureListener
GestureDetector.SimpleOnGestureListener
را گسترش دهید. برای مثال، این کد کلاسی را ایجاد می کند که GestureDetector.SimpleOnGestureListener
را گسترش می دهد و onDown(MotionEvent)
را لغو می کند.
کاتلین
private val myListener = object : GestureDetector.SimpleOnGestureListener() { override fun onDown(e: MotionEvent): Boolean { return true } } private val detector: GestureDetector = GestureDetector(context, myListener)
جاوا
class MyListener extends GestureDetector.SimpleOnGestureListener { @Override public boolean onDown(MotionEvent e) { return true; } } detector = new GestureDetector(getContext(), new MyListener());
چه از GestureDetector.SimpleOnGestureListener
استفاده کنید یا نه، همیشه یک متد onDown()
را اجرا کنید که true
برمی گرداند. این ضروری است زیرا همه حرکات با یک پیام onDown()
شروع می شوند. اگر false
از onDown()
برگردانید، همانطور که GestureDetector.SimpleOnGestureListener
انجام می دهد، سیستم فرض می کند که می خواهید بقیه ژست را نادیده بگیرید، و سایر روش های GestureDetector.OnGestureListener
نامیده نمی شوند. فقط اگر میخواهید یک ژست کامل را نادیده بگیرید، از onDown()
false
را برگردانید.
پس از پیاده سازی GestureDetector.OnGestureListener
و ایجاد یک نمونه از GestureDetector
، می توانید از GestureDetector
خود برای تفسیر رویدادهای لمسی که در onTouchEvent()
دریافت می کنید استفاده کنید.
کاتلین
override fun onTouchEvent(event: MotionEvent): Boolean { return detector.onTouchEvent(event).let { result -> if (!result) { if (event.action == MotionEvent.ACTION_UP) { stopScrolling() true } else false } else true } }
جاوا
@Override public boolean onTouchEvent(MotionEvent event) { boolean result = detector.onTouchEvent(event); if (!result) { if (event.getAction() == MotionEvent.ACTION_UP) { stopScrolling(); result = true; } } return result; }
هنگامی که روی onTouchEvent()
یک رویداد لمسی را ارسال می کنید که آن را به عنوان بخشی از یک حرکت تشخیص نمی دهد، false
را برمی گرداند. سپس می توانید کد تشخیص حرکت سفارشی خود را اجرا کنید.
حرکت قابل قبول فیزیکی ایجاد کنید
ژستها روشی قدرتمند برای کنترل دستگاههای صفحه لمسی هستند، اما میتوانند غیرقابل درک و به خاطر سپردن آنها مشکل باشند، مگر اینکه نتایج قابل قبولی از لحاظ فیزیکی ایجاد کنند.
برای مثال، فرض کنید میخواهید یک ژست پرتاب افقی اجرا کنید که آیتم ترسیم شده در نمای را در اطراف محور عمودی خود بچرخاند. اگر UI با حرکت سریع در جهت پرتاب و سپس کاهش سرعت پاسخ دهد، این ژست منطقی به نظر می رسد، گویی که کاربر چرخ لنگر را فشار داده و باعث چرخش آن شود.
مستندات مربوط به نحوه متحرک سازی ژست اسکرول توضیح مفصلی در مورد نحوه اجرای رفتار اسکول خود ارائه می دهد. اما شبیه سازی احساس چرخ طیار امری پیش پا افتاده نیست. برای اینکه یک مدل فلایویل به درستی کار کند، فیزیک و ریاضیات زیادی لازم است. خوشبختانه اندروید کلاس های کمکی را برای شبیه سازی این رفتار و سایر رفتارها ارائه می دهد. کلاس Scroller
مبنایی برای کنترل حرکات پرتاب به سبک چرخ طیار است.
برای شروع یک fling، fling()
را با سرعت شروع و حداقل و حداکثر مقادیر x و y fling فراخوانی کنید. برای مقدار سرعت، می توانید از مقدار محاسبه شده توسط GestureDetector
استفاده کنید.
کاتلین
fun onFling(e1: MotionEvent, e2: MotionEvent, velocityX: Float, velocityY: Float): Boolean { scroller.fling( currentX, currentY, (velocityX / SCALE).toInt(), (velocityY / SCALE).toInt(), minX, minY, maxX, maxY ) postInvalidate() return true }
جاوا
@Override public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) { scroller.fling(currentX, currentY, velocityX / SCALE, velocityY / SCALE, minX, minY, maxX, maxY); postInvalidate(); return true; }
فراخوانی به fling()
مدل فیزیک را برای ژست fling تنظیم می کند. پس از آن، Scroller
را با فراخوانی Scroller.computeScrollOffset()
در فواصل زمانی منظم به روز کنید. computeScrollOffset()
وضعیت داخلی شی Scroller
را با خواندن زمان جاری و استفاده از مدل فیزیک برای محاسبه موقعیت x و y در آن زمان به روز می کند. برای بازیابی این مقادیر getCurrX()
و getCurrY()
را فراخوانی کنید.
اکثر نماها موقعیت های x و y شی Scroller
را مستقیماً به scrollTo()
می دهند. این مثال کمی متفاوت است: از موقعیت فعلی اسکرول x برای تنظیم زاویه چرخش نما استفاده می کند.
کاتلین
scroller.apply { if (!isFinished) { computeScrollOffset() setItemRotation(currX) } }
جاوا
if (!scroller.isFinished()) { scroller.computeScrollOffset(); setItemRotation(scroller.getCurrX()); }
کلاس Scroller
موقعیت های اسکرول را برای شما محاسبه می کند، اما به طور خودکار این موقعیت ها را در نمای شما اعمال نمی کند. مختصات جدید را اغلب به اندازه کافی اعمال کنید تا انیمیشن اسکرول صاف به نظر برسد. دو راه برای این کار وجود دارد:
- با فراخوانی
postInvalidate()
پس از فراخوانیfling()
یک ترسیم مجدد اجباری کنید. این تکنیک مستلزم آن است که افست های اسکرول را درonDraw()
محاسبه کنید و هر بار که افست اسکرول تغییر می کندpostInvalidate()
فراخوانی کنید. - یک
ValueAnimator
برای متحرک سازی در طول مدت پرتاب تنظیم کنید و یک شنونده برای پردازش به روز رسانی های انیمیشن با فراخوانیaddUpdateListener()
اضافه کنید. این تکنیک به شما امکان می دهد خصوصیات یکView
را متحرک کنید.
انتقال خود را صاف کنید
کاربران انتظار دارند که یک رابط کاربری مدرن به آرامی بین حالتها جابهجا شود: عناصر رابط کاربری به جای ظاهر شدن و ناپدید شدن به داخل و خارج میشوند و حرکتها بهجای شروع و توقف ناگهانی به آرامی شروع و پایان مییابند. فریم ورک انیمیشن دارای ویژگی اندروید، انتقال روان را آسان تر می کند.
برای استفاده از سیستم پویانمایی، هر زمان که ویژگی چیزی را تغییر داد که بر ظاهر نمای شما تأثیر می گذارد، ویژگی را مستقیماً تغییر ندهید. در عوض، از ValueAnimator
برای ایجاد تغییر استفاده کنید. در مثال زیر، با تغییر مولفه فرزند انتخاب شده در نما، کل نمای رندر شده بچرخد تا نشانگر انتخاب در مرکز قرار گیرد. ValueAnimator
چرخش را در مدت چند صد میلی ثانیه تغییر می دهد، نه اینکه بلافاصله مقدار چرخش جدید را تنظیم کند.
کاتلین
autoCenterAnimator = ObjectAnimator.ofInt(this, "Rotation", 0).apply { setIntValues(targetAngle) duration = AUTOCENTER_ANIM_DURATION start() }
جاوا
autoCenterAnimator = ObjectAnimator.ofInt(this, "Rotation", 0); autoCenterAnimator.setIntValues(targetAngle); autoCenterAnimator.setDuration(AUTOCENTER_ANIM_DURATION); autoCenterAnimator.start();
اگر مقداری که میخواهید تغییر دهید یکی از ویژگیهای View
پایه است، انجام انیمیشن آسانتر است، زیرا Viewها دارای ViewPropertyAnimator
داخلی هستند که برای انیمیشنسازی همزمان چندین ویژگی بهینه شده است، مانند مثال زیر:
کاتلین
animate() .rotation(targetAngle) .duration = ANIM_DURATION .start()
جاوا
animate().rotation(targetAngle).setDuration(ANIM_DURATION).start();