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

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

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

ينشئ هذا المثال نمط مظهر مخصّص 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>

أعلِن عن مظهر Media Router المخصّص وأعلِن عن 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. أنشئ فئة فرعية من ExpandedControllerActivity، ثم تجاوَز 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>
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
        ...
    }
}

// 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);
        ...
    }
}