Я пытался добавить google-cast в свое приложение для Android, которое воспроизводит видео с помощью Exoplayer. Приложение имеет одно действие с темойTheme.Leanback
, и один и тот же пользовательский интерфейс используется как для телефонов, так и для Android TV.
Чтобы добавить поддержку трансляции на телефонах, я изменил вид проигрывателя и добавил кнопку трансляции, как показано ниже:
<androidx.mediarouter.app.MediaRouteButton
android:id = "@+id/media_route_button"
android:layout_width = "wrap_content"
android:layout_height = "wrap_content"
android:layout_gravity = "left"
android:scaleY = "0.8"
android:scaleX = "0.8"
android:background = "@android:color/transparent"
android:mediaRouteTypes = "user"
android:visibility = "visible" />
Когда я запускаю приложение, я получаю следующую ошибку:
E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.EXAMPLE.PACKAGE, PID: 10661
android.view.InflateException: Binary XML file line #76 in com.EXAMPLE.PACKAGE:layout/activity_player: Binary XML file line #76 in com.EXAMPLE.PACKAGE:layout/activity_player: Error inflating class androidx.mediarouter.app.MediaRouteButton
Caused by: android.view.InflateException: Binary XML file line #76 in com.EXAMPLE.PACKAGE:layout/activity_player: Error inflating class androidx.mediarouter.app.MediaRouteButton
Caused by: java.lang.reflect.InvocationTargetException
at java.lang.reflect.Constructor.newInstance0(Native Method)
at java.lang.reflect.Constructor.newInstance(Constructor.java:343)
at android.view.LayoutInflater.createView(LayoutInflater.java:852)
at android.view.LayoutInflater.createViewFromTag(LayoutInflater.java:1004)
at android.view.LayoutInflater.createViewFromTag(LayoutInflater.java:959)
at android.view.LayoutInflater.rInflate(LayoutInflater.java:1121)
at android.view.LayoutInflater.rInflateChildren(LayoutInflater.java:1082)
at android.view.LayoutInflater.rInflate(LayoutInflater.java:1124)
at android.view.LayoutInflater.rInflateChildren(LayoutInflater.java:1082)
at android.view.LayoutInflater.rInflate(LayoutInflater.java:1124)
at android.view.LayoutInflater.rInflateChildren(LayoutInflater.java:1082)
at android.view.LayoutInflater.rInflate(LayoutInflater.java:1124)
at android.view.LayoutInflater.rInflateChildren(LayoutInflater.java:1082)
at android.view.LayoutInflater.inflate(LayoutInflater.java:680)
at android.view.LayoutInflater.inflate(LayoutInflater.java:532)
at com.EXAMPLE.PACKAGE.fragment.PlayerFragment.onCreateView(PlayerFragment.java:162)
at androidx.fragment.app.Fragment.performCreateView(Fragment.java:2963)
at androidx.fragment.app.FragmentStateManager.createView(FragmentStateManager.java:518)
at androidx.fragment.app.FragmentStateManager.moveToExpectedState(FragmentStateManager.java:282)
at androidx.fragment.app.FragmentManager.executeOpsTogether(FragmentManager.java:2189)
at androidx.fragment.app.FragmentManager.removeRedundantOperationsAndExecute(FragmentManager.java:2100)
at androidx.fragment.app.FragmentManager.execPendingActions(FragmentManager.java:2002)
at androidx.fragment.app.FragmentManager$5.run(FragmentManager.java:524)
at android.os.Handler.handleCallback(Handler.java:938)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loop(Looper.java:223)
at android.app.ActivityThread.main(ActivityThread.java:7656)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:592)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:947)
Caused by: java.lang.IllegalArgumentException: background can not be translucent: #0
at androidx.core.graphics.ColorUtils.calculateContrast(ColorUtils.java:161)
at androidx.mediarouter.app.MediaRouterThemeHelper.getControllerColor(MediaRouterThemeHelper.java:177)
at androidx.mediarouter.app.MediaRouterThemeHelper.getRouterThemeId(MediaRouterThemeHelper.java:313)
at androidx.mediarouter.app.MediaRouterThemeHelper.createThemedButtonContext(MediaRouterThemeHelper.java:107)
at androidx.mediarouter.app.MediaRouteButton.<init>(MediaRouteButton.java:153)
at androidx.mediarouter.app.MediaRouteButton.<init>(MediaRouteButton.java:149)
at java.lang.reflect.Constructor.newInstance0(Native Method)
at java.lang.reflect.Constructor.newInstance(Constructor.java:343)
at android.view.LayoutInflater.createView(LayoutInflater.java:852)
at android.view.LayoutInflater.createViewFromTag(LayoutInflater.java:1004)
at android.view.LayoutInflater.createViewFromTag(LayoutInflater.java:959)
at android.view.LayoutInflater.rInflate(LayoutInflater.java:1121)
at android.view.LayoutInflater.rInflateChildren(LayoutInflater.java:1082)
at android.view.LayoutInflater.rInflate(LayoutInflater.java:1124)
at android.view.LayoutInflater.rInflateChildren(LayoutInflater.java:1082)
at android.view.LayoutInflater.rInflate(LayoutInflater.java:1124)
at android.view.LayoutInflater.rInflateChildren(LayoutInflater.java:1082)
at android.view.LayoutInflater.rInflate(LayoutInflater.java:1124)
at android.view.LayoutInflater.rInflateChildren(LayoutInflater.java:1082)
at android.view.LayoutInflater.inflate(LayoutInflater.java:680)
at android.view.LayoutInflater.inflate(LayoutInflater.java:532)
at com.EXAMPLE.PACKAGE.fragment.PlayerFragment.onCreateView(PlayerFragment.java:162)
at androidx.fragment.app.Fragment.performCreateView(Fragment.java:2963)
at androidx.fragment.app.FragmentStateManager.createView(FragmentStateManager.java:518)
at androidx.fragment.app.FragmentStateManager.moveToExpectedState(FragmentStateManager.java:282)
at androidx.fragment.app.FragmentManager.executeOpsTogether(FragmentManager.java:2189)
at androidx.fragment.app.FragmentManager.removeRedundantOperationsAndExecute(FragmentManager.java:2100)
at androidx.fragment.app.FragmentManager.execPendingActions(FragmentManager.java:2002)
at androidx.fragment.app.FragmentManager$5.run(FragmentManager.java:524)
at android.os.Handler.handleCallback(Handler.java:938)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loop(Looper.java:223)
at android.app.ActivityThread.main(ActivityThread.java:7656)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:592)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:947)
Я попытался сделать фон кастбустона прозрачным, как предлагалось в нескольких местах в Интернете, но это не помогло.
У меня может быть несколько других кнопок изображения и текста, которые работают нормально.
Я пробовал использовать <MediaRouteButton
вместо <androidx.mediarouter.app.MediaRouteButton
. Таким образом, кнопка будет отображаться и выделена серым цветом в макете проигрывателя, однако, когда я пытаюсь настроить контекст приведения, как показано ниже:
mMediaRouteButton = (MediaRouteButton) mView.findViewById(R.id.media_route_button);
CastButtonFactory.setUpMediaRouteButton(mContext, mMediaRouteButton);
mCastContext = CastContext.getSharedInstance(mContext);
Я получаю следующую ошибку:
java.lang.ClassCastException: android.app.MediaRouteButton cannot be cast to androidx.mediarouter.app.MediaRouteButton
I have been puzzled by this issue for couple of days. Thank you very much in advance for your kind comments and suggestion.
Убедитесь, что вы включили зависимость, содержащую этот класс, в список зависимостей вашего модуля, например:
implementation "androidx.mediarouter:mediarouter:1.2.6"
(или другая версия по необходимости)
Знаете ли вы, можно ли добавить MediaRouteButton при использовании Theme.Leanback
?
@krahimi: Казалось бы, странная комбинация. Theme.Leanback
обычно используется на телевизоре; MediaRouteButton
будет использоваться на телевизоре без телевизора. Тем не менее, я бы не ожидал краха. Есть ли что-то еще в вашей трассировке стека, например, больше разделов «Вызвано» после той части, которую вы опубликовали?
Это правда, в настоящее время приложение использует одну тему как для телевизора, так и для телефонов. Нет, это вся трассировка стека, которую я вижу.
Я также попробовал контекстную оболочку при раздувании View: Context app_compat__context = new ContextThemeWrapper(mContext, R.style.Theme_AppCompat); inflater.cloneInContext(app_compat__context);
, но я все еще вижу ту же ошибку.
Поскольку MediaRouteButton
, который вы используете, находится в библиотеке, вы должны иметь возможность поставить точку останова в его конструкторе. Посмотрите, попали ли вы в него, и посмотрите, как далеко вы сможете пройти через него. InvocationTargetException
иногда означает, что класс или метод не public
, но здесь это не так. Это также иногда означает, что вы где-то потерпели крах в вызываемом методе — в идеале это должно отображаться как еще один раздел «Причинено», но если вы этого не понимаете, возможно, отладка покажет, где что-то идет не так.
@ CommonsWare: Большое спасибо. Я только что понял, что часть трассировки стека была свернута, чего я раньше не замечал. Извини за это. Я только что обновил трассировку стека. Я также попытаюсь пройти через код.
Похоже, проблема вызвана здесь: Caused by: java.lang.IllegalArgumentException: background can not be translucent: #0
Казалось бы, это соответствует android:background = "@android:color/transparent"
. Если вы удалите это, вы получите другой сбой?
Оказывается, у основного цвета темы не должно быть прозрачного канала. После внесения этого изменения он работает нормально. Большое спасибо за подсказки
Большой! Возможно, вы захотите опубликовать свой собственный ответ на свой вопрос с подробностями того, что вы изменили!
Обязательно сделаю. Спасибо еще раз
Проблема заключалась в том, что primaryColor
темы приложения, которую я использовал, имел канал прозрачности, то есть #3d000000
. Однако MediaRouteButton, который использует primaryColor
темы, нуждается в цвете без канала прозрачности, таком как #000000
. Итак, я решил проблему следующим образом:
<style name = "AppTheme" parent = "@style/Theme.Leanback">
<!-- Primary Color -->
<item name = "colorPrimary">@color/defaultColor</item>
</style>
где я определил <color name = "defaultColor">#000000</color>
Позже я столкнулся с другой проблемой, что MediaCastButton можно использовать только с AppCompat
наследственной темой. Решение для меня состояло в том, чтобы изменить тему приложения на тему Theme.AppCompat.Light.NoActionBar
и изменить ее, чтобы она выглядела как Theme.Leanback
, как показано ниже:
<style name = "AppTheme1" parent = "@style/Theme.AppCompat.Light.NoActionBar">
<item name = "colorPrimary">@color/blackColor</item>
<item name = "android:windowBackground">@color/blackColor</item>
<!-- <item name = "mediaRouteButtonTint">@color/whiteColor</item>-->
<item name = "baseCardViewStyle">@style/Widget.Leanback.BaseCardViewStyle</item>
<item name = "imageCardViewStyle">@style/Widget.Leanback.ImageCardViewStyle</item>
<item name = "imageCardViewImageStyle">@style/Widget.Leanback.ImageCardView.ImageStyle</item>
<item name = "imageCardViewTitleStyle">@style/Widget.Leanback.ImageCardView.TitleStyle</item>
<item name = "imageCardViewContentStyle">@style/Widget.Leanback.ImageCardView.ContentStyle</item>
<item name = "imageCardViewBadgeStyle">@style/Widget.Leanback.ImageCardView.BadgeStyle</item>
<item name = "imageCardViewInfoAreaStyle">@style/Widget.Leanback.ImageCardView.InfoAreaStyle</item>
<item name = "browsePaddingStart">@dimen/lb_browse_padding_start</item>
<item name = "browsePaddingEnd">@dimen/lb_browse_padding_end</item>
<item name = "browsePaddingTop">@dimen/lb_browse_padding_top</item>
<item name = "browsePaddingBottom">@dimen/lb_browse_padding_bottom</item>
<item name = "browseRowsMarginStart">@dimen/lb_browse_rows_margin_start</item>
<item name = "browseRowsMarginTop">@dimen/lb_browse_rows_margin_top</item>
<item name = "browseRowsFadingEdgeLength">@dimen/lb_browse_rows_fading_edge</item>
<!-- <item name = "headersVerticalGridStyle">@style/Widget.Leanback.Headers.VerticalGridView</item>-->
<!-- <item name = "headerStyle">@style/Widget.Leanback.Header</item>-->
<!-- <item name = "sectionHeaderStyle">@style/Widget.Leanback.Header.Section</item>-->
<item name = "rowsVerticalGridStyle">@style/Widget.Leanback.Rows.VerticalGridView</item>
<item name = "rowHorizontalGridStyle">@style/Widget.Leanback.Row.HorizontalGridView</item>
<item name = "itemsVerticalGridStyle">@style/Widget.Leanback.GridItems.VerticalGridView</item>
<item name = "browseTitleViewLayout">@layout/lb_browse_title</item>
<item name = "browseTitleTextStyle">@style/Widget.Leanback.Title.Text</item>
<item name = "browseTitleIconStyle">@style/Widget.Leanback.Title.Icon</item>
<item name = "browseTitleViewStyle">@style/Widget.Leanback.TitleView</item>
<item name = "rowHeaderStyle">@style/Widget.Leanback.Row.Header</item>
<item name = "rowHeaderDescriptionStyle">@style/Widget.Leanback.Row.Header.Description</item>
<item name = "rowHoverCardTitleStyle">@style/Widget.Leanback.Row.HoverCardTitle</item>
<item name = "rowHoverCardDescriptionStyle">@style/Widget.Leanback.Row.HoverCardDescription</item>
<item name = "rowHeaderDockStyle">@style/Widget.Leanback.Row.HeaderDock</item>
<!-- <item name = "searchOrbViewStyle">@style/Widget.Leanback.SearchOrbViewStyle</item>-->
<item name = "detailsDescriptionTitleStyle">@style/Widget.Leanback.DetailsDescriptionTitleStyle</item>
<item name = "detailsDescriptionSubtitleStyle">@style/Widget.Leanback.DetailsDescriptionSubtitleStyle</item>
<item name = "detailsDescriptionBodyStyle">@style/Widget.Leanback.DetailsDescriptionBodyStyle</item>
<item name = "detailsActionButtonStyle">@style/Widget.Leanback.DetailsActionButtonStyle</item>
<!-- Attributes used for styling of a playback -->
<item name = "playbackPaddingStart">@dimen/lb_playback_controls_margin_start</item>
<item name = "playbackPaddingEnd">@dimen/lb_playback_controls_margin_end</item>
<item name = "playbackMediaItemPaddingStart">@dimen/lb_playback_media_row_horizontal_padding</item>
<item name = "playbackMediaListHeaderStyle">@style/Widget.Leanback.PlaybackMediaListHeaderStyle</item>
<item name = "playbackMediaItemRowStyle">@style/Widget.Leanback.PlaybackMediaItemRowStyle</item>
<item name = "playbackMediaItemSeparatorStyle">@style/Widget.Leanback.PlaybackMediaItemSeparatorStyle</item>
<item name = "playbackMediaListHeaderTitleStyle">@style/Widget.Leanback.PlaybackMediaListHeaderTitleStyle</item>
<item name = "playbackMediaItemDetailsStyle">@style/Widget.Leanback.PlaybackMediaItemDetailsStyle</item>
<item name = "playbackMediaItemNumberViewFlipperStyle">@style/Widget.Leanback.PlaybackMediaItemNumberViewFlipperStyle</item>
<item name = "playbackMediaItemNumberViewFlipperLayout">@layout/lb_media_item_number_view_flipper</item>
<item name = "playbackMediaItemNumberStyle">@style/Widget.Leanback.PlaybackMediaItemNumberStyle</item>
<item name = "playbackMediaItemNameStyle">@style/Widget.Leanback.PlaybackMediaItemNameStyle</item>
<item name = "playbackMediaItemDurationStyle">@style/Widget.Leanback.PlaybackMediaItemDurationStyle</item>
<item name = "playbackControlsButtonStyle">@style/Widget.Leanback.PlaybackControlsButtonStyle</item>
<item name = "playbackControlButtonLabelStyle">@style/Widget.Leanback.PlaybackControlLabelStyle</item>
<item name = "playbackControlsTimeStyle">@style/Widget.Leanback.PlaybackControlsTimeStyle</item>
<item name = "playbackControlsActionIcons">@style/Widget.Leanback.PlaybackControlsActionIconsStyle</item>
<item name = "playbackControlsAutoHideTimeout">@integer/lb_playback_controls_show_time_ms</item>
<item name = "playbackControlsAutoHideTickleTimeout">@integer/lb_playback_controls_tickle_timeout_ms</item>
<item name = "errorMessageStyle">@style/Widget.Leanback.ErrorMessageStyle</item>
<item name = "defaultSearchColor">@color/lb_default_search_color</item>
<item name = "defaultSearchIconColor">@color/lb_default_search_icon_color</item>
<item name = "defaultSearchBrightColor">?attr/defaultSearchColor</item>
<item name = "defaultSearchIcon">@drawable/lb_ic_in_app_search</item>
<item name = "defaultSectionHeaderColor">?attr/defaultSearchColor</item>
<item name = "overlayDimMaskColor">@color/lb_view_dim_mask_color</item>
<item name = "overlayDimActiveLevel">@fraction/lb_view_active_level</item>
<item name = "overlayDimDimmedLevel">@fraction/lb_view_dimmed_level</item>
</style>
Большое спасибо за ваш добрый ответ. Я уже включил эту зависимость.