استخدام عناصر التحكّم في النقل

الإنشاء بشكل أفضل باستخدام Compose
يمكنك إنشاء واجهات مستخدم رائعة باستخدام أقل عدد ممكن من الرموز باستخدام Jetpack Compose لنظام التشغيل Android TV.

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

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

عناصر التحكّم والمشغّل

تفصل مجموعة أدوات واجهة مستخدم Leanback واجهة المستخدم لعناصر التحكم في النقل من المشغل الذي يقوم بتشغيل الفيديو. ويتم تحقيق ذلك من خلال مكونَين: جزء دعم التشغيل لعرض عناصر التحكم في النقل (والفيديو بشكل اختياري) ومحوّل المشغّل لتغليف مشغّل وسائط.

جزء التشغيل

يجب أن يستخدم سجلّ "نشاط واجهة المستخدم" في تطبيقك PlaybackSupportFragment أو VideoSupportFragment. يحتوي كلاهما على عناصر التحكم في النقل leanback:

يمكنك تخصيص ObjectAdapter للأجزاء لتحسين واجهة المستخدم. على سبيل المثال، استخدِم setAdapter() لإضافة صف "فيديوهات ذات صلة".

مهايئ المشغّل

PlayerAdapter هي فئة مجردة تتحكم في مشغّل الوسائط الأساسي. يمكن للمطوّرين اختيار طريقة تنفيذ MediaPlayerAdapter سابقة الإعداد أو كتابة تنفيذهم الخاص لهذه الفئة.

لصق القطع معًا

يجب استخدام بعض "غراء التحكم" لربط جزء التشغيل بالمشغّل. توفر مكتبة leanback نوعين من الغراء:

  • يرسم PlaybackBannerControlGlue عناصر التحكم في النقل في جزء التشغيل "بالنمط القديم"، ويضعها داخل خلفية معتمة. (يحلّ PlaybackBannerControlGlue محلّ PlaybackControlGlue، الذي تم إيقافه).
  • يستخدم PlaybackTransportControlGlue عناصر تحكم "نمط جديد" بخلفية شفافة.

غراء التحكّم في نقل البيانات من leanback

إذا كنت تريد أن يوفّر تطبيقك ميزة تنقيح الفيديوهات، عليك استخدام PlaybackTransportControlGlue.

ويتعين عليك أيضًا تحديد "مضيف غراء" يربط الشريط بجزء التشغيل، ويرسم عناصر التحكم في النقل في واجهة المستخدم، ويحافظ على حالتها، ويعيد الاحتفاظ بأحداث التحكم في النقل. يجب أن يطابق المضيف نوع جزء التشغيل. يمكنك استخدام PlaybackSupportFragmentGlueHost مع PlaybackFragment و VideoSupportFragmentGlueHost مع VideoFragment.

إليك رسم توضيحي يوضح كيفية ملاءمة أجزاء عنصر التحكم في النقل leanback معًا:

غراء التحكّم في نقل البيانات من leanback

يجب أن يكون الرمز الذي يلتصق بتطبيقك معًا داخل PlaybackSupportFragment أو VideoSupportFragment اللذين يحدّدان واجهة المستخدم.

في المثال التالي، ينشئ التطبيق مثيلاً لـ PlaybackTransportControlGlue، ويطلق عليه اسم playerGlue، ويربط VideoSupportFragment بعنصر MediaPlayerAdapter تم إنشاؤه حديثًا. ولأنّ هذا VideoSupportFragment، يستدعي رمز الإعداد setHost() لإرفاق VideoSupportFragmentGlueHost إلى playerGlue. ويتم تضمين الرمز داخل الفئة التي تُوسّع VideoSupportFragment.

Kotlin

class MyVideoFragment : VideoSupportFragment() {

  fun onCreate(savedInstanceState: Bundle) {
      super.onCreate(savedInstanceState)
      val playerGlue = PlaybackTransportControlGlue(getActivity(),
          MediaPlayerAdapter(getActivity()))
      playerGlue.setHost(VideoSupportFragmentGlueHost(this))
      playerGlue.addPlayerCallback(object : PlaybackGlue.PlayerCallback() {
          override fun onPreparedStateChanged(glue: PlaybackGlue) {
              if (glue.isPrepared()) {
                  playerGlue.seekProvider = MySeekProvider()
                  playerGlue.play()
              }
          }
      })
      playerGlue.setSubtitle("Leanback artist")
      playerGlue.setTitle("Leanback team at work")
      val uriPath = "android.resource://com.example.android.leanback/raw/video"
      playerGlue.getPlayerAdapter().setDataSource(Uri.parse(uriPath))
  }
}

Java

public class MyVideoFragment extends VideoSupportFragment {

  @Override
  public void onCreate(Bundle savedInstanceState) {
      super.onCreate(savedInstanceState);
      final PlaybackTransportControlGlue<MediaPlayerAdapter> playerGlue =
              new PlaybackTransportControlGlue(getActivity(),
                      new MediaPlayerAdapter(getActivity()));
      playerGlue.setHost(new VideoSupportFragmentGlueHost(this));
      playerGlue.addPlayerCallback(new PlaybackGlue.PlayerCallback() {
          @Override
          public void onPreparedStateChanged(PlaybackGlue glue) {
              if (glue.isPrepared()) {
                  playerGlue.setSeekProvider(new MySeekProvider());
                  playerGlue.play();
              }
          }
      });
      playerGlue.setSubtitle("Leanback artist");
      playerGlue.setTitle("Leanback team at work");
      String uriPath = "android.resource://com.example.android.leanback/raw/video";
      playerGlue.getPlayerAdapter().setDataSource(Uri.parse(uriPath));
  }
}

يُرجى العلم أنّ رمز الإعداد يحدِّد أيضًا PlayerAdapter.Callback للتعامل مع الأحداث من مشغّل الوسائط.

تخصيص الغراء لواجهة المستخدم

يمكنك تخصيص PlaybackBannerControlGlue وPlaybackTransportControlGlue لتغيير PlaybackControlsRow.

تخصيص العنوان والوصف

لتخصيص العنوان والوصف في أعلى عناصر التحكّم في التشغيل، يمكنك إلغاء onCreateRowPresenter():

Kotlin

override fun onCreateRowPresenter(): PlaybackRowPresenter {
    return super.onCreateRowPresenter().apply {
        (this as? PlaybackTransportRowPresenter)
                ?.setDescriptionPresenter(MyCustomDescriptionPresenter())
    }
}

Java

@Override
protected PlaybackRowPresenter onCreateRowPresenter() {
  PlaybackTransportRowPresenter presenter = (PlaybackTransportRowPresenter) super.onCreateRowPresenter();
  presenter.setDescriptionPresenter(new MyCustomDescriptionPresenter());
  return presenter;
}

جارٍ إضافة عناصر التحكّم

يعرض غراء التحكّم عناصر تحكّم في الإجراءات في "PlaybackControlsRow".

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

يمكنك إضافة إجراءات إلى المجموعات الأساسية والثانوية عن طريق إلغاء onCreatePrimaryActions() وonCreateSecondaryActions().

Kotlin

private lateinit var repeatAction: PlaybackControlsRow.RepeatAction
private lateinit var pipAction: PlaybackControlsRow.PictureInPictureAction
private lateinit var thumbsUpAction: PlaybackControlsRow.ThumbsUpAction
private lateinit var thumbsDownAction: PlaybackControlsRow.ThumbsDownAction
private lateinit var skipPreviousAction: PlaybackControlsRow.SkipPreviousAction
private lateinit var skipNextAction: PlaybackControlsRow.SkipNextAction
private lateinit var fastForwardAction: PlaybackControlsRow.FastForwardAction
private lateinit var rewindAction: PlaybackControlsRow.RewindAction

override fun onCreatePrimaryActions(primaryActionsAdapter: ArrayObjectAdapter) {
    // Order matters, super.onCreatePrimaryActions() will create the play / pause action.
    // Will display as follows:
    // play/pause, previous, rewind, fast forward, next
    //   > /||      |<        <<        >>         >|
    super.onCreatePrimaryActions(primaryActionsAdapter)
    primaryActionsAdapter.apply {
        add(skipPreviousAction)
        add(rewindAction)
        add(fastForwardAction)
        add(skipNextAction)
    }
}

override fun onCreateSecondaryActions(adapter: ArrayObjectAdapter?) {
    super.onCreateSecondaryActions(adapter)
    adapter?.apply {
        add(thumbsDownAction)
        add(thumbsUpAction)
    }
}

Java

private PlaybackControlsRow.RepeatAction repeatAction;
private PlaybackControlsRow.PictureInPictureAction pipAction;
private PlaybackControlsRow.ThumbsUpAction thumbsUpAction;
private PlaybackControlsRow.ThumbsDownAction thumbsDownAction;
private PlaybackControlsRow.SkipPreviousAction skipPreviousAction;
private PlaybackControlsRow.SkipNextAction skipNextAction;
private PlaybackControlsRow.FastForwardAction fastForwardAction;
private PlaybackControlsRow.RewindAction rewindAction;

@Override
protected void onCreatePrimaryActions(ArrayObjectAdapter primaryActionsAdapter) {
    // Order matters, super.onCreatePrimaryActions() will create the play / pause action.
    // Will display as follows:
    // play/pause, previous, rewind, fast forward, next
    //   > /||      |<        <<        >>         >|
    super.onCreatePrimaryActions(primaryActionsAdapter);
    primaryActionsAdapter.add(skipPreviousAction);
    primaryActionsAdapter.add(rewindAction);
    primaryActionsAdapter.add(fastForwardAction);
    primaryActionsAdapter.add(skipNextAction);
}

@Override
protected void onCreateSecondaryActions(ArrayObjectAdapter adapter) {
    super.onCreateSecondaryActions(adapter);
    adapter.add(thumbsDownAction);
    adapter.add(thumbsUpAction);
}

وعليك إلغاء onActionClicked() للتعامل مع الإجراءات الجديدة.

Kotlin

override fun onActionClicked(action: Action) {
    when(action) {
        rewindAction -> {
            // Handle Rewind
        }
        fastForwardAction -> {
            // Handle FastForward
        }
        thumbsDownAction -> {
            // Handle ThumbsDown
        }
        thumbsUpAction -> {
            // Handle ThumbsUp
        }
        else ->
            // The superclass handles play/pause and delegates next/previous actions to abstract methods,
            // so those two methods should be overridden rather than handling the actions here.
            super.onActionClicked(action)
    }
}

override fun next() {
    // Skip to next item in playlist.
}

override fun previous() {
    // Skip to previous item in playlist.
}

Java

@Override
public void onActionClicked(Action action) {
    if (action == rewindAction) {
        // Handle Rewind
    } else if (action == fastForwardAction ) {
        // Handle FastForward
    } else if (action == thumbsDownAction) {
        // Handle ThumbsDown
    } else if (action == thumbsUpAction) {
        // Handle ThumbsUp
    } else {
        // The superclass handles play/pause and delegates next/previous actions to abstract methods,
        // so those two methods should be overridden rather than handling the actions here.
        super.onActionClicked(action);
    }
}

@Override
public void next() {
    // Skip to next item in playlist.
}

@Override
public void previous() {
    // Skip to previous item in playlist.
}

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

التقديم والترجيع في الفيديو

إذا كان تطبيقك يستخدم VideoSupportFragment وتريد أن تتيح تنظيف الفيديو.

تنظيف بالفرشاة

يجب تنفيذ PlaybackSeekDataProvider. يوفر هذا المكوِّن الصور المصغّرة للفيديو المستخدمة عند التمرير. وعليك الاستعانة بمقدّم الخدمة الذي تتعامل معه من خلال تمديد فترة PlaybackSeekDataProvider. اطّلع على المثال في تطبيق Leanback Showcase.