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

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

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

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

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

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

جزء التشغيل

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

  • تعمل الأداة PlaybackSupportFragment على تحريك عناصر التحكّم في النقل لإخفائها أو إظهارها حسب الحاجة.
  • يمتدّ VideoSupportFragment إلى PlaybackSupportFragment ويتضمّن SurfaceView لعرض الفيديو.

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

PlayerAdapter

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

تجميع الأجزاء

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

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

leanback transport control glue

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

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

في ما يلي رسم توضيحي يوضّح كيفية عمل عناصر التحكّم في النقل في وضع الاسترخاء:

leanback transport control glue

يجب أن يكون الرمز البرمجي الذي يربط أجزاء تطبيقك معًا داخل 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. .