تخصيص واجهة مستخدم Android Sender

يمكنك تخصيص أدوات البث عن طريق ضبط الألوان وتحديد أنماط الأزرار والنص ومظهر الصورة المصغّرة وعن طريق اختيار أنواع الأزرار التي سيتم عرضها.

تخصيص مظهر التطبيق

ينشئ هذا المثال نمط مظهر مخصّص Theme.CastVideosTheme يمكنه تحديد ألوان مخصّصة ونمط تراكب تمهيدي ونمط وحدة تحكّم مصغّرة ونمط وحدة تحكّم موسّعة.

<style name="Theme.CastVideosTheme" parent="Theme.AppCompat.Light.NoActionBar">
    <!-- Set AppCompat's color theming attrs -->
    <item name="colorPrimary">@color/primary</item>
    <item name="colorPrimaryDark">@color/primary_dark</item>
    <item name="colorAccent">@color/accent</item>
    <item name="android:textColorPrimary">@color/primary_text</item>
    <item name="android:textColorSecondary">@color/secondary_text</item>
    <item name="castIntroOverlayStyle">@style/CustomCastIntroOverlay</item>
    <item name="castMiniControllerStyle">@style/CustomCastMiniController</item>
    <item name="castExpandedControllerStyle">@style/CustomCastExpandedController</item>
</style>

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

تخصيص زر البث

لإضافة mediaRouteTheme مخصّص إلى مظهر تطبيقك، يُرجى اتّباع الخطوات التالية:

<style name="Theme.CastVideosTheme" parent="Theme.AppCompat.Light.NoActionBar">
  <!-- ... -->
  <item name="mediaRouteTheme">@style/CustomMediaRouterTheme</item>
</style>

يُرجى تعريف مظهر جهاز توجيه الوسائط المخصّص وتعريف مظهر مخصّص. mediaRouteButtonStyle:

<style name="CustomMediaRouterTheme" parent="Theme.MediaRouter">
  <item name="mediaRouteButtonStyle">@style/CustomMediaRouteButtonStyle</item>
</style>

<style name="CustomMediaRouteButtonStyle" parent="Widget.MediaRouter.Light.MediaRouteButton">
  <item name="mediaRouteButtonTint">#EEFF41</item>
</style>

يجب استخدام setTint إذا كان إصدار مكتبة الدعم أحدث من 26.0.0. بالنسبة إلى الإصدارات القديمة من مكتبة الدعم، يُرجى استخدام buttonTint بدلاً من ذلك.

تخصيص مظهر المقدمة التي تظهر على سطح الفيديو

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

<style name="CustomCastIntroOverlay" parent="CastIntroOverlay">
    <item name="castButtonTextAppearance">@style/TextAppearance.CustomCastIntroOverlay.Button</item>
    <item name="castTitleTextAppearance">@style/TextAppearance.CustomCastIntroOverlay.Title</item>
</style>
<style name="TextAppearance.CustomCastIntroOverlay.Button" parent="android:style/TextAppearance">
    <item name="android:textColor">#FFFFFF</item>
</style>
<style name="TextAppearance.CustomCastIntroOverlay.Title"parent="android:style/TextAppearance.Large">
    <item name="android:textColor">#FFFFFF</item>
</style>

تخصيص وحدة التحكّم المصغّرة

تخصيص المظهر

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

<style name="CustomCastMiniController" parent="CastMiniController">
    <item name="castShowImageThumbnail">true</item>
    <item name="castTitleTextAppearance">@style/TextAppearance.AppCompat.Subhead</item>
    <item name="castSubtitleTextAppearance">@style/TextAppearance.AppCompat.Caption</item>
    <item name="castBackground">#FFFFFF</item>
    <item name="castProgressBarColor">#FFFFFF</item>
    <item name="castPlayButtonDrawable">@drawable/cast_ic_mini_controller_play</item>
    <item name="castPauseButtonDrawable">@drawable/cast_ic_mini_controller_pause</item>
    <item name="castStopButtonDrawable">@drawable/cast_ic_mini_controller_stop</item>
    <item name="castLargePlayButtonDrawable">@drawable/cast_ic_mini_controller_play_large</item>
    <item name="castLargePauseButtonDrawable">@drawable/cast_ic_mini_controller_pause_large</item>
    <item name="castLargeStopButtonDrawable">@drawable/cast_ic_mini_controller_stop_large</item>
    <item name="castSkipPreviousButtonDrawable">@drawable/cast_ic_mini_controller_skip_prev</item>
    <item name="castSkipNextButtonDrawable">@drawable/cast_ic_mini_controller_skip_next</item>
    <item name="castRewind30ButtonDrawable">@drawable/cast_ic_mini_controller_rewind30</item>
    <item name="castForward30ButtonDrawable">@drawable/cast_ic_mini_controller_forward30</item>
    <item name="castMuteToggleButtonDrawable">@drawable/cast_ic_mini_controller_mute</item>
    <item name="castClosedCaptionsButtonDrawable">@drawable/cast_ic_mini_controller_closed_caption</item
</style>

اختيار الأزرار

يتضمّن MiniControllerFragment ثلاث خانات يمكنها عرض صورة الألبوم وزرَين أو ثلاثة أزرار تحكّم في حال عدم تعبئة صورة الألبوم.

SLOT  SLOT  SLOT
  1     2     3

يعرض الجزء بشكل افتراضي زر التبديل بين التشغيل والإيقاف المؤقت. ويمكن لمطوّري البرامج استخدام السمة castControlButtons لإلغاء الأزرار التي يجب عرضها. يتم تعريف أزرار التحكّم المتوافقة على أنّها موارد المعرِّف:

نوع الزر الوصف
@id/cast_button_type_empty لا تضع زرًا في هذه الخانة
@id/cast_button_type_custom الزر المخصّص
@id/cast_button_type_play_pause_toggle التبديل بين التشغيل والإيقاف المؤقت
@id/cast_button_type_skip_previous التخطي إلى العنصر السابق في قائمة الانتظار
@id/cast_button_type_skip_next التخطي إلى العنصر التالي في قائمة الانتظار
@id/cast_button_type_rewind_30_seconds ترجيع التشغيل بمقدار 30 ثانية
@id/cast_button_type_forward_30_seconds تخطّي التشغيل بمقدار 30 ثانية
@id/cast_button_type_mute_toggle كتم صوت جهاز الاستقبال وإعادته
@id/cast_button_type_closed_caption يفتح مربّع حوار لاختيار النصوص والمقاطع الصوتية.

فيما يلي مثال على استخدام صورة الألبوم وزر التبديل بين التشغيل والإيقاف المؤقت وزر التخطي للأمام بهذا الترتيب من اليسار إلى اليمين:

<array name="cast_mini_controller_control_buttons">
    <item>@id/cast_button_type_empty</item>
    <item>@id/cast_button_type_play_pause_toggle</item>
    <item>@id/cast_button_type_forward_30_seconds</item>
</array>
...
<fragment
    android:id="@+id/cast_mini_controller"
    ...
    app:castControlButtons="@array/cast_mini_controller_control_buttons"
    class="com.google.android.gms.cast.framework.media.widget.MiniControllerFragment">

تحذير: يجب أن يحتوي هذا الصفيف على ثلاثة عناصر فقط، وإلّا سيتم تجاهل وقت التشغيل. إذا كنت لا تريد عرض زر في خانة، استخدِم @id/cast_button_type_empty.

إضافة أزرار مخصصة

تتيح MiniControllerFragment إضافة أزرار تحكّم مخصّصة لا توفّرها حزمة تطوير البرامج (SDK)، مثل زر "التمرير السريع". الخطوات كالآتي:

  1. حدِّد خانة تحتوي على زر مخصّص باستخدام @id/cast_button_type_custom في السمة castControlButtons الخاصة بـ MiniControllerFragment.

  2. نفِّذ فئة فرعية من UIController. تتضمّن السمة UIController طرقًا تطلبها حزمة تطوير البرامج (SDK) عند تغيُّر حالة جلسة البث أو جلسة الوسائط. يجب أن تأخذ فئتك الفرعية من UIController ImageView كإحدى المَعلمات وتعدِّل حالتها حسب الحاجة.

  3. فئة فرعية MiniControllerFragment، ثم إلغاء onCreateView واستدعاء getButtonImageViewAt(int) للحصول على ImageView لهذا الزر المخصّص. بعد ذلك، يمكنك طلب الرمز bindViewToUIController(View, UIController) لربط العرض بالرمز المخصّص UIController.

  4. يمكنك الاطّلاع على MediaIntentReceiver في إضافة إجراءات مخصّصة لمعرفة كيفية التعامل مع الإجراء من الزرّ المخصّص.

    في ما يلي مثال على ربط زر في الفتحة 2 بـ UIController باسم MyCustomUIController:

// arrays.xml
<array name="cast_expanded_controller_control_buttons">
    <item>@id/cast_button_type_empty</item>
    <item>@id/cast_button_type_rewind_30_seconds</item>
    <item>@id/cast_button_type_custom</item>
    <item>@id/cast_button_type_empty</item>
</array>
لغة Kotlin
// MyCustomUIController.kt
class MyCustomUIController(private val mView: View) : UIController() {
    override fun onMediaStatusUpdated() {
        // Update the state of mView based on the latest the media status.
        ...
        mView.visibility = View.INVISIBLE
        ...
    }
}

// MyMiniControllerFragment.kt
class MyMiniControllerFragment : MiniControllerFragment() {
    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        super.onCreateView(inflater, container, savedInstanceState)
        val customButtonView = getButtonImageViewAt(2)
        val myCustomUiController = MyCustomUIController(customButtonView)
        uiMediaController.bindViewToUIController(customButtonView, myCustomUiController)
        ...
    }
}
Java
// MyCustomUIController.java
class MyCustomUIController extends UIController {
    private final View mView;

    public MyCustomUIController(View view) {
            mView = view;
    }

    @Override
    public onMediaStatusUpdated() {
        // Update the state of mView based on the latest the media status.
        ...
        mView.setVisibility(View.INVISIBLE);
        ...
    }
}

// MyMiniControllerFragment.java
class MyMiniControllerFragment extends MiniControllerFragment {
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
            Bundle savedInstanceState) {
        super.onCreateView(inflater, container, savedInstanceState);
        ImageView customButtonView = getButtonImageViewAt(2);
        MyCustomUIController myCustomUiController = new MyCustomUIController(customButtonView);
        getUIMediaController().bindViewToUIController(customButtonView, myCustomUiController);
        ...
    }
}

تخصيص وحدة التحكّم الموسّعة

تخصيص المظهر

إذا كان نشاط وحدة تحكم موسّعة يستخدم شريط أدوات للمظهر الداكن، فيمكنك ضبط مظهر على شريط الأدوات لاستخدام نص فاتح ولون رمز فاتح:

<style name="Theme.CastVideosTheme" parent="Theme.AppCompat.Light.NoActionBar">
    <item name="castExpandedControllerToolbarStyle">
        @style/ThemeOverlay.AppCompat.Dark.ActionBar
    </item>
</style>

يمكنك تحديد صورك الخاصة التي تُستخدم لرسم الأزرار على وحدة التحكم الموسعة:

<style name="CustomCastExpandedController" parent="CastExpandedController">
    <item name="castButtonColor">@null</item>
    <item name="castPlayButtonDrawable">@drawable/cast_ic_expanded_controller_play</item>
    <item name="castPauseButtonDrawable">@drawable/cast_ic_expanded_controller_pause</item>
    <item name="castStopButtonDrawable">@drawable/cast_ic_expanded_controller_stop</item>
    <item name="castSkipPreviousButtonDrawable">@drawable/cast_ic_expanded_controller_skip_previous</item>
    <item name="castSkipNextButtonDrawable">@drawable/cast_ic_expanded_controller_skip_next</item>
    <item name="castRewind30ButtonDrawable">@drawable/cast_ic_expanded_controller_rewind30</item>
    <item name="castForward30ButtonDrawable">@drawable/cast_ic_expanded_controller_forward30</item>
</style>

اختيار الأزرار

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

SLOT  SLOT  PLAY/PAUSE  SLOT  SLOT
  1     2     BUTTON      3     4

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

إليك مثال على وضع زر ترجيع في الخانة الثانية، وزر تخطٍ للأمام في الخانة الثالثة، وترك الخانتين الأولى والأخيرة فارغتين:

// arrays.xml
<array name="cast_expanded_controller_control_buttons">
    <item>@id/cast_button_type_empty</item>
    <item>@id/cast_button_type_rewind_30_seconds</item>
    <item>@id/cast_button_type_forward_30_seconds</item>
    <item>@id/cast_button_type_empty</item>
</array>
...
// styles.xml
<style name="Theme.MyTheme">
    <item name="castExpandedControllerStyle">
        @style/CustomCastExpandedController
    </item>
</style>
...
<style name="CustomCastExpandedController" parent="CastExpandedController">
    <item name="castControlButtons">
        @array/cast_expanded_controller_control_buttons
    </item>
</style>

يجب أن تحتوي الصفيفة على أربعة عناصر فقط، وإلا فسيتم طرح استثناء لبيئة التشغيل. إذا كنت لا تريد عرض زر في خانة، استخدِم @id/cast_button_type_empty. بإمكان "CastContext" إدارة مراحل النشاط وتقديمه لهذا النشاط.

إضافة أزرار مخصصة

تتيح ExpandedControllerActivity إضافة أزرار التحكّم المخصّصة التي لا توفّرها حزمة تطوير البرامج (SDK)، مثل زر "رفع الصوت". الخطوات كالآتي:

  1. حدِّد خانة تحتوي على زر مخصّص باستخدام @id/cast_button_type_custom في السمة castControlButtons الخاصة بـ ExpandedControllerActivity. يمكنك بعد ذلك استخدام getButtonImageViewAt(int) للحصول على ImageView لهذا الزرّ المخصّص.

  2. نفِّذ فئة فرعية من UIController. يحتوي UIController على طرق تطلبها حزمة تطوير البرامج (SDK) عند تغيُّر حالة جلسة البث أو جلسة الوسائط. يجب أن تستخدِم فئتك الفرعية من UIController ImageView كإحدى المَعلمات، وتعدِّل حالتها حسب الحاجة.

  3. فئة فرعية ExtendedControllerActivity، ثم تجاهُل onCreate واستدعاء getButtonImageViewAt(int) للحصول على كائن العرض الخاص بالزر. بعد ذلك، يمكنك طلب bindViewToUIController(View, UIController) لربط طريقة العرض مع مساحة التخزين المخصّصة UIController.

  4. يمكنك الاطّلاع على MediaIntentReceiver في إضافة إجراءات مخصّصة لمعرفة كيفية التعامل مع الإجراء من الزرّ المخصّص.

في ما يلي مثال على ربط زر في الفتحة 2 بـ UIController باسم MyCustomUIController:

// arrays.xml
<array name="cast_expanded_controller_control_buttons">
    <item>@id/cast_button_type_empty</item>
    <item>@id/cast_button_type_rewind_30_seconds</item>
    <item>@id/cast_button_type_custom</item>
    <item>@id/cast_button_type_empty</item>
</array>
كوتلين
// MyCustomUIController.kt
class MyCustomUIController(private val mView: View) : UIController() {
    override fun onMediaStatusUpdated() {
        // Update the state of mView based on the latest the media status.
        ...
        mView.visibility = View.INVISIBLE
        ...
    }
}

// MyExpandedControllerActivity.kt
internal class MyExpandedControllerActivity : ExpandedControllerActivity() {
    public override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        val customButtonView = getButtonImageViewAt(2)
        val myCustomUiController = MyCustomUIController(customButtonView)
        uiMediaController.bindViewToUIController(customButtonView, myCustomUiController)
        ...
    }
}
Java
// MyCustomUIController.java
class MyCustomUIController extends UIController {
    private final View mView;

    public MyCustomUIController(View view) {
        mView = view;
    }

    @Override
    public onMediaStatusUpdated() {
        // Update the state of mView based on the latest the media status.
        ...
        mView.setVisibility(View.INVISIBLE);
        ...
    }
}

// MyExpandedControllerActivity.java
class MyExpandedControllerActivity extends ExpandedControllerActivity {
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        ImageView customButtonView = getButtonImageViewAt(2);
        MyCustomUIController myCustomUiController = new MyCustomUIController(customButtonView);
        getUIMediaController().bindViewToUIController(customButtonView, myCustomUiController);
        ...
    }
}