Flutter TV Android端開(kāi)發(fā)技巧詳細(xì)教程
前言
最近公司有了新的業(yè)務(wù),把現(xiàn)有Flutter Android項(xiàng)目應(yīng)用到TV上去,這不,Asscre的活就來(lái)了。
本文詳細(xì)說(shuō)明Flutter for TV的兩種實(shí)現(xiàn)方式,能力有限,不足之處歡迎指點(diǎn),哈哈哈
開(kāi)發(fā)思路
在開(kāi)發(fā)之前,我們先設(shè)定一下我們的思路。
即,如何對(duì)原有程序代碼侵入式最小、性能最佳、可玩性更高做出設(shè)定。
那么,通過(guò)上面的設(shè)定,我們?cè)贔lutter Widget中就發(fā)現(xiàn)了兩個(gè)東西:
- RawKeyboardListener
- InkWell和其他Android TV配置
先上效果
可玩性、可塑性更高的RawKeyboardListener解決方案效果
對(duì)原有程序修改最小的InkWell和其他Android TV配置解決方案效果
開(kāi)發(fā)細(xì)節(jié)
可玩性、可塑性更高的RawKeyboardListener解決方案
使用RawKeyboardListener
RawKeyboardListener( focusNode: d.focusNode, // 配置focusNode onKey: (RawKeyEvent event) => context.read<HomePageContentWidgetProvider>().focusEventHandler(event, context, d), // 對(duì)特殊事件進(jìn)行監(jiān)聽(tīng)和處理 child: Container( height: 190, width: 190, decoration: BoxDecoration( border: Border.all( width: 2, color: d.focusNode.hasFocus ? Colors.blue : Colors.transparent), borderRadius: BorderRadius.circular(20), color: Colors.white.withAlpha(20), ), child: Column( mainAxisAlignment: MainAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.center, children: [ Image.asset( d.img, height: 80, ), SizedBox(height: 20), Text( d.name, style: TextStyle( color: Colors.white, fontSize: 32, ), ), ], ), ), ),
Provider層對(duì)事件進(jìn)行處理
import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:tv_test/pages/memory_page/memory_page.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart'; class HomePageContentWidgetProvider with ChangeNotifier { bool init = false; double maxWScreen = 0; // 按鈕距離屏幕右側(cè)最大邊界 double minWScreen = 60.w; // 按鈕距離屏幕最左側(cè)距離邊界 final List<HomePageMakeBtn> makeBtnList = [ HomePageMakeBtn('lib/assets/img/youtube.png', 'You Tube', '', FocusNode()), HomePageMakeBtn('lib/assets/img/apple.png', 'Apple', '', FocusNode()), HomePageMakeBtn('lib/assets/img/facebook.png', 'Facebook', '', FocusNode()), HomePageMakeBtn('lib/assets/img/douyin.png', 'Tik Tok', '', FocusNode()), HomePageMakeBtn('lib/assets/img/mi.png', 'MI', '', FocusNode()), HomePageMakeBtn('lib/assets/img/huawei.png', 'Hua Wei', '', FocusNode()), HomePageMakeBtn('lib/assets/img/youtube.png', 'TTT', '', FocusNode()), HomePageMakeBtn('lib/assets/img/apple.png', 'DDDD', '', FocusNode()), HomePageMakeBtn('lib/assets/img/facebook.png', 'FFFF', '', FocusNode()), HomePageMakeBtn('lib/assets/img/douyin.png', 'AAAA', '', FocusNode()), HomePageMakeBtn('lib/assets/img/mi.png', 'QQQQQ', '', FocusNode()), HomePageMakeBtn('lib/assets/img/huawei.png', 'WWWW', '', FocusNode()), HomePageMakeBtn('lib/assets/img/youtube.png', 'EEEEE', '', FocusNode()), HomePageMakeBtn('lib/assets/img/apple.png', 'RRRRR', '', FocusNode()), HomePageMakeBtn('lib/assets/img/facebook.png', 'YYYYYY', '', FocusNode()), HomePageMakeBtn('lib/assets/img/douyin.png', 'UUUUUU', '', FocusNode()), HomePageMakeBtn('lib/assets/img/mi.png', 'SSSSS', '', FocusNode()), HomePageMakeBtn('lib/assets/img/huawei.png', 'VVVV', '', FocusNode()), ]; HomePageContentWidgetProvider(BuildContext context) { maxWScreen = MediaQuery.of(context).size.width - 246.w; // setMakeFocusAddListener(); if (!init) { makeBtnList.first.focusNode.requestFocus(); init = true; } } setMakeFocusAddListener() { for (int i = 0; i < makeBtnList.length; i++) { makeBtnList[i].focusNode.addListener(() { if (makeBtnList[i].focusNode.hasFocus) { // notifyListeners(); print( '====${makeBtnList[i].name} : ${makeBtnList[i].focusNode.hasFocus}'); } }); } } setMakeFocusDispose() { for (var item in makeBtnList) { item.focusNode.removeListener(() {}); item.focusNode.dispose(); } } focusEventHandler( RawKeyEvent event, BuildContext context, HomePageMakeBtn param) async { /// 只處理按鍵按下的事件 if (event.data is RawKeyEventDataAndroid && event.runtimeType.toString() == 'RawKeyDownEvent') { CustomRawKeyEventDataAndroid _d = CustomRawKeyEventDataAndroid.format(event.data); /// 對(duì)按下確定鍵和中心鍵進(jìn)行處理 if (_d.keyCode == 23 || _d.keyCode == 66) { Navigator.of(context).push( MaterialPageRoute(builder: (_) => MemoryPage(title: param.name))); } else { // for (var e in makeBtnList) { // print('${e.name} : ${e.focusNode.hasFocus}'); // } /// 對(duì)左鍵進(jìn)行處理 if (_d.keyCode == 21) { await keyCodeDpadLeft(context, param); } /// 對(duì)右鍵進(jìn)行處理 if (_d.keyCode == 22) { await keyCodeDpadRight(context, param); } notifyListeners(); } } } /// 對(duì)左鍵進(jìn)行處理 keyCodeDpadLeft(BuildContext context, HomePageMakeBtn param) async { /// 首位邊界處理 final int _idx = makeBtnList.indexWhere((e) => e == param); if (_idx == 0) return; final int _nextIndex = _idx + 1; if ((_nextIndex % 7) == 1) { HomePageMakeBtn _nextNode = makeBtnList[_idx - 1]; print(_nextNode.name); await Future.delayed(const Duration(milliseconds: 20)); _nextNode.focusNode.requestFocus(); } } /// 對(duì)右鍵進(jìn)行處理 keyCodeDpadRight(BuildContext context, HomePageMakeBtn param) async { final int _idx = makeBtnList.indexWhere((e) => e == param); /// 末位邊界處理 if (_idx == (makeBtnList.length - 1)) return; final int _nextIndex = _idx + 1; if ((_nextIndex % 7) == 0) { HomePageMakeBtn _nextNode = makeBtnList[_nextIndex]; await Future.delayed(const Duration(milliseconds: 20)); _nextNode.focusNode.requestFocus(); } } @override void dispose() { setMakeFocusDispose(); super.dispose(); } } class HomePageMakeBtn { final String img; final String name; final String routerName; final FocusNode focusNode; HomePageMakeBtn(this.img, this.name, this.routerName, this.focusNode); } class CustomRawKeyEventDataAndroid { final int flags; final int codePoint; final int plainCodePoint; /// case 19: KEY_UP /// case 20: KEY_DOWN /// case 21: KEY_LEFT /// case 22: KEY_RIGHT /// case 23: KEY_CENTER final int keyCode; final int scanCode; final int metaState; CustomRawKeyEventDataAndroid(this.flags, this.codePoint, this.plainCodePoint, this.keyCode, this.scanCode, this.metaState); static CustomRawKeyEventDataAndroid format(d) { return CustomRawKeyEventDataAndroid(d.flags, d.codePoint, d.plainCodePoint, d.keyCode, d.scanCode, d.metaState); } }
注意
我們可以看到在處理左鍵和右鍵的時(shí)候我們用了
這是為什么呢?
那是因?yàn)樵趯?shí)際效果中,我們r(jià)equestFocus操作的時(shí)候,F(xiàn)lutter的機(jī)制會(huì)首先觸發(fā)一次requestFocus,然后再觸發(fā)一次requestFocus,一共兩次,這就與我們的預(yù)想就有沖突了。
例如:
使用按鍵末尾向右時(shí),系統(tǒng)觸發(fā)的focus到UUUUU這個(gè)按鈕,我們的實(shí)際預(yù)想的是到Y(jié)YYYY即可。
使用按鍵首位向左時(shí),同樣會(huì)跨兩個(gè)focus。
目前Asscre并沒(méi)有找到很好的解決方案,但使用await Future delayed可以舒緩一下這不人性的操作。
對(duì)原有程序修改最小的InkWell和其他Android TV配置解決方案
首先,我們需要在AndroidManifest.xml 設(shè)置LEANBACK_LAUNCHER告訴平臺(tái)我們的程序是一個(gè)電視應(yīng)用程序
<intent-filter> <action android:name="android.intent.action.MAIN"/> <category android:name="android.intent.category.LEANBACK_LAUNCHER"/> // 新增這一句 <category android:name="android.intent.category.LAUNCHER"/> </intent-filter>
然后,我們?cè)贛ain入口文件中添加 Shortcuts用于我們的程序響應(yīng)我們的遙控器指令。
return Shortcuts( shortcuts: <LogicalKeySet, Intent>{ LogicalKeySet(LogicalKeyboardKey.select): ActivateIntent(), }, child: MaterialApp( ... );
最后,使用InkWell來(lái)獲取焦點(diǎn)設(shè)置用戶遙控點(diǎn)擊的效果,其中focusColor幫助我們提醒用戶此時(shí)的按鈕位置。
return Material( color: Colors.white.withAlpha(20), child: InkWell( focusColor: Colors.deepOrange.withAlpha(80), onTap: () => Navigator.of(context) .push(MaterialPageRoute(builder: (_) => MemoryPage(title: d.name))), child: SizedBox( height: 190, width: 190, child: Column( mainAxisAlignment: MainAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.center, children: [ Image.asset( d.img, height: 80, ), SizedBox(height: 20), Text( d.name, style: TextStyle( color: Colors.white, fontSize: 32, ), ), ], ), ), ), );
總結(jié)
上述兩種解決方案中,大家可以根據(jù)自己(boss)的喜好或者業(yè)務(wù)需求選擇一種使用。
在需要復(fù)雜的自定義的業(yè)務(wù)情況下,推薦使用RawKeyboardListener的解決方案,可以做出很多酷炫的效果,譬如按鍵事件觸發(fā)時(shí),focus住的widget可以做出放大、漸變等等效果,這有助于提升用戶的體驗(yàn)。
但,要是在現(xiàn)有的業(yè)務(wù)邏輯上,在少量調(diào)整后就可使用上述中的InkWell的解決方案。
文件參考
TV keyCode詳解
name | keycode | 說(shuō)明 |
---|---|---|
KEYCODE_UNKNOWN | 0 | |
--------------------------------------- | ----- | -------------- |
KEYCODE_SOFT_LEFT | 1 | |
KEYCODE_SOFT_RIGHT | 2 | |
KEYCODE_HOME | 3 | HOME鍵 |
KEYCODE_BACK | 4 | 返回鍵 |
KEYCODE_CALL | 5 | 撥號(hào)鍵 |
KEYCODE_ENDCALL | 6 | 掛機(jī)鍵 |
KEYCODE_0 | 7 | |
KEYCODE_1 | 8 | |
KEYCODE_2 | 9 | |
KEYCODE_3 | 10 | |
KEYCODE_4 | 11 | |
KEYCODE_5 | 12 | |
KEYCODE_6 | 13 | |
KEYCODE_7 | 14 | |
KEYCODE_8 | 15 | |
KEYCODE_9 | 16 | |
KEYCODE_STAR | 17 | 按鍵 * |
KEYCODE_POUND | 18 | 按鍵 # |
KEYCODE_DPAD_UP | 19 | 向上 |
KEYCODE_DPAD_DOWN | 20 | 向下 |
KEYCODE_DPAD_LEFT | 21 | 向左 |
KEYCODE_DPAD_RIGHT | 22 | 向右 |
KEYCODE_DPAD_CENTER | 23 | 確定鍵 |
KEYCODE_VOLUME_UP | 24 | 音量增加鍵 |
KEYCODE_VOLUME_DOWN | 25 | 音量減小鍵 |
KEYCODE_POWER | 26 | 電源鍵 |
KEYCODE_CAMERA | 27 | 拍照鍵 |
KEYCODE_CLEAR | 28 | |
KEYCODE_A | 29 | |
KEYCODE_B | 30 | |
KEYCODE_C | 31 | |
KEYCODE_D | 32 | |
KEYCODE_E | 33 | |
KEYCODE_F | 34 | |
KEYCODE_G | 35 | |
KEYCODE_H | 36 | |
KEYCODE_I | 37 | |
KEYCODE_J | 38 | |
KEYCODE_K | 39 | |
KEYCODE_L | 40 | |
KEYCODE_M | 41 | |
KEYCODE_N | 42 | |
KEYCODE_O | 43 | |
KEYCODE_P | 44 | |
KEYCODE_Q | 45 | |
KEYCODE_R | 46 | |
KEYCODE_S | 47 | |
KEYCODE_T | 48 | |
KEYCODE_U | 49 | |
KEYCODE_V | 50 | |
KEYCODE_W | 51 | |
KEYCODE_X | 52 | |
KEYCODE_Y | 53 | |
KEYCODE_Z | 54 | |
KEYCODE_COMMA | 55 | 按鍵 , |
KEYCODE_PERIOD | 56 | 按鍵 . |
KEYCODE_ALT_LEFT | 57 | |
KEYCODE_ALT_RIGHT | 58 | |
KEYCODE_SHIFT_LEFT | 59 | |
KEYCODE_SHIFT_RIGHT | 60 | |
KEYCODE_TAB | 61 | Tab鍵 |
KEYCODE_SPACE | 62 | 空格鍵 |
KEYCODE_SYM | 63 | |
KEYCODE_EXPLORER | 64 | |
KEYCODE_ENVELOPE | 65 | |
KEYCODE_ENTER | 66 | 回車鍵 |
KEYCODE_DEL | 67 | 退格鍵 |
KEYCODE_GRAVE | 68 | 按鍵 ` |
KEYCODE_MINUS | 69 | 按鍵- |
KEYCODE_EQUALS | 70 | 按鍵 = |
KEYCODE_LEFT_BRACKET | 71 | 按鍵 [ |
KEYCODE_RIGHT_BRACKET | 72 | 按鍵 ] |
KEYCODE_BACKSLASH | 73 | 按鍵 \ |
KEYCODE_SEMICOLON | 74 | 按鍵 , |
KEYCODE_APOSTROPHE | 75 | 按鍵 ''單引號(hào) |
KEYCODE_SLASH | 76 | 按鍵 / |
KEYCODE_AT | 77 | 按鍵 @ |
KEYCODE_NUM | 78 | |
KEYCODE_HEADSETHOOK | 79 | |
KEYCODE_FOCUS | 80 | 拍照對(duì)焦鍵 |
KEYCODE_PLUS | 81 | 按鍵+ |
KEYCODE_MENU | 82 | 菜單鍵 |
KEYCODE_NOTIFICATION | 83 | 通知鍵 |
KEYCODE_SEARCH | 84 | |
KEYCODE_MEDIA_PLAY_PAUSE | 85 | 多媒體鍵 播放/暫停 |
KEYCODE_MEDIA_STOP | 86 | 多媒體鍵 暫停 |
KEYCODE_MEDIA_NEXT | 87 | 多媒體鍵 下一首 |
KEYCODE_MEDIA_PREVIOUS | 88 | 多媒體鍵 上一首 |
KEYCODE_MEDIA_REWIND | 89 | 多媒體鍵 快退 |
KEYCODE_MEDIA_FAST_FORWARD | 90 | 多媒體鍵 快進(jìn) |
KEYCODE_MUTE | 91 | 話筒靜音鍵 |
KEYCODE_PAGE_UP | 92 | 向上翻頁(yè)鍵 |
KEYCODE_PAGE_DOWN | 93 | 向下翻頁(yè)鍵 |
KEYCODE_PICTSYMBOLS | 94 | |
KEYCODE_SWITCH_CHARSET | 95 | |
KEYCODE_BUTTON_A | 96 | |
KEYCODE_BUTTON_B | 97 | |
KEYCODE_BUTTON_C | 98 | |
KEYCODE_BUTTON_X | 99 | |
KEYCODE_BUTTON_Y | 100 | |
KEYCODE_BUTTON_Z | 101 | |
KEYCODE_BUTTON_L1 | 102 | |
KEYCODE_BUTTON_R1 | 103 | |
KEYCODE_BUTTON_L2 | 104 | |
KEYCODE_BUTTON_R2 | 105 | |
KEYCODE_BUTTON_THUMBL | 106 | |
KEYCODE_BUTTON_THUMBR | 107 | |
KEYCODE_BUTTON_START | 108 | |
KEYCODE_BUTTON_SELECT | 109 | |
KEYCODE_BUTTON_MODE | 110 | |
KEYCODE_ESCAPE | 111 | ESC鍵 |
KEYCODE_FORWARD_DEL | 112 | 刪除鍵 |
KEYCODE_CTRL_LEFT | 113 | |
KEYCODE_CTRL_RIGHT | 114 | |
KEYCODE_CAPS_LOCK | 115 | 大寫鎖定鍵 |
KEYCODE_SCROLL_LOCK | 116 | |
KEYCODE_META_LEFT | 117 | |
KEYCODE_META_RIGHT | 118 | |
KEYCODE_FUNCTION | 119 | |
KEYCODE_SYSRQ | 120 | |
KEYCODE_BREAK | 121 | Break/Pause鍵 |
KEYCODE_MOVE_HOME | 122 | 光標(biāo)移動(dòng)到開(kāi)始鍵 |
KEYCODE_MOVE_END | 123 | 光標(biāo)移動(dòng)到末尾鍵 |
KEYCODE_INSERT | 124 | |
KEYCODE_FORWARD | 125 | |
KEYCODE_MEDIA_PLAY | 126 | 多媒體鍵 播放 |
KEYCODE_MEDIA_PAUSE | 127 | 多媒體鍵 暫停 |
KEYCODE_MEDIA_CLOSE | 128 | 多媒體鍵 關(guān)閉 |
KEYCODE_MEDIA_EJECT | 129 | 多媒體鍵 彈出 |
KEYCODE_MEDIA_RECORD | 130 | 多媒體鍵 錄音 |
KEYCODE_F1 | 131 | |
KEYCODE_F2 | 132 | |
KEYCODE_F3 | 133 | |
KEYCODE_F4 | 134 | |
KEYCODE_F5 | 135 | |
KEYCODE_F6 | 136 | |
KEYCODE_F7 | 137 | |
KEYCODE_F8 | 138 | |
KEYCODE_F9 | 139 | |
KEYCODE_F10 | 140 | |
KEYCODE_F11 | 141 | |
KEYCODE_F12 | 142 | |
KEYCODE_NUM_LOCK | 143 | 小鍵盤鎖 |
KEYCODE_NUMPAD_0 | 144 | |
KEYCODE_NUMPAD_1 | 145 | |
KEYCODE_NUMPAD_2 | 146 | |
KEYCODE_NUMPAD_3 | 147 | |
KEYCODE_NUMPAD_4 | 148 | |
KEYCODE_NUMPAD_5 | 149 | |
KEYCODE_NUMPAD_6 | 150 | |
KEYCODE_NUMPAD_7 | 151 | |
KEYCODE_NUMPAD_8 | 152 | |
KEYCODE_NUMPAD_9 | 153 | |
KEYCODE_NUMPAD_DIVIDE | 154 | |
KEYCODE_NUMPAD_MULTIPLY | 155 | |
KEYCODE_NUMPAD_SUBTRACT | 156 | |
KEYCODE_NUMPAD_ADD | 157 | |
KEYCODE_NUMPAD_DOT | 158 | |
KEYCODE_NUMPAD_COMMA | 159 | |
KEYCODE_NUMPAD_ENTER | 160 | |
KEYCODE_NUMPAD_EQUALS | 161 | |
KEYCODE_NUMPAD_LEFT_PAREN | 162 | |
KEYCODE_NUMPAD_RIGHT_PAREN | 163 | |
KEYCODE_VOLUME_MUTE | 164 | 揚(yáng)聲器靜音鍵 |
KEYCODE_INFO | 165 | |
KEYCODE_CHANNEL_UP | 166 | |
KEYCODE_CHANNEL_DOWN | 167 | |
KEYCODE_ZOOM_IN | 168 | 放大鍵 |
KEYCODE_ZOOM_OUT | 169 | 縮小鍵 |
KEYCODE_TV | 170 | |
KEYCODE_WINDOW | 171 | |
KEYCODE_GUIDE | 172 | |
KEYCODE_DVR | 173 | |
KEYCODE_BOOKMARK | 174 | |
KEYCODE_CAPTIONS | 175 | |
KEYCODE_SETTINGS | 176 | |
KEYCODE_TV_POWER | 177 | |
KEYCODE_TV_INPUT | 178 | |
KEYCODE_STB_POWER | 179 | |
KEYCODE_STB_INPUT | 180 | |
KEYCODE_AVR_POWER | 181 | |
KEYCODE_AVR_INPUT | 182 | |
KEYCODE_PROG_RED | 183 | |
KEYCODE_PROG_GREEN | 184 | |
KEYCODE_PROG_YELLOW | 185 | |
KEYCODE_PROG_BLUE | 186 | |
KEYCODE_APP_SWITCH | 187 | |
KEYCODE_BUTTON_1 | 188 | |
KEYCODE_BUTTON_2 | 189 | |
KEYCODE_BUTTON_3 | 190 | |
KEYCODE_BUTTON_4 | 191 | |
KEYCODE_BUTTON_5 | 192 | |
KEYCODE_BUTTON_6 | 193 | |
KEYCODE_BUTTON_7 | 194 | |
KEYCODE_BUTTON_8 | 195 | |
KEYCODE_BUTTON_9 | 196 | |
KEYCODE_BUTTON_10 | 197 | |
KEYCODE_BUTTON_11 | 198 | |
KEYCODE_BUTTON_12 | 199 | |
KEYCODE_BUTTON_13 | 200 | |
KEYCODE_BUTTON_14 | 201 | |
KEYCODE_BUTTON_15 | 202 | |
KEYCODE_BUTTON_16 | 203 | |
KEYCODE_LANGUAGE_SWITCH | 204 | |
KEYCODE_MANNER_MODE | 205 | |
KEYCODE_3D_MODE | 206 | |
KEYCODE_CONTACTS | 207 | |
KEYCODE_CALENDAR | 208 | |
KEYCODE_MUSIC | 209 | |
KEYCODE_CALCULATOR | 210 | |
KEYCODE_ZENKAKU_HANKAKU | 211 | |
KEYCODE_EISU | 212 | |
KEYCODE_MUHENKAN | 213 | |
KEYCODE_HENKAN | 214 | |
KEYCODE_KATAKANA_HIRAGANA | 215 | |
KEYCODE_YEN | 216 | |
KEYCODE_RO | 217 | |
KEYCODE_KANA | 218 | |
KEYCODE_ASSIST | 219 | |
KEYCODE_BRIGHTNESS_DOWN | 220 | |
KEYCODE_BRIGHTNESS_UP | 221 | |
KEYCODE_MEDIA_AUDIO_TRACK | 222 | |
KEYCODE_SLEEP | 223 | |
KEYCODE_WAKEUP | 224 | |
KEYCODE_PAIRING | 225 | |
KEYCODE_MEDIA_TOP_MENU | 226 | |
KEYCODE_11 | 227 | |
KEYCODE_12 | 228 | |
KEYCODE_LAST_CHANNEL | 229 | |
KEYCODE_TV_DATA_SERVICE | 230 | |
KEYCODE_VOICE_ASSIST | 231 | |
KEYCODE_TV_RADIO_SERVICE | 232 | |
KEYCODE_TV_TELETEXT | 233 | |
KEYCODE_TV_NUMBER_ENTRY | 234 | |
KEYCODE_TV_TERRESTRIAL_ANALOG | 235 | |
KEYCODE_TV_TERRESTRIAL_DIGITAL | 236 | |
KEYCODE_TV_SATELLITE | 237 | |
KEYCODE_TV_SATELLITE_BS | 238 | |
KEYCODE_TV_SATELLITE_CS | 239 | |
KEYCODE_TV_SATELLITE_SERVICE | 240 | |
KEYCODE_TV_NETWORK | 241 | |
KEYCODE_TV_ANTENNA_CABLE | 242 | |
KEYCODE_TV_INPUT_HDMI_1 | 243 | |
KEYCODE_TV_INPUT_HDMI_2 | 244 | |
KEYCODE_TV_INPUT_HDMI_3 | 245 | |
KEYCODE_TV_INPUT_HDMI_4 | 246 | |
KEYCODE_TV_INPUT_COMPOSITE_1 | 247 | |
KEYCODE_TV_INPUT_COMPOSITE_2 | 248 | |
KEYCODE_TV_INPUT_COMPONENT_1 | 249 | |
KEYCODE_TV_INPUT_COMPONENT_2 | 250 | |
KEYCODE_TV_INPUT_VGA_1 | 251 | |
KEYCODE_TV_AUDIO_DESCRIPTION | 252 | |
KEYCODE_TV_AUDIO_DESCRIPTION_MIX_UP | 253 | |
KEYCODE_TV_AUDIO_DESCRIPTION_MIX_DOWN | 254 | |
KEYCODE_TV_ZOOM_MODE | 255 | |
KEYCODE_TV_CONTENTS_MENU | 256 | |
KEYCODE_TV_MEDIA_CONTEXT_MENU | 257 | |
KEYCODE_TV_TIMER_PROGRAMMING | 258 | |
KEYCODE_HELP | 259 | |
KEYCODE_NAVIGATE_PREVIOUS | 260 | |
KEYCODE_NAVIGATE_NEXT | 261 | |
KEYCODE_NAVIGATE_IN | 262 | |
KEYCODE_NAVIGATE_OUT | 263 | |
KEYCODE_STEM_PRIMARY | 264 | |
KEYCODE_STEM_1 | 265 | |
KEYCODE_STEM_2 | 266 | |
KEYCODE_STEM_3 | 267 | |
KEYCODE_DPAD_UP_LEFT | 268 | |
KEYCODE_DPAD_DOWN_LEFT | 269 | |
KEYCODE_DPAD_UP_RIGHT | 270 | |
KEYCODE_DPAD_DOWN_RIGHT | 271 | |
KEYCODE_MEDIA_SKIP_FORWARD | 272 | |
KEYCODE_MEDIA_SKIP_BACKWARD | 273 | |
KEYCODE_MEDIA_STEP_FORWARD | 274 | |
KEYCODE_MEDIA_STEP_BACKWARD | 275 | |
KEYCODE_SOFT_SLEEP | 276 | |
KEYCODE_CUT | 277 | |
KEYCODE_COPY | 278 | |
KEYCODE_PASTE | 279 | |
KEYCODE_SYSTEM_NAVIGATION_UP | 280 | |
KEYCODE_SYSTEM_NAVIGATION_DOWN | 281 | |
KEYCODE_SYSTEM_NAVIGATION_LEFT | 282 | |
KEYCODE_SYSTEM_NAVIGATION_RIGHT | 283 |
以上就是Flutter TV Android端開(kāi)發(fā)技巧詳細(xì)教程的詳細(xì)內(nèi)容,更多關(guān)于Flutter TV Android端開(kāi)發(fā)的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Android編程實(shí)現(xiàn)自動(dòng)調(diào)整TextView字體大小以適應(yīng)文字長(zhǎng)度的方法
這篇文章主要介紹了Android編程實(shí)現(xiàn)自動(dòng)調(diào)整TextView字體大小以適應(yīng)文字長(zhǎng)度的方法,涉及Android基于TextView類的繼承及Paint屬性操作實(shí)現(xiàn)字體大小自適應(yīng)的相關(guān)技巧,需要的朋友可以參考下2016-01-01appium運(yùn)行各種坑爹報(bào)錯(cuò)問(wèn)題及解決方法【推薦】
這篇文章主要介紹了 appium運(yùn)行各種坑爹報(bào)錯(cuò)問(wèn)題及解決方法,本文給大家介紹的非常詳細(xì),具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2019-06-06android多線程斷點(diǎn)下載-帶進(jìn)度條和百分比進(jìn)度顯示效果
下面小編就為大家?guī)?lái)一篇android多線程斷點(diǎn)下載-帶進(jìn)度條和百分比進(jìn)度顯示效果。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2017-06-06Android中SeekBar拖動(dòng)條使用方法詳解
這篇文章主要為大家詳細(xì)介紹了Android中SeekBar拖動(dòng)條使用方法,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-03-03Android 中ListView和GridView賦值錯(cuò)位
這篇文章主要介紹了Android 中ListView和GridView賦值錯(cuò)位的相關(guān)資料,希望通過(guò)本文能幫助到大家,需要的朋友可以參考下2017-10-10Ubuntu中為Android簡(jiǎn)單介紹硬件抽象層(HAL)
本文主要介紹在Android 的硬件抽象層,學(xué)習(xí)Android 硬件抽象層(HAL)對(duì)理解整個(gè)Android都是有非常大的作用,有興趣的小伙伴可以參考下2016-08-08Android開(kāi)發(fā)筆記 Handler使用總結(jié)
當(dāng)應(yīng)用程序啟動(dòng)時(shí),Android首先會(huì)開(kāi)啟一個(gè)主線程(也就是UI線程),主線程為管理界面中的UI控件,進(jìn)行事件分發(fā)2012-11-11