從門外漢到開(kāi)發(fā)出AndroidTV應(yīng)用,我只用了三天(android tv app開(kāi)發(fā))
背景
在這之前,本人一直從事Android手機(jī)應(yīng)用層的開(kāi)發(fā)。從沒(méi)有接觸過(guò)Android TV端的開(kāi)發(fā)工作。當(dāng)領(lǐng)導(dǎo)問(wèn)能否在一周內(nèi)做出一個(gè)TV端Demo時(shí),自己心里還很是比較虛的。但是實(shí)際上,從開(kāi)始調(diào)研到動(dòng)手做到最后將Demo給領(lǐng)導(dǎo)演示出來(lái),花了不超過(guò)3天的時(shí)間。當(dāng)然這三天,每天都是做到晚上11點(diǎn)以后的(大部分時(shí)間是解決一些細(xì)節(jié)問(wèn)題)。功能相當(dāng)于是KEEP訓(xùn)練跟練的TV版,業(yè)務(wù)交互還是有一些難點(diǎn)的。當(dāng)領(lǐng)導(dǎo)對(duì)你的Demo表示認(rèn)可后,接下來(lái)等產(chǎn)品和設(shè)計(jì)師完整的出了TV端產(chǎn)品的整個(gè)UI和設(shè)計(jì),我們就開(kāi)始正式的進(jìn)入TV端產(chǎn)品的搭建過(guò)程。
Android TV當(dāng)然也是Android的系統(tǒng),所以盡管我們沒(méi)有任何開(kāi)發(fā)TV的經(jīng)驗(yàn),我們?nèi)匀荒軌驊{借開(kāi)發(fā)手機(jī)應(yīng)用的經(jīng)驗(yàn)在開(kāi)發(fā)TV應(yīng)用時(shí)也能得心應(yīng)手。但是,在TV端開(kāi)發(fā)和手機(jī)也是不同的,下面就把我的一點(diǎn)小經(jīng)驗(yàn)分享給大家。
連接開(kāi)發(fā)設(shè)備
開(kāi)發(fā)TV的項(xiàng)目,最好使用機(jī)頂盒或者智能電視,當(dāng)然也可以使用模擬器(推薦:網(wǎng)易MuMu)。當(dāng)你使用盒子(機(jī)頂盒以下均稱盒子)作為開(kāi)發(fā)設(shè)備時(shí),你需要將你的開(kāi)發(fā)電腦與盒子進(jìn)行連接。一般來(lái)說(shuō),不同的盒子的連接方式不一樣。以樂(lè)視和天貓的盒子為例,我們需要將盒子和開(kāi)發(fā)設(shè)備連在同一局域網(wǎng)中,并查看盒子的IP地址(假如為192.168.2.17)。
接下來(lái)你可以使用下面的adb命令連接盒子:
java adb connect 192.168.2.17
這個(gè)命令相當(dāng)于我們開(kāi)發(fā)手機(jī)應(yīng)用時(shí)連接手機(jī)的過(guò)程,如果連接失敗,你可以檢查一下是否在同一個(gè)局域網(wǎng),IP地址是否正確,端口號(hào)是否正確。注意,有些盒子在連接的時(shí)候需要加上端口號(hào)的,有些不用指定端口號(hào),因?yàn)槟J(rèn)使用的是5555。如果是小米的盒子可以直接用USB線連接盒子和電腦就可以了,當(dāng)然盒子也是有開(kāi)發(fā)者模式的,在我們安裝我們的APP之前,先要允許通過(guò)adb安裝APK。
新建TV項(xiàng)目
使用AndroidStudio新建一個(gè)TV Project,或者如果是基于你們公司移動(dòng)產(chǎn)品做一個(gè)TV版,你完全可以將一些公共組件抽取出來(lái),對(duì)于TV新建一個(gè)Module依賴于這些基礎(chǔ)組件,這樣會(huì)為TV項(xiàng)目節(jié)省很多成本。開(kāi)發(fā)TV應(yīng)用的時(shí)候,可以借助谷歌的一個(gè)庫(kù):Leanback。谷歌官方也提供非常豐富的Demo頁(yè)面,當(dāng)然你的產(chǎn)品設(shè)計(jì)可能沒(méi)法完全使用谷歌提供的demo頁(yè)面,但是Leanback中也提供了一些好用的控件:HorizontalGridView、VerticalGridView等等。
編碼
代碼寫起來(lái),和移動(dòng)應(yīng)用開(kāi)發(fā)沒(méi)什么不同。但是由于TV端的操作不是觸屏的,是由遙控器來(lái)操作的。所以,在TV中,能夠被遙控器選中的View需要設(shè)置focusable=true,即需要View能夠獲取焦點(diǎn)。當(dāng)我們操作遙控器的上下左右鍵的時(shí)候,Android默認(rèn)會(huì)尋找當(dāng)前焦點(diǎn)View的上下左右最近的可獲得焦點(diǎn)的View作為焦點(diǎn)移動(dòng)的方向。
這樣就有一個(gè)問(wèn)題,有時(shí)候默認(rèn)的移動(dòng)方向并不是按照我們所預(yù)期的。還有的時(shí)候,我們布局中有嵌套好幾層的View,外部View獲取焦點(diǎn)和內(nèi)部View獲取焦點(diǎn)的策略我們?cè)趺慈タ刂??與我們?cè)谟|屏中的Touch事件的分發(fā),在TV中大多需要考慮的便是焦點(diǎn)事件的分發(fā)。Android也為我們配置了簡(jiǎn)單的API去定義焦點(diǎn)的移動(dòng)。例如,我們可以在代碼中或者布局中指定某一個(gè)View的上(nextFocusUp)下(nextFocusDown)左(nextFocusLeft)右(nextFocusRight)鍵該移動(dòng)到哪個(gè)View。
還有一些需要焦點(diǎn)記憶功能,你需要自己重寫焦點(diǎn)的分發(fā)。
另外一個(gè)交互特點(diǎn)是,當(dāng)我們的View獲取到焦點(diǎn)即通過(guò)遙控器選擇到View時(shí),通常我們需要對(duì)被選擇的View做特殊處理。比如背景色突出,大小放大縮小(其實(shí)就是做一個(gè)scale的動(dòng)畫(huà))。如果需要選中框的效果,可以使用selector的state_focused控制使用不同的drawable做背景。
對(duì)于遙控的按鍵監(jiān)控,可以通過(guò)重寫:
@Override public boolean onKeyDown(int keyCode, KeyEvent event) { return super.onKeyDown(keyCode, event); }
常用的按鍵如下(定義在KeyEvent中):
KEYCODE_DPAD_UP :導(dǎo)航鍵上鍵KEYCODE_DPAD_DOWN:導(dǎo)航鍵下鍵KEYCODE_DPAD_LEFT:導(dǎo)航鍵左鍵KEYCODE_DPAD_RIGHT:導(dǎo)航鍵右鍵KEYCODE_DPAD_CENTER:導(dǎo)航鍵確認(rèn)鍵KEYCODE_VOLUME_UP:音量增大鍵KEYCODE_VOLUME_DOWN:音量減小鍵
這里著重講一下Home鍵,一般我們電視的遙控器上都有一個(gè)主頁(yè)鍵(Home鍵)。其實(shí)對(duì)于Home鍵的監(jiān)聽(tīng),我們是無(wú)法在應(yīng)用層捕獲的,已經(jīng)被framework層處理了:
// 源碼 /** Key code constant: Home key. * This key is handled by the framework and is never delivered to applications. */ public static final int KEYCODE_HOME = 3;
那么,如果我們需要監(jiān)聽(tīng)用戶按了遙控器的主頁(yè)鍵該怎么辦呢?
幸好,系統(tǒng)為我們提供了一個(gè)很有用的廣播:
/** * Broadcast Action: This is broadcast when a user action should request a * temporary system dialog to dismiss. Some examples of temporary system * dialogs are the notification window-shade and the recent tasks dialog. */ @SdkConstant(SdkConstantType.BROADCAST_intent_ACTION) public static final String ACTION_CLOSE_SYSTEM_DIALOGS = \”android.intent.action.CLOSE_SYSTEM_DIALOGS\”;
當(dāng)用戶按Home鍵的時(shí)候,系統(tǒng)會(huì)發(fā)送上面的廣播(當(dāng)然不僅僅是Home鍵),所以我們需要需要注冊(cè)一個(gè)這樣的廣播,然后判斷一下廣播發(fā)生的原因是否為按Home鍵發(fā)出的,示例代碼如下:
public class HomeReceiver extends BroadcastReceiver { private static final String CLOSE_SYSTEM_DIALOG_REASON_KEY = \”reason\”; private static final String CLOSE_SYSTEM_DISLOG_BY_HOME_KEY = \”homekey\”; @Override public void onReceive(Context context, Intent intent) { String action = intent.getAction(); if (action.equals(Intent.ACTION_CLOSE_SYSTEM_DIALOGS)) { String reason = intent.getStringExtra(CLOSE_SYSTEM_DIALOG_REASON_KEY); // 是因?yàn)镠ome鍵發(fā)出的廣播 if (CLOSE_SYSTEM_DISLOG_BY_HOME_KEY.equals(reason)) { //監(jiān)聽(tīng)到Home鍵直接退出整個(gè)應(yīng)用 MyApplication.getInstance().exit(); } } }}
適配更多的分辨率
到這里,完整的開(kāi)發(fā)TV應(yīng)用的流程基本上沒(méi)問(wèn)題了。但是,要想開(kāi)發(fā)出通用性更高的代碼。不得不考慮到TV和機(jī)頂盒的分辨率眾多。如果不做適配或者布局處理不太合理。很有可能當(dāng)你在開(kāi)發(fā)設(shè)備上顯示的很完美,換一臺(tái)設(shè)備后,UI大不相同,混亂不堪。所以,我們需要注意以下幾點(diǎn):
- 布局盡量使用百分比的方式布局(ConstraintLayout非常合適)
- 涉及到具體大小的地方可以多建一些典型分辨率的資源目錄(values-w960dp、values-w1280dp等等),在各自的dimens.xml分別使用不同的值
- 我們是針對(duì)機(jī)頂盒的分辨率做適配,而不是顯示器的分辨率
總結(jié)
TV的開(kāi)發(fā)與手機(jī)開(kāi)發(fā)如出一轍,大同小異。UI開(kāi)發(fā)上盡量使用LeanBack提供的組件,基本滿足TV端的交互要求。這里給大家提供了一部分比較有用的網(wǎng)站:
Android TV 開(kāi)源社區(qū):https://gitee.com/kumei
LeanBack使用Demo:https://github.com/googlesamples/androidtv-Leanback
Leanback 庫(kù)使用簡(jiǎn)介:https://www.jianshu.com/p/d575e0c7bd59
本人14年java轉(zhuǎn)Android開(kāi)發(fā),去阿里,華為等大廠待過(guò),也面試過(guò)很多人。不少人私下問(wèn)我,2019年Android進(jìn)階該怎么學(xué),方法有沒(méi)有?深知大多數(shù)初中級(jí)Android工程師,想要提升技能,往往是自己摸索成長(zhǎng),不成體系的學(xué)習(xí)效果低效漫長(zhǎng)且無(wú)助。
沒(méi)錯(cuò),三月份開(kāi)始我花了一個(gè)多月的時(shí)間整理出來(lái)的學(xué)習(xí)資料,希望能幫助那些想進(jìn)階提升Android開(kāi)發(fā),卻又不知道怎么進(jìn)階學(xué)習(xí)的朋友。【包括高級(jí)UI、性能優(yōu)化、架構(gòu)師課程、NDK、Kotlin、混合式開(kāi)發(fā)(ReactNative Weex)、Flutter等架構(gòu)技術(shù)資料】,希望能幫助到您面試前的復(fù)習(xí)且找到一個(gè)好的工作,也節(jié)省大家在網(wǎng)上搜索資料的時(shí)間來(lái)學(xué)習(xí)。
資料獲取方式:轉(zhuǎn)發(fā) 關(guān)注后私信回復(fù)我【學(xué)習(xí)】免費(fèi)獲取Android進(jìn)階開(kāi)發(fā)資料?。。?/strong>