TV(電視)應用開發指南

前陣子將一個手機APP改爲TV應用,由於首次開發TV,故把開發過程中的一些問題記錄下來,以備不時之需。電視應用和手機應用開發過程大同小異,電視應用主要注意三個地方:1是清單文件,2是佈局文件,3是處理好控件獲取焦點時的背景顯示,因爲對於沒有觸控功能的電視設備,用戶想要點擊某個控件時,只能先操作遙控器的方向鍵將焦點移到該控件上,接着才能按遙控器的確定鍵執行點擊,所以就需要處理好控件獲取焦點時的背景顯示(區別於沒有獲取焦點的其他控件),以便用戶能一眼看出來現在是哪個控件在獲取焦點並可點擊。

1.清單文件需要改的地方

1.1 處理電視可能不支持的硬件功能

電視上的android系統一般不支持以下硬件功能:
在這裏插入圖片描述
因此,需要在清單文件中將以上特性聲明爲非必須的,你的應用才能安裝在不支持這些特性的電視上,如下:

<uses-feature android:name="android.hardware.touchscreen"
            android:required="false"/>
    <uses-feature android:name="android.hardware.faketouch"
            android:required="false"/>
    <uses-feature android:name="android.hardware.telephony"
            android:required="false"/>
    <uses-feature android:name="android.hardware.camera"
            android:required="false"/>
    <uses-feature android:name="android.hardware.nfc"
            android:required="false"/>
    <uses-feature android:name="android.hardware.location.gps"
            android:required="false"/>
    <uses-feature android:name="android.hardware.microphone"
            android:required="false"/>
    <uses-feature android:name="android.hardware.sensor"
            android:required="false"/>

當然,如果你的應用的某個功能需要用到以上某個特性,你應該在代碼中判斷設備是否支持改特性,以便做出對應的處理邏輯,怎麼判斷呢:

是否支持撥打電話:

if (getPackageManager().hasSystemFeature(PackageManager.FEATURE_TELEPHONY)) {
       //支持電話撥打
    }

是否觸摸屏

if (getPackageManager().hasSystemFeature(PackageManager.FEATURE_TOUCHSCREEN)) {
        //是觸摸屏
    }

是否可開啓相機

if (getPackageManager().hasSystemFeature(PackageManager.FEATURE_CAMERA)) {
        //可開啓相機
    } 

其他特性判斷就不再贅述。

1.2 application節點下

在這裏插入圖片描述
如上圖,應用的啓動圖除了提供常規的手機APP的logo外,還需要另外提供一張橫幅圖片放在banner屬性下,什麼是橫幅?請看下圖:
在這裏插入圖片描述
橫幅圖片分辨率一般建議320*180的,放在xhdpi的圖片資源目錄下。

另外,當 TV 應用啓動時,系統會顯示動畫,就像一個不斷膨脹的實心圓。要自定義此動畫的顏色,請將 TV 應用或 Activity 的 android:colorPrimary 屬性設爲特定顏色。此外,還應將另外兩個過渡重疊屬性設爲 true,如主題背景資源 XML 文件中的以下代碼段所示:

<resources>
        <style ... >
          <item name="android:colorPrimary">@color/primary</item>
          <item name="android:windowAllowReturnTransitionOverlap">true</item>
          <item name="android:windowAllowEnterTransitionOverlap">true</item>
        </style>
    </resources>

1.3 啓動頁

電視應用的啓動頁需要將intent-filter的categorycategory值從LAUNCHER改爲LEANBACK_LAUNCHER:

<intent-filter>
                <action android:name="android.intent.action.MAIN"/>
                <category android:name="android.intent.category.LEANBACK_LAUNCHER"/>
            </intent-filter>

否則,你的應用將不會在電視桌面或橫幅列表上顯示出來,反正,LEANBACK_LAUNCHER的電視應用也不會在手機桌面顯示。

1.4 啓動activity的config配置

config屬性需要配置navigation值,因爲當用戶在操作鍵盤方向鍵切換導航時,activity是會重走生命週期方法的,這是爲了不讓其重走生命週期方法:
在這裏插入圖片描述
另外,電視應用不支持豎屏顯示,所以我們可以直接在清單文件中將activity的屏幕方向限定爲橫屏.

2.佈局文件

2.1 由於電視應用都是橫屏顯示,所以界面佈局文件要放在layout-land目錄下。

2.2 電視沒有狀態欄,所以應用主題最好用NoActionBar的。

2.3 對於沒有觸控功能的電視,fragment的切換最好不要藉助Viewpage來管理

2.4 對於界面內容過長時,根佈局最好用NestedScrollView

3.處理焦點

3.1 爲了控件能響應遙控器的方向鍵切換時獲取焦點,需要將控件的focusable屬性設爲true,這樣設置過後,你會發現當將應用裝在具有觸控功能的設備時,點擊這個控件時,需要點擊兩次才執行,這是因爲控件要先走focused,才走onclick,爲了避免這種情況,再將focusable屬性設爲true後,記得同時顯性的將控件的clickable也設爲true。

3.2 對於幾個同一級別或類型的ImageView,爲了區分當前是哪張圖片獲得焦點,可以用放大的方式將獲取焦點圖片區別出來,如下圖:

在這裏插入圖片描述
仔細看上圖,“撥號”圖片當前是獲取焦點的,它相對於其他三張圖片放大了。要想圖片獲取焦點時放大,不獲取焦點時縮小到正常水平,其實只需要重寫AppCompatImageView的onFocusChanged方法即可:

/**
 * @author Administrator
 * @time 2019/9/5 17:12
 * @des ${TODO}
 * @updateAuthor $Author$
 * @updateDate $Date$
 * @updateDes ${TODO}
 * 獲取焦點放大的ImageView
 */
public class ScaleWithFocusImageView extends AppCompatImageView {

    private FocusedListener mFocusedListener;

    public ScaleWithFocusImageView(Context context) {
        super(context);
    }

    public ScaleWithFocusImageView(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
    }

    @Override
    protected void onFocusChanged(boolean gainFocus, int direction,
                                  @Nullable Rect previouslyFocusedRect) {
        super.onFocusChanged(gainFocus, direction, previouslyFocusedRect);

        ViewCompat.animate(this)
                .scaleX(gainFocus ? 1.3f : 1.0f)//x軸方向的縮放
                .scaleY(gainFocus ? 1.3f : 1.0f)//y軸方向的縮放
                .translationZ(gainFocus ? 1f : 0f)//z軸方向的移動
                .start();

        if (mFocusedListener != null) {
            mFocusedListener.focused(gainFocus);
        }
    }

    public void setFocusedListener(FocusedListener focusedListener) {
        mFocusedListener = focusedListener;
    }

}

3.3 對於幾個同一級別或類型的TextView,區分的方式可以包括縮放、文字顏色等,看下圖:

在這裏插入圖片描述
上圖的“撥號”和“通訊錄”分別是兩個Fragment的導航標題,當前獲取焦點的是“撥號”的TextView,獲取焦點時放大了,並且文字顏色爲純白色,相較於“通訊錄”,還是很容易看得出來的。下面是重新改TextView的代碼

public class FocuseScaleRadiubutton extends AppCompatRadioButton {

    private int mWhiteColor;
    private int mWhiteColor70Transp;

    public FocuseScaleRadiubutton(Context context) {
        super(context);
        init();
    }

    public FocuseScaleRadiubutton(Context context, AttributeSet attrs) {
        super(context, attrs);
        init();
    }

    private void init() {
        mWhiteColor = getResources().getColor(R.color.color_white);
        mWhiteColor70Transp = getResources().getColor(R.color.color_70_transp_white);
    }

    @Override
    protected void onFocusChanged(boolean focused, int direction, Rect previouslyFocusedRect) {
        super.onFocusChanged(focused, direction, previouslyFocusedRect);
        ViewCompat.animate(this)
                .scaleX(focused ? 1.15f : 1.0f)
                .scaleY(focused ? 1.15f : 1.0f)
                .translationZ(focused ? 1.0f : 0f)
                .start();
        setTextColor(focused ? mWhiteColor : mWhiteColor70Transp);
    }
}

3.4 區別RecyclerView中的哪個Item獲取焦點

同樣的,爲了時recyclerview的itemitem能夠獲取焦點,需要給item的佈局根節點設置focusable爲true,clickable爲true。還是看上面那個撥號界面:
在這裏插入圖片描述
當前獲取焦點的是RecyclerView中的第二個item,我這裏是顯示了一個紅色的線框來區別,當然,你也可以對這個item放大來區別,或者周邊高亮陰影來區別。下面代碼是該item中的狀態背景:

<selector xmlns:android="http://schemas.android.com/apk/res/android">
    <!--獲取焦點時的背景-->
    <item android:drawable="@drawable/shape_rec_red_stroke" android:state_focused="true"/>
    <!--按壓時的背景-->
    <item android:drawable="@drawable/shape_rec_red_stroke" android:state_pressed="true"/>
    <!--焦點掠過時的背景-->
    <item android:drawable="@drawable/shape_rec_red_stroke" android:state_hovered="true"/>
    <!--失去焦點時的背景-->
    <item android:drawable="@drawable/item_tv_create_video_meeting"/>
</selector>

3.5 監聽用戶是否按下了遙控

這個場景主要是用於當監聽到用戶按下了遙控建時做對應的邏輯,例如視頻播放界面,當全屏播放時,用戶一段時間不操作遙控後,就隱藏某些按鈕,當用戶按下遙控時,再顯示隱藏的按鈕,只需要重寫Activity的onKeyDown方法即可:

@Override
    public boolean onKeyDown(int keyCode, KeyEvent event) {

        if ((event.getSource() & InputDevice.SOURCE_DPAD)
                != InputDevice.SOURCE_DPAD) {
            //用戶按了遙控
        }
            return super.onKeyDown(keyCode, event);
    }

3.5 處理導航

一般不需要處理用戶操作遙控器方向鍵(上、下、左、右)時的導航順序,如果有這個需求,可以根據下面屬性在佈局文件中指定你的導航順序:
在這裏插入圖片描述
如果你想在最後一個控件獲取焦點後,繼續按右方向鍵的話導航到第一個控件,那麼你只需要將最後一個控件中nextFocusRight的值設爲第一個控件的id即可。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章