یک نمای سفارشی تعاملی ایجاد کنید

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

ترسیم 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();