Android TV 开发之 TV视频播放器

public void onViewClicked() {

//Toast 提示

Toast.makeText(this,tvTest.getText().toString(),Toast.LENGTH_SHORT).show();

}

}

这个时候你应该迫不及待的想要运行一下了吧,我们还有一步就是主题的设置

打开values下面的styles.xml文件

在这里插入图片描述

我们不用它这个主题,重新创建一个

styles.xml的完整代码如下:

<?xml version="1.0" encoding="utf-8"?>

然后你会发现少几个颜色,这时候我们在values文件夹下面创建一个colors.xml的文件

colors.xml代码如下:

<?xml version="1.0" encoding="utf-8"?>

#008577

#00574B

#D81B60

#1EBADE

然后就要使用我们新创建的主题了,打开AndroidManifest.xml文件

在这里插入图片描述

修改,AppTheme,改成AppTheme2,然后运行项目.运行效果如下图

在这里插入图片描述

这个时候你没有想过,我怎么点击这个按钮呢?电视机都是用遥控器的啊,遥控器又怎么操作呢?

这些问题一定在你的脑海里面环绕着,我们注意到,电视机使用遥控器,而我们的手机使用手指触摸点击,这个不能混为一谈,所以电视上需要用到焦点电视上都是通过控件获取焦点来实现点击效果的,我们在布局文件的button中写入

android:focusable=“true”

意思就是可以获取到焦点,为false则不可获取焦点,

在代码里

btnTest.setFocusable(true);

为false则不可获取焦点。

在已知控件ID的情况下我们可以设置上下左右的移动控件,

android:nextFocusUp=“@id/tv_test”

android:nextFocusDown=“@id/tv_test”

android:nextFocusLeft=“@id/tv_test”

android:nextFocusRight=“@id/tv_test”

代码中:

btnTest.setNextFocusUpId(R.id.tv_test);

btnTest.setNextFocusDownId(R.id.tv_test);

btnTest.setNextFocusLeftId(R.id.tv_test);

btnTest.setNextFocusRightId(R.id.tv_test);

了解这个之后,我们还得知道遥控器的按键监听,毕竟是用遥控器来操作的啊,按键监听代码如下:

private String TAG = “key”;

@Override

public boolean onKeyDown(int keyCode, KeyEvent event) {

switch (keyCode) {

case KeyEvent.KEYCODE_ENTER: //确定键enter

case KeyEvent.KEYCODE_DPAD_CENTER:

Log.d(TAG, “enter—>”);

break;

case KeyEvent.KEYCODE_BACK: //返回键

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: //数字键0

Log.d(TAG, “0—>”);

break;

case KeyEvent.KEYCODE_DPAD_LEFT: //向左键

Log.d(TAG, “left—>”);

break;

case KeyEvent.KEYCODE_DPAD_RIGHT: //向右键

Log.d(TAG, “right—>”);

break;

case KeyEvent.KEYCODE_INFO: //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);

}

如果你要监听Home键的话,就需要通过广播来,

在MainActivity中创建一个class

class HomeReceiver extends BroadcastReceiver {

@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(MainActivity.this,“home键触发”,Toast.LENGTH_SHORT).show();

Log.d(TAG, “home键触发”);

}

}

}

}

在onCreate()方法中注册广播,只要调用initReceiver()方法即可

public final String SYSTEM_DIALOG_REASON_KEY = “reason”;

public final String SYSTEM_DIALOG_REASON_HOME_KEY = “homekey”;

private HomeReceiver homeReceiver;

/**

  • 注册广播

*/

private void initReceiver() {

homeReceiver = new HomeReceiver();

IntentFilter filter = new IntentFilter(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);

registerReceiver(homeReceiver, filter);

}

页面销毁时,注销掉广播

@Override

protected void onDestroy() {

super.onDestroy();

if(homeReceiver!=null){

unregisterReceiver(homeReceiver);

}

}

这段代码我也是从网上找的,

然后我们在确定键的下面弹出这个Toast

case KeyEvent.KEYCODE_ENTER: //确定键enter

case KeyEvent.KEYCODE_DPAD_CENTER:

Log.d(TAG, “enter—>”);

Toast.makeText(this,tvTest.getText().toString(),Toast.LENGTH_SHORT).show();

break;

运行效果如下:

在这里插入图片描述

MainActivity.java完整代码如下:

package com.llw.androidtvdemo;

import android.content.BroadcastReceiver;

import android.content.Context;

import android.content.Intent;

import android.content.IntentFilter;

import android.os.Bundle;

import android.util.Log;

import android.view.KeyEvent;

import android.widget.Button;

import android.widget.TextView;

import android.widget.Toast;

import androidx.appcompat.app.AppCompatActivity;

import butterknife.BindView;

import butterknife.ButterKnife;

import butterknife.OnClick;

public class MainActivity extends AppCompatActivity {

@BindView(R.id.tv_test)

TextView tvTest;

@BindView(R.id.btn_test)

Button btnTest;

public final String SYSTEM_DIALOG_REASON_KEY = “reason”;

public final String SYSTEM_DIALOG_REASON_HOME_KEY = “homekey”;

private HomeReceiver homeReceiver;

/**

  • 注册广播

*/

private void initReceiver() {

homeReceiver = new HomeReceiver();

IntentFilter filter = new IntentFilter(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);

registerReceiver(homeReceiver, filter);

}

@Override

protected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.activity_main);

ButterKnife.bind(this);

btnTest.setFocusable(true);

initReceiver();

}

@OnClick(R.id.btn_test)

public void onViewClicked() {

//Toast 提示

Toast.makeText(this,tvTest.getText().toString(),Toast.LENGTH_SHORT).show();

}

class HomeReceiver extends BroadcastReceiver {

@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(MainActivity.this,“home键触发”,Toast.LENGTH_SHORT).show();

Log.d(TAG, “home键触发”);

}

}

}

}

private String TAG = “key”;

@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,tvTest.getText().toString(),Toast.LENGTH_SHORT).show();

break;

case KeyEvent.KEYCODE_BACK: //返回键

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: //数字键0

Log.d(TAG, “0—>”);

break;

case KeyEvent.KEYCODE_DPAD_LEFT: //向左键

Log.d(TAG, “left—>”);

break;

case KeyEvent.KEYCODE_DPAD_RIGHT: //向右键

Log.d(TAG, “right—>”);

break;

case KeyEvent.KEYCODE_INFO: //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);

}

@Override

protected void onDestroy() {

super.onDestroy();

if(homeReceiver!=null){

unregisterReceiver(homeReceiver);

}

}

}

然后我们就要想一下编码的过程和逻辑问题了,

1.播放视频的来源 本地 和 网络

2.播放视频的的停止播放、继续播放、重新播放

3.播放视频时的时间和进度计算

4.播放时候按遥控器左右键时,前进 后退

先想清楚这些问题,才能使编码过程中变得有条理

视频来源


本地:

我们可以在valuse文件夹下面创建一个raw文件夹,在里面放一个mp4短视频文件,(PS:至于在真机存储里面放一个视频,你只要播放路径指定这个视频所在地址,然后再加上文件的读写权限,因为我不是这么实现的,所以就不过多赘述了)

网络:

就是通过一个视频地址来播放视频,既然是通过网络来播放的,我们肯定要有联网的权限啊,在AndroidManifest.xml文件中添加联网许可权限

如下所示

布局文件


<?xml version="1.0" encoding="utf-8"?>

<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”>

<com.llw.androidtvdemo.view.MyVideoView

android:id=“@+id/video_view”

android:layout_width=“match_parent”

android:layout_height=“match_parent”

android:layout_gravity=“center” />

<RelativeLayout

android:background=“@drawable/shape_gradual_change”

android:layout_alignParentBottom=“true”

android:layout_width=“match_parent”

android:layout_height=“@dimen/dp_100”>

<LinearLayout

android:gravity=“center_vertical”

android:layout_margin=“@dimen/dp_10”

android:layout_alignParentBottom=“true”

android:orientation=“horizontal”

android:layout_width=“match_parent”

android:layout_height=“wrap_content”>

<TextView

android:id=“@+id/tv_play_time”

android:text=“00:00”

android:textSize=“@dimen/sp_24”

android:textColor=“@color/white”

android:layout_width=“wrap_content”

android:layout_height=“wrap_content”/>

<SeekBar

android:layout_marginLeft=“@dimen/dp_20”

android:layout_marginRight=“@dimen/dp_20”

android:id=“@+id/time_seekBar”

android:layout_width=“0dp”

android:layout_weight=“1”

android:layout_height=“wrap_content”

android:layout_centerInParent=“true”

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:text=“00:00”

android:textSize=“@dimen/sp_24”

android:textColor=“@color/white”

android:layout_width=“wrap_content”

android:layout_height=“wrap_content”/>

<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”/>

注释已经加在布局文件里面了,下面就不过多讲述了,布局文件中的自定义VideoView代码如下:

package com.llw.androidtvdemo.view;

import android.content.Context;

import android.net.Uri;

import android.util.AttributeSet;

import android.widget.VideoView;

import com.llw.androidtvdemo.view.util.SSlUtiles;

import javax.net.ssl.HttpsURLConnection;

/**

  • 自定义VideoView

*/

public class MyVideoView extends VideoView {

public MyVideoView(Context context) {

super(context);

}

public MyVideoView(Context context, AttributeSet attrs) {

super(context, attrs);

}

public MyVideoView(Context context, AttributeSet attrs, int defStyle) {

super(context, attrs, defStyle);

}

@Override

protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {

//super.onMeasure(widthMeasureSpec, heightMeasureSpec);

int width = getDefaultSize(getWidth(), widthMeasureSpec);

int height = getDefaultSize(getHeight(), heightMeasureSpec);

setMeasuredDimension(width, height);

}

@Override

public void setVideoURI(Uri uri) {

super.setVideoURI(uri);

try {

HttpsURLConnection.setDefaultSSLSocketFactory(SSlUtiles.createSSLSocketFactory());

HttpsURLConnection.setDefaultHostnameVerifier(new SSlUtiles.TrustAllHostnameVerifier());

} catch (Exception e) {

e.printStackTrace();

}

}

}

自定义VideoView中SSlUtils网络证书许可类代码如下:

package com.llw.androidtvdemo.view.util;

import java.security.SecureRandom;

import java.security.cert.CertificateException;

import java.security.cert.X509Certificate;

import javax.net.ssl.HostnameVerifier;

import javax.net.ssl.SSLContext;

import javax.net.ssl.SSLSession;

import javax.net.ssl.SSLSocketFactory;

import javax.net.ssl.TrustManager;

import javax.net.ssl.X509TrustManager;

public class SSlUtils {

/**

  • 默认信任所有的证书

  • xts

*/

public static SSLSocketFactory createSSLSocketFactory() {

SSLSocketFactory sSLSocketFactory = null;

try {

SSLContext sc = SSLContext.getInstance(“TLS”);

sc.init(null, new TrustManager[] { (TrustManager) new TrustAllManager() }, new SecureRandom());

sSLSocketFactory = sc.getSocketFactory();

} catch (Exception e) {

}

return sSLSocketFactory;

}

public static class TrustAllManager implements X509TrustManager {

@Override

public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {

}

@Override

public void checkServerTrusted(X509Certificate[] chain, String authType)

throws CertificateException {

}

@Override

public X509Certificate[] getAcceptedIssuers() {

return new X509Certificate[0];

}

}

public static class TrustAllHostnameVerifier implements HostnameVerifier {

public boolean verify(String hostname, SSLSession session) {

return true;

}

}

}

这个类主要是针对于 VideoView 无法播放此视频 问题,如果你没有这个问题的话,可以在MyVideoView去掉下面这一段代码:

@Override

public void setVideoURI(Uri uri) {

super.setVideoURI(uri);

try {

HttpsURLConnection.setDefaultSSLSocketFactory(SSlUtils.createSSLSocketFactory());

HttpsURLConnection.setDefaultHostnameVerifier(new SSlUtils.TrustAllHostnameVerifier());

} catch (Exception e) {

e.printStackTrace();

}

}

然后来看MainActivity中的代码,通过注解的方式我的控件已经不需要声明和findById了。

首先配置一下我们的VideoVIew

/**

  • 初始化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));//设置视频总时间,stringForTime是写的一个时间装换方法,下面会提到

// 开始线程,更新进度条的刻度

handler.postDelayed(runnable, 0);

timeSeekBar.setMax(videoView.getDuration());

//视频加载完成,准备好播放视频的回调

videoView.start();

}

});

}

上面的初始化中用到了一个线程,线程代码如下:

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);//播放过程中0.5秒执行一次

}

};

然后是onCreate()方法

@Override

protected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.activity_main);

ButterKnife.bind(this);//注释然后自动添加的

timeSeekBar.setOnSeekBarChangeListener(onSeekBarChangeListener);//添加进度条的变化监听

initVideo();//初始化VideoView

//videoView播放完成监听

videoView.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {

@Override

public void onCompletion(MediaPlayer mp) {

key = 1;//这是一个全局变量,用于控制遥控单击确定或者ok键时,是暂停继续还是重新播放,1则是重新播放视频

btnRestartPlay.setVisibility(View.VISIBLE);//显示黑色背景,布局文件中注释提到了

layFinishBg.setVisibility(View.VISIBLE);//显示白色重播图标,布局文件中注释提到了

}

});

//videoView播放异常监听,类似于 此视频无法播放 这样的错误提示

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());

}

控制视频是 播放还是暂停 或者是重播

/**

  • 控制视频是 播放还是暂停 或者是重播

  • @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();//当我们选择继续播放之后,就不能让这个图标一直显示下去,但是又不能马上消失,这样很突兀,所以用了延时1.5秒隐藏,比较合理,这个方法后面会贴出来。

}

break;

case 1://重新播放

initVideo();

btnRestartPlay.setVisibility(View.GONE);//白色重播图标隐藏

layFinishBg.setVisibility(View.GONE);//黑色背景隐藏

key = 0;//重新播放之后,我们再将key置为0,这样就不会影响到下一次视频播放过程中的暂停和继续的监听操作了

break;

}

延时1.5秒隐藏

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”;

/**

最后

分享一份工作1到5年以上的Android程序员架构进阶学习路线体系,希望能对那些还在从事Android开发却还不知道如何去提升自己的,还处于迷茫的朋友!

  • 阿里P7级Android架构师技术脑图;查漏补缺,体系化深入学习提升

  • **全套体系化高级架构视频;**七大主流技术模块,视频+源码+笔记

有任何问题,欢迎广大网友一起来交流

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化学习资料的朋友,可以戳这里获取

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

进度条监听

/**

  • 进度条监听

*/

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”;

/**

最后

分享一份工作1到5年以上的Android程序员架构进阶学习路线体系,希望能对那些还在从事Android开发却还不知道如何去提升自己的,还处于迷茫的朋友!

  • 阿里P7级Android架构师技术脑图;查漏补缺,体系化深入学习提升

    [外链图片转存中…(img-dcfckjGN-1714296515208)]

  • **全套体系化高级架构视频;**七大主流技术模块,视频+源码+笔记

[外链图片转存中…(img-FvP2KAlp-1714296515209)]

有任何问题,欢迎广大网友一起来交流

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化学习资料的朋友,可以戳这里获取

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

  • 14
    点赞
  • 28
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
Android tv 视频会议代码是指在 Android TV 平台上开发视频会议应用程序所使用的代码。视频会议是一种通过网络连接实现远程多人参与的会议形式,利用视频和音频通信技术,使不同地区的人们能够方便地进行实时会议。 Android TV 是 Google 开发的专为电视设备设计的操作系统,类似于智能手机上的 Android 操作系统,但做了相应的优化和适配,以适应电视设备的特点。 开发 Android TV 视频会议应用的代码主要包括以下几个方面: 1. 用户界面代码:Android TV 应用通常使用 Leanback Library 来构建用户界面,它提供了电视友好的布局和控件,使用户能够方便地通过遥控器进行操作。视频会议应用的界面应该包含用户登录、创建或加入会议、显示视频画面、控制音频、发送消息等功能。 2. 视频和音频通信代码:视频会议应用需要利用网络传输技术,将与会者的音视频数据进行实时传输。一般可以选择使用 WebRTC 技术,通过实时传输协议(RTP)和实时传输控制协议(RTCP)进行音视频通信。代码中需要实现音视频采集、编码、解码、传输和播放等功能。 3. 数据通信代码:视频会议应用通常需要支持参会者之间的文字消息交流。可以利用 WebSocket 技术建立与服务器的长连接,通过服务器转发文本消息。代码中需要实现与服务器的通信,处理收发消息的逻辑。 4. 会议管理代码:视频会议应用需要支持创建会议、设置会议参数、邀请他人参加会议、退出会议等功能。代码中需要实现会议管理的逻辑,包括创建会议房间、生成会议链接、管理参会者信息等。 总之,开发 Android TV 视频会议应用的代码需要涵盖用户界面、视频和音频通信、数据通信以及会议管理等方面的功能和逻辑。通过合理的代码设计和实现,可以让用户在 Android TV 平台上方便地参与视频会议,实现远程协作和沟通。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值