android-tvdemo.zip
一、创建TV版的页面:
1.新建一个项目:
2.在TV中选择Android TV Activity的项目:
3.给TV设置名称,包名,项目所在位置,语言,选择最低版本的api:
4.在grable里面给java配置为1.8版本,因为能接受的最低版本不能小于1.8:
5.写上butterknife相关配置语句(目前我也还不太懂这个是什么):
6.file→setting,在setting里面寻找这个配置,选中,点击OK安装:
7.在TV里面加入语句(你想怎么设置就怎么设置,这里我只是随便写一下,主要是测试他的功能):
8.在手机型号里面找到TV版的页面,这里两个,看你需要的选择):
9.TV版选择之后的页面如下:
10.在主要的main.java里面找到绑定的布局,然后右击,选择Generate:
11.再选择Generate Butterknife lnjections:
12.选择你要绑定的控件的定义和后面绑定按钮需要的点击事件,然后confirm(这里是快捷键导出的控件绑定和点击事件,初学者建议手写,熟练之后,再用这种操作):
13.打开values下的styles.xml,给需要的颜色命名:
14.在colors.xml下给命名添加你所需要的颜色值,然后在布局页面中调用即可:
15.电视上需要用到焦点,电视上都是通过控件获取焦点来实现点击效果的:
16.在java中绑定:
17.在已知控件ID的情况下我们可以设置上下左右的移动控件
18.在java中绑定:
19.遥控器的按键监听,毕竟是用遥控器来操作的啊,按键监听代码如下:

20.如果要监听Home键的话,就需要通过广播来,在MainActivity中创建一个class:
21.在onCreate()方法中注册广播,只要调用initReceiver()方法即可:
22.页面销毁时,注销掉广播:
23.在确定键的遥控器按键监听代码中加入弹窗显示:
二、整体代码:
<RelativeLayout 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:orientation="vertical" android:gravity="center" tools:context=".MainActivity"> <LinearLayout android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical"> <!--自定义的VideoView 做了绘制改变,和网络地址许可--> <com.llw.androidtvdemo.view.MyVideoView android:id="@+id/video_view" android:layout_width="match_parent" android:layout_height="match_parent" android:layout_gravity="center" /> </LinearLayout> <!--底部控制栏 开始时间 进度条 结束时间--> <RelativeLayout android:layout_width="match_parent" android:layout_height="@dimen/dp_100" android:layout_alignParentBottom="true" android:background="@drawable/shape_gradual_change"> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_alignParentBottom="true" android:layout_margin="@dimen/dp_10" android:gravity="center_vertical" android:orientation="horizontal"> <TextView android:id="@+id/tv_play_time" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="00:00" android:textColor="@color/white" android:textSize="@dimen/sp_24" /> <SeekBar android:id="@+id/time_seekBar" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_centerInParent="true" android:layout_marginLeft="@dimen/dp_20" android:layout_marginRight="@dimen/dp_20" android:layout_weight="1" android:max="100" android:maxHeight="3dp" android:minHeight="3dp" android:progress="0" android:progressDrawable="@drawable/seekbar_style" android:thumb="@drawable/thumb" /> <TextView android:id="@+id/tv_total_time" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="00:00" android:textColor="@color/white" android:textSize="@dimen/sp_24" /> </LinearLayout> </RelativeLayout> <!--视频结束时 显示黑色背景--> <RelativeLayout android:visibility="gone" android:id="@+id/lay_finish_bg" android:background="#000" android:layout_width="match_parent" android:layout_height="match_parent"/> <!--视频播放中 控制暂停和播放的按钮--> <ImageButton android:visibility="gone" android:focusable="true" android:layout_centerInParent="true" android:id="@+id/btn_play_or_pause" android:background="@mipmap/icon_pause" android:layout_width="@dimen/dp_100" android:layout_height="@dimen/dp_100"/> <!--视频结束时 显示重播图标--> <ImageButton android:visibility="gone" android:layout_centerInParent="true" android:id="@+id/btn_restart_play" android:background="@mipmap/icon_restart_play" android:layout_width="@dimen/dp_100" android:layout_height="@dimen/dp_100"/> <LinearLayout android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" android:gravity="center"> <TextView android:id="@+id/tv_test" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Hello TV" /> <Button android:layout_width="wrap_content" android:layout_height="wrap_content" android:id="@+id/btn_test" android:text="TV" android:layout_marginTop="20dp" android:focusable="true" android:nextFocusDown="@id/tv_test" android:nextFocusUp="@id/tv_test" android:nextFocusLeft="@id/tv_test" android:nextFocusRight="@id/tv_test" /> </LinearLayout></RelativeLayout>
package com.llw.androidtvdemo;import android.content.BroadcastReceiver;import android.content.Context;import android.content.Intent;import android.util.Log;import android.widget.Toast;import static androidx.constraintlayout.widget.Constraints.TAG;public class HomeReceiver extends BroadcastReceiver { public final String SYSTEM_DIALOG_REASON_KEY="reason"; public final String SYSTEM_DIALOG_REASON_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(SYSTEM_DIALOG_REASON_KEY); if (SYSTEM_DIALOG_REASON_HOME_KEY.equals(reason)){ Toast.makeText(context, "home键触发", Toast.LENGTH_SHORT).show(); Log.d(TAG,"home键触发"); } } }}
package com.llw.androidtvdemo;import android.content.Intent;import android.content.IntentFilter;import android.media.MediaPlayer;import android.net.Uri;import android.os.Bundle;import android.os.Handler;import android.util.Log;import android.view.KeyEvent;import android.view.View;import android.widget.Button;import android.widget.ImageButton;import android.widget.RelativeLayout;import android.widget.SeekBar;import android.widget.TextView;import android.widget.Toast;import androidx.appcompat.app.AppCompatActivity;import com.llw.androidtvdemo.view.MyVideoView;import java.text.SimpleDateFormat;import java.util.Calendar;import java.util.Formatter;import java.util.Locale;import butterknife.BindView;import butterknife.ButterKnife;import butterknife.OnClick;public class MainActivity extends AppCompatActivity { @BindView(R.id.video_view) MyVideoView videoView; @BindView(R.id.tv_play_time) TextView tvPlayTime; @BindView(R.id.time_seekBar) SeekBar timeSeekBar; @BindView(R.id.tv_total_time) TextView tvTotalTime; @BindView(R.id.lay_finish_bg) RelativeLayout layFinishBg; @BindView(R.id.btn_play_or_pause) ImageButton btnPlayOrPause; @BindView(R.id.btn_restart_play) ImageButton btnRestartPlay; @BindView(R.id.tv_test) TextView tvTest; @BindView(R.id.btn_test) Button btnTest; private HomeReceiver homeReceiver; private void initReceiver(){ homeReceiver=new HomeReceiver(); IntentFilter filter=new IntentFilter(Intent.ACTION_CLOSE_SYSTEM_DIALOGS); registerReceiver(homeReceiver,filter); } private int key = 0; private Handler handler = new Handler(); private Runnable runnable = new Runnable() { public void run() { if (videoView.isPlaying()) { int current = videoView.getCurrentPosition(); timeSeekBar.setProgress(current); tvPlayTime.setText(time(videoView.getCurrentPosition())); } handler.postDelayed(runnable, 500); } }; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); ButterKnife.bind(this); btnTest.setFocusable(true); btnTest.setNextFocusUpId(R.id.tv_test); btnTest.setNextFocusDownId(R.id.tv_test); btnTest.setNextFocusLeftId(R.id.tv_test); btnTest.setNextFocusRightId(R.id.tv_test); timeSeekBar.setOnSeekBarChangeListener(onSeekBarChangeListener); initVideo(); videoView.setOnCompletionListener(new MediaPlayer.OnCompletionListener() { @Override public void onCompletion(MediaPlayer mp) { key = 1; btnRestartPlay.setVisibility(View.VISIBLE); layFinishBg.setVisibility(View.VISIBLE); } }); videoView.setOnErrorListener(new MediaPlayer.OnErrorListener() { @Override public boolean onError(MediaPlayer mp, int what, int extra) { Toast.makeText(MainActivity.this, "播放出错", Toast.LENGTH_SHORT).show(); return false; } }); } /** * 时间转换方法 * * @param millionSeconds * @return */ protected String time(long millionSeconds) { SimpleDateFormat simpleDateFormat = new SimpleDateFormat("mm:ss"); Calendar c = Calendar.getInstance(); c.setTimeInMillis(millionSeconds); return simpleDateFormat.format(c.getTime()); } /** * 初始化VideoView */ private void initVideo() { //本地视频 //videoView.setVideoURI(Uri.parse("android.resource://" + getPackageName() + "/raw/test")); //网络视频 final Uri uri = Uri.parse("http://gslb.miaopai.com/stream/ed5HCfnhovu3tyIQAiv60Q__.mp4"); videoView.setVideoURI(uri); videoView.requestFocus(); videoView.setOnPreparedListener(new MediaPlayer.OnPreparedListener() { @Override public void onPrepared(MediaPlayer mp) { int totalTime = videoView.getDuration();//获取视频的总时长 tvTotalTime.setText(stringForTime(totalTime)); // 开始线程,更新进度条的刻度 handler.postDelayed(runnable, 0); timeSeekBar.setMax(videoView.getDuration()); //视频加载完成,准备好播放视频的回调 videoView.start(); } }); } /** * 控制视频是 播放还是暂停 或者是重播 * * @param isPlay * @param keys */ private void isVideoPlay(boolean isPlay, int keys) { switch (keys) { case 0: if (isPlay) {//暂停 btnPlayOrPause.setBackground(getResources().getDrawable(R.mipmap.icon_player)); btnPlayOrPause.setVisibility(View.VISIBLE); videoView.pause(); } else {//继续播放 btnPlayOrPause.setBackground(getResources().getDrawable(R.mipmap.icon_pause)); btnPlayOrPause.setVisibility(View.VISIBLE); // 开始线程,更新进度条的刻度 handler.postDelayed(runnable, 0); videoView.start(); timeSeekBar.setMax(videoView.getDuration()); timeGone(); } break; case 1://重新播放 initVideo(); btnRestartPlay.setVisibility(View.GONE); layFinishBg.setVisibility(View.GONE); key = 0; break; } } /** * 延时隐藏 */ private void timeGone() { new Handler().postDelayed(new Runnable() { @Override public void run() { btnPlayOrPause.setVisibility(View.INVISIBLE); } }, 1500); } /** * 进度条监听 */ private SeekBar.OnSeekBarChangeListener onSeekBarChangeListener = new SeekBar.OnSeekBarChangeListener() { // 当进度条停止修改的时候触发 @Override public void onStopTrackingTouch(SeekBar seekBar) { // 取得当前进度条的刻度 int progress = seekBar.getProgress(); if (videoView.isPlaying()) { // 设置当前播放的位置 videoView.seekTo(progress); } } @Override public void onStartTrackingTouch(SeekBar seekBar) { } @Override public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { } }; //将长度转换为时间 StringBuilder mFormatBuilder = new StringBuilder(); Formatter mFormatter = new Formatter(mFormatBuilder, Locale.getDefault()); private String stringForTime(int timeMs) { int totalSeconds = timeMs / 1000; int seconds = totalSeconds % 60; int minutes = (totalSeconds / 60) % 60; int hours = totalSeconds / 3600; mFormatBuilder.setLength(0); if (hours > 0) { return mFormatter.format("%d:%02d:%02d", hours, minutes, seconds).toString(); } else { return mFormatter.format("%02d:%02d", minutes, seconds).toString(); } } private String TAG = "key"; /** * 遥控器按键监听 * * @param keyCode * @param event * @return */ @Override public boolean onKeyDown(int keyCode, KeyEvent event) { switch (keyCode) { case KeyEvent.KEYCODE_ENTER: //确定键enter case KeyEvent.KEYCODE_DPAD_CENTER: Log.d(TAG, "enter--->"); Toast.makeText(this,"确定", Toast.LENGTH_SHORT).show(); //如果是播放中则暂停、如果是暂停则继续播放 //isVideoPlay(videoView.isPlaying(), key); break; case KeyEvent.KEYCODE_BACK: //返回键 Toast.makeText(this,"返回键", Toast.LENGTH_SHORT).show(); Log.d(TAG, "back--->"); return true; //这里由于break会退出,所以我们自己要处理掉 不返回上一层 case KeyEvent.KEYCODE_SETTINGS: //设置键 Log.d(TAG, "setting--->"); break; case KeyEvent.KEYCODE_DPAD_DOWN: //向下键 /* 实际开发中有时候会触发两次,所以要判断一下按下时触发 ,松开按键时不触发 * exp:KeyEvent.ACTION_UP */ if (event.getAction() == KeyEvent.ACTION_DOWN) { Log.d(TAG, "down--->"); } break; case KeyEvent.KEYCODE_DPAD_UP: //向上键 Log.d(TAG, "up--->"); break; case KeyEvent.KEYCODE_0: Log.d(TAG,"0--->"); case KeyEvent.KEYCODE_DPAD_LEFT: //向左键 Log.d(TAG, "left--->"); if (videoView.getCurrentPosition() > 4) { videoView.seekTo(videoView.getCurrentPosition() - 5 * 1000); } break; case KeyEvent.KEYCODE_DPAD_RIGHT: //向右键 Log.d(TAG, "right--->"); videoView.seekTo(videoView.getCurrentPosition() + 5 * 1000); break; //info键 case KeyEvent.KEYCODE_INFO: Log.d(TAG,"info--->"); break; //向上翻页键 case KeyEvent.KEYCODE_PAGE_DOWN: case KeyEvent.KEYCODE_MEDIA_NEXT: Log.d(TAG,"page down--->"); break; //向下翻页键 case KeyEvent.KEYCODE_PAGE_UP: case KeyEvent.KEYCODE_MEDIA_PREVIOUS: Log.d(TAG,"page up--->"); break; case KeyEvent.KEYCODE_VOLUME_UP: //调大声音键 Log.d(TAG, "voice up--->"); break; case KeyEvent.KEYCODE_VOLUME_DOWN: //降低声音键 Log.d(TAG, "voice down--->"); break; case KeyEvent.KEYCODE_VOLUME_MUTE: //禁用声音 Log.d(TAG, "voice mute--->"); break; default: break; } return super.onKeyDown(keyCode, event); } @OnClick(R.id.btn_test) public void onViewClicked() { //Toast 提示 Toast.makeText(this,tvTest.getText().toString(), Toast.LENGTH_SHORT).show(); }protected void onDestroy(){ super.onDestroy(); if (homeReceiver!=null){ unregisterReceiver(homeReceiver); }}}