家里的电视看不了CCTV直播?那我们就自己写一个吧

起因:

上周上班正在划水,忽然群里有个老司机说家里电视看不了直播了,之前充的会员也给退了;

作为一个热爱新闻联播死忠粉这怎么能忍受,既然这样那就自己手撸一个吧.

经过一系列的百度,google,调研了好几个晚上最终确认了两个方案

  1. 直接android原生webview内嵌CCTV官网直播这种方式简单,可是做好,电视遥控器交互性不是很好
  2. 自己撸一个原生的android tv

说干就干,本文采用第二种方案手撸一个tv app大概分为四个步骤

  • 首先打开AndroidStudio创建一个 android项目
  • 找了一个开源的播放器copy进来
  • 找了一个开源的直播源然后再copy进去
  • 然后将代码+资源,删删改改,完事,先上图展示一下

这里模拟了55寸720p和55寸1080P分辨率的电视

看起来还不错 下面进入正题纯干货,因为手机app跟电视app还是有差别的总结一下有以下几点

用户操作角度

  1. 电视遥控器操作只能上下左右+确定+返回
  2. 没有触摸,不用处理手机app手指的触摸问题

开发者角度

  1. 电视app每一步操作逻辑都是view获取焦点,失去焦点的操作
  2. 监听遥控器的按键事件
  3. 获取焦点后要有添加选中状态方便用户知道遥控器指到了哪里
  4. 页面布局+适配

下面我们具体看下开发者角度

google官方给我们提供了一套完整的开发tv的库以及官方文档先看看google提供的框架demo

设计风格还是有差异的 回到咱们手撸的电视app首页用到的控件是官方提供的leanback

java 复制代码
implementation 'androidx.leanback:leanback:1.0.0'
implementation 'androidx.leanback:leanback-preference:1.0.0'
implementation "androidx.leanback:leanback-tab:1.1.0-beta01"

首页是LeanbackTabLayout+LeanbackViewPager跟手机app用法一致

js 复制代码
<androidx.leanback.tab.LeanbackTabLayout
    android:id="@+id/tab"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    app:layout_constraintLeft_toLeftOf="parent"
    app:layout_constraintTop_toTopOf="parent"
    app:tabBackground="@color/home_color"
    app:tabGravity="start"
    android:layout_marginStart="12dp"
    app:tabIndicatorColor="@color/color_CF3231"
    app:tabMode="scrollable"
    app:tabSelectedTextColor="@color/color_CF3231"
    app:tabTextAppearance="@android:style/TextAppearance.Holo.Large"
    app:tabTextColor="@color/white" />

<androidx.leanback.tab.LeanbackViewPager
    android:id="@+id/viewPager"
    android:nextFocusUp="@+id/tab"
    android:layout_width="match_parent"
    android:descendantFocusability="afterDescendants"
    android:layout_height="0dp"
    app:layout_constraintBottom_toBottomOf="parent"
    app:layout_constraintTop_toBottomOf="@+id/tab" />

看了下源码发现leanbackTabLayout内部已经帮我们处理好了焦点问题

js 复制代码
public void addFocusables(@SuppressLint("ConcreteCollection") @NonNull ArrayList<View> views,
        int direction, int focusableMode) {

    boolean isViewPagerFocused = this.mViewPager != null && this.mViewPager.hasFocus();
    boolean isCurrentlyFocused = this.hasFocus();
    LinearLayout tabStrip = (LinearLayout) this.getChildAt(0);
    if ((direction == View.FOCUS_DOWN || direction == View.FOCUS_UP)
            && tabStrip != null && tabStrip.getChildCount() > 0 && this.mViewPager != null) {
        View selectedTab =  tabStrip.getChildAt(this.mViewPager.getCurrentItem());
        if (selectedTab != null) {
            views.add(selectedTab);
        }
    } else if ((direction == View.FOCUS_RIGHT || direction == View.FOCUS_LEFT)
            && !isCurrentlyFocused && isViewPagerFocused) {
        return;
    } else {
        super.addFocusables(views, direction, focusableMode);
    }
}

void updatePageTabs() {
    LinearLayout tabStrip = (LinearLayout) this.getChildAt(0);

    if (tabStrip == null) {
        return;
    }

    int tabCount = tabStrip.getChildCount();
    for (int i = 0; i < tabCount; i++) {
        final View tabView = tabStrip.getChildAt(i);
        tabView.setFocusable(true);
        tabView.setOnFocusChangeListener(
                new TabFocusChangeListener(this, this.mViewPager));
    }
}

播放页面

js 复制代码
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@color/black"
    android:gravity="center"
    android:orientation="vertical">

    <com.cookiecatspop.ceop.main.CusVideoPlayer
        android:id="@+id/mVideoPlayer"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />

    <androidx.leanback.widget.VerticalGridView
        android:id="@+id/verticalGridview"
        android:layout_width="270dp"
        android:layout_height="match_parent"
        android:nextFocusLeft="@id/verticalGridview"
        android:nextFocusRight="@id/verticalGridview"
        tools:listitem="@layout/item_address" />
</FrameLayout>

这里用到了CusVideoPlayer(这个是自己基于开源库封装的电视专用的播放器)和VerticalGridView(这个也是官方提供的组件用来显示列表父类是recycleView,所以用法跟recycleView差不多)

好了今天就到这里了我要去看成人频道了

相关推荐
工程师老罗27 分钟前
如何在Android工程中配置NDK版本
android
Libraeking4 小时前
破壁行动:在旧项目中丝滑嵌入 Compose(混合开发实战)
android·经验分享·android jetpack
市场部需要一个软件开发岗位4 小时前
JAVA开发常见安全问题:Cookie 中明文存储用户名、密码
android·java·安全
JMchen1236 小时前
Android后台服务与网络保活:WorkManager的实战应用
android·java·网络·kotlin·php·android-studio
crmscs7 小时前
剪映永久解锁版/电脑版永久会员VIP/安卓SVIP手机永久版下载
android·智能手机·电脑
localbob7 小时前
杀戮尖塔 v6 MOD整合版(Slay the Spire)安卓+PC端免安装中文版分享 卡牌肉鸽神作!杀戮尖塔中文版,电脑和手机都能玩!杀戮尖塔.exe 杀戮尖塔.apk
android·杀戮尖塔apk·杀戮尖塔exe·游戏分享
机建狂魔7 小时前
手机秒变电影机:Blackmagic Camera + LUT滤镜包的专业级视频解决方案
android·拍照·摄影·lut滤镜·拍摄·摄像·录像
hudawei9967 小时前
flutter和Android动画的对比
android·flutter·动画
lxysbly9 小时前
md模拟器安卓版带金手指2026
android
儿歌八万首9 小时前
硬核春节:用 Compose 打造“赛博鞭炮”
android·kotlin·compose·春节