إنشاء عرض مخصّص تفاعلي

تجربة طريقة ComposeAllowed
Jetpack Compose هي مجموعة أدوات واجهة المستخدم التي ننصح بها لنظام التشغيل Android. تعرَّف على كيفية استخدام التنسيقات في Compose.

يعد رسم واجهة مستخدم جزءًا واحدًا فقط من إنشاء طريقة عرض مخصصة. يجب عليك أيضًا جعل طريقة العرض تستجيب لإدخال المستخدم بطريقة تشبه إلى حد كبير حركة العالم الحقيقي التي تحاكيها.

جعل العناصر في تطبيقك تعمل مثل العناصر الحقيقية على سبيل المثال، لا تدع الصور في تطبيقك تظهر خارج الوجود ثم تظهر مرة أخرى في مكان آخر، نظرًا لأن الكائنات في العالم الحقيقي لا تفعل ذلك. بدلاً من ذلك، انقل صورك من مكان إلى البعض

يشعر المستخدمون بسلوكيات خفية أو شعور تجاه واجهة ما ويتفاعلون بشكل أفضل مع التفاصيل الدقيقة التي تحاكي العالم الحقيقي. فعلى سبيل المثال، عندما يتنقل المستخدمون كائن واجهة مستخدم، ويمنحهم إحساسًا بالقصور الذاتي في البداية مما يؤخر الحركة. في النهاية من الحركة، يمنحهم إحساسًا بالزخم الذي يحمل الكائن إلى ما وراء قذف

توضح هذه الصفحة كيفية استخدام ميزات إطار عمل Android لإضافة هذه السلوكيات في العالم الحقيقي إلى طريقة العرض المخصّصة لديك

يمكنك العثور على معلومات إضافية ذات صلة في نظرة عامة على أحداث الإدخال تحريك الموقع نظرة عامة.

التعامل مع إيماءات الإدخال

على غرار العديد من أطر عمل واجهة المستخدم الأخرى، يتيح Android نموذج إدخال الأحداث. مستخدِم تتحول إلى أحداث تؤدي إلى استدعاءات، ويمكنك إلغاء لطلبات معاودة الاتصال لتخصيص كيفية استجابة تطبيقك للمستخدم. الإدخال الأكثر شيوعًا الحدث في نظام Android وهو حدث Touch، ما يؤدي إلى تشغيل onTouchEvent(android.view.MotionEvent) يمكنك تجاهُل هذه الطريقة للتعامل مع الحدث، كما يلي:

Kotlin

override fun onTouchEvent(event: MotionEvent): Boolean {
    return super.onTouchEvent(event)
}

Java

@Override
   public boolean onTouchEvent(MotionEvent event) {
    return super.onTouchEvent(event);
   }

أحداث اللمس وحدها ليست مفيدة بشكل خاص. واجهات مستخدم بلمسة عصرية تحديد التفاعلات من حيث الإيماءات مثل النقر والسحب والدفع والقفز والتكبير. لتحويل أحداث اللمس الأولية إلى إيماءات، يستخدم Android توفّر GestureDetector

إنشاء GestureDetector بتمريرها في نسخة افتراضية من فئة معيّنة تنفّذ GestureDetector.OnGestureListener إذا كنت تريد معالجة بعض الإيماءات فقط، فيمكنك تمديد GestureDetector.SimpleOnGestureListener بدلاً من تنفيذ سياسة "GestureDetector.OnGestureListener" من واجهة pyplot. على سبيل المثال، تُنشئ هذه التعليمة البرمجية فئة تمتد GestureDetector.SimpleOnGestureListener وعمليات الإلغاء onDown(MotionEvent)

Kotlin

private val myListener =  object : GestureDetector.SimpleOnGestureListener() {
    override fun onDown(e: MotionEvent): Boolean {
        return true
    }
}

private val detector: GestureDetector = GestureDetector(context, myListener)

Java

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. الإرجاع فقط false من onDown() إذا كنت تريد تجاهل مجموعة إيماءة.

بعد تنفيذ GestureDetector.OnGestureListener وإنشاء وهي مثيل لـ GestureDetector، يمكنك استخدام GestureDetector لتفسير أحداث اللمس التي تتلقاها onTouchEvent()

Kotlin

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
    }
}

Java

@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. يمكنك بعد ذلك تنفيذ الرمز المخصص الخاص باكتشاف الإيماءات.

إنشاء حركة معقولة جسديًا

الإيماءات هي طريقة فعالة للتحكم في الأجهزة ذات الشاشات التي تعمل باللمس، إلا أنه يمكن بعكس البديهية ويصعب تذكرها ما لم تنتج جسدًا نتائج معقولة.

على سبيل المثال، لنفترض أنك تريد تنفيذ إيماءة التنقُّل الأفقي لتعيين العنصر المرسوم في طريقة العرض مع الدوران حول محوره الرأسي. هذه الإيماءة منطقية إذا استجابت واجهة المستخدم بالتحرك بسرعة في اتجاه الاتجاه، ثم البطء، كما لو كان المستخدم يدفع دولاب العجلة ويجعلها تدور.

المستندات التي تتناول كيفية تحريك التمرير الإيماءة تقدّم شرحًا تفصيليًا حول كيفية تنفيذ جمجمتك الخاصة السلوك. لكن محاكاة إحساس الحدافة ليس أمرًا تافهًا. الكثير من الفيزياء والرياضيات لكي يعمل نموذج الحدافة بشكل صحيح. لحسن الحظ، ويوفّر Android صفوفًا مساعدة لمحاكاة هذا السلوك وغيره من السلوكيات. تشير رسالة الأشكال البيانية Scroller الفئة هو الأساس للتعامل مع إيماءات التنقل بنمط حدافة.

لبدء الانتقال السريع، يُرجى الاتصال fling() مع سرعة البدء والحد الأدنى والأقصى س وص قيم الانتقال. بالنسبة لقيمة السرعة، يمكنك استخدام القيمة المحسوبة بواسطة GestureDetector

Kotlin

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
}

Java

@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()" في إعداد النموذج الفيزيائي للقفز. إيماءة. بعد ذلك، يمكنك تعديل "Scroller" من خلال الاتصال. Scroller.computeScrollOffset() على فترات منتظمة. يعدّل computeScrollOffset() الحالة الداخلية لكائن Scroller من خلال قراءة الوقت الحالي استخدام النموذج الفيزيائي لحساب الموضعَين س وص عند ذلك الوقت. اتصل getCurrX() أو getCurrY() لاسترداد هذه القيم.

تتجاوز معظم المشاهدات الرمزين x وy للكائن Scroller. المناصب مباشرة إلى scrollTo() يختلف هذا المثال قليلاً، إذ يستخدِم الموضع الحالي للتمرير x لتعيين زاوية الدوران للعرض.

Kotlin

scroller.apply {
    if (!isFinished) {
        computeScrollOffset()
        setItemRotation(currX)
    }
}

Java

if (!scroller.isFinished()) {
    scroller.computeScrollOffset();
    setItemRotation(scroller.getCurrX());
}

تحسب الفئة Scroller مواضع التمرير نيابة عنك، لكنها لا يطبق هذه المواضع تلقائيًا على العرض. تطبيق الإحداثيات الجديدة غالبًا بما يكفي لجعل الرسوم المتحركة المتحركة تبدو سلسة. هناك طريقتان إجراء ذلك:

  • فرض إعادة الرسم عن طريق الاتصال postInvalidate() بعد الاتصال بـ fling(). يتطلب هذا الأسلوب احتساب إزاحة التمرير في onDraw() واستدعاء postInvalidate() في كل مرة تساوي قيمة الانتقال التغييرات.
  • إعداد ValueAnimator الرسوم المتحركة طوال مدة الانتقال وإضافة مستمع لمعالجته تحديثات الرسوم المتحركة من خلال الاتصال addUpdateListener() تتيح لك هذه التقنية تحريك خصائص View

تسهيل الانتقالات

يتوقع المستخدمون أن تنتقل واجهة المستخدم الحديثة بسلاسة بين الحالات التالية: عناصر واجهة المستخدم يتلاشى للداخل والخارج بدلاً من الظهور ويختفي، وتبدأ الحركات وينتهي بسلاسة بدلاً من البدء والتوقف فجأة. نظام التشغيل Android صورة متحركة للخصائص إطار عمل جديد يجعل عمليات الانتقال السلسة أسهل.

لاستخدام نظام الرسوم المتحركة، كلما تغيرت خاصية ما يؤثر في مظهر الملف الشخصي، فلا تغيِّر الموقع مباشرةً. بدلاً من ذلك، استخدم ValueAnimator لإجراء التغيير. في المثال التالي، يؤدي تعديل العنصر الثانوي المحدد في طريقة العرض إلى جعل الجزء المعروض بالكامل تدوير العرض بحيث يتم توسيط مؤشر التحديد. تغيّر الدالة ValueAnimator الدوران على مدى عدة مئات. بالمللي ثانية، بدلاً من تعيين قيمة التدوير الجديدة على الفور.

Kotlin

autoCenterAnimator = ObjectAnimator.ofInt(this, "Rotation", 0).apply {
    setIntValues(targetAngle)
    duration = AUTOCENTER_ANIM_DURATION
    start()
}

Java

autoCenterAnimator = ObjectAnimator.ofInt(this, "Rotation", 0);
autoCenterAnimator.setIntValues(targetAngle);
autoCenterAnimator.setDuration(AUTOCENTER_ANIM_DURATION);
autoCenterAnimator.start();

إذا كانت القيمة التي تريد تغييرها هي إحدى قيم View الأساسية الخصائص، فإن تنفيذ الرسوم المتحركة يكون أسهل، نظرًا لأن طرق العرض تشتمل على ViewPropertyAnimator التي تم تحسينها للحركة المتزامنة لخصائص متعددة، كما هو الحال في المثال التالي:

Kotlin

animate()
    .rotation(targetAngle)
    .duration = ANIM_DURATION
    .start()

Java

animate().rotation(targetAngle).setDuration(ANIM_DURATION).start();