youtube API 面临的两个主要问题是:
1.当我们将 YoutubePlayerStlye 设置为 MINIMAL 样式时,我们无法访问搜索栏(时间栏)。
2. 相关视频在视频末尾弹出。
解决方案 1:
所以我们必须构建一个自定义搜索栏来访问时间进度并根据需要更新(拖动)搜索栏上的时间。但是 youtubePlayer 不允许在其上方/顶部有视图(小部件),当它发生时 youtubePlayer 会暂停视频。
所以解决方案是在youtubePlayer下方绘制一个自定义搜索栏。
我们可以通过这个布局来实现:
youtube_player_layout.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@android:color/black"
android:gravity="center"
android:orientation="vertical"
tools:context=".features.video.YoutubePlayerActivity">
<!-- youtube player view to play videos -->
<com.google.android.youtube.player.YouTubePlayerView
android:id="@+id/youtube_player_view"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:background="@android:color/black"
android:focusable="true" />
<!-- Full layout of seekbar, you can also use a constraintLayout-->
<LinearLayout
android:id="@+id/video_control_bottom"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="bottom"
android:background="@android:color/black"
android:gravity="center"
android:orientation="horizontal">
<!-- Seekbar to display video progress -->
<SeekBar
android:id="@+id/video_seekbar"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginTop="10dp"
android:layout_marginEnd="10dp"
android:layout_marginBottom="10dp"
android:layout_weight="6"
android:max="100"
android:progress="0" />
<!-- Time display -->
<TextView
android:id="@+id/play_time"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="5dp"
android:gravity="center"
android:text="--:--"
android:textColor="@android:color/white" />
<!-- FullScreen button -->
<ImageButton
android:id="@+id/bt_fullscreen"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@null"
android:src="@drawable/ic_fullscreen" />
</LinearLayout>
</LinearLayout>
纵向视图:
风景:
此自定义搜索栏还包含一个全屏按钮 (ImageButton),您可以在 方向更改 上从矢量资产和处理 kotlin/Java 代码中添加该按钮。
解决方案 2:
我在网上找到的常用解决方案是在视频结束时将 seekTime 设置为零。
@Override
public void onVideoEnded() {
if (null == mPlayer) return;
mPlayer.seekToMillis(0);
mPlayer.pause();
}
但是这个解决方案并不是最优的,因为即使你使用它,相关的视频有时也会显示半秒钟,看起来并不好看。
因此,简单的解决方案是永远不要让视频结束,这样就不会在最后显示相关视频。为了实现它,我们必须在视频结束前约 1.2 秒停止视频(我认为可以从视频中牺牲约 1.2 秒,但这取决于用例)。
if (null == mPlayer) return;
if (mPlayer.getCurrentTimeMillis() >= (mPlayer.getDurationMillis() - 1200L)) {
mPlayer.seekToMillis(0);
mPlayer.pause();
}
我还将发布 youtubePlayer 的完整 Java 代码。
YoutubePlayerActivity.java
import android.content.pm.ActivityInfo;
import android.content.res.Configuration;
import android.os.Bundle;
import android.os.Handler;
import android.util.Log;
import android.view.WindowManager;
import android.widget.ImageButton;
import android.widget.SeekBar;
import android.widget.TextView;
import android.widget.Toast;
import androidx.core.content.ContextCompat;
import com.google.android.youtube.player.YouTubeBaseActivity;
import com.google.android.youtube.player.YouTubeInitializationResult;
import com.google.android.youtube.player.YouTubePlayer;
import com.google.android.youtube.player.YouTubePlayerView;
import com.syncnicia.theguideapp.R;
public class YoutubePlayerActivity extends YouTubeBaseActivity {
private static final String TAG = "YoutubePlayerActivity";
private String videoID;
private YouTubePlayerView youTubePlayerView;
private TextView mPlayTimeTextView;
private YouTubePlayer mPlayer;
private Handler mHandler = null;
private SeekBar mSeekBar;
private ImageButton btFullScreen;
private int seekTime = 0;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_youtube_player);
//get the video id
videoID = getIntent().getStringExtra("video_id");
youTubePlayerView = findViewById(R.id.youtube_player_view);
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);
btFullScreen = findViewById(R.id.bt_fullscreen);
btFullScreen.setOnClickListener(view -> {
int orientation = this.getResources().getConfiguration().orientation;
if (null != mPlayer) {
if (mPlayer.isPlaying()) {
mPlayer.pause();
}
if (orientation == Configuration.ORIENTATION_PORTRAIT) {
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
} else {
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
}
mPlayer.play();
}
});
mPlayTimeTextView = (TextView) findViewById(R.id.play_time);
mSeekBar = (SeekBar) findViewById(R.id.video_seekbar);
mSeekBar.setOnSeekBarChangeListener(mVideoSeekBarChangeListener);
mHandler = new Handler();
initializeYoutubePlayer();
}
@Override
protected void onResume() {
super.onResume();
if (mPlayer != null) {
initializeYoutubePlayer();
}
}
private void initializeYoutubePlayer() {
youTubePlayerView.initialize(String.valueOf(getText(R.string.youtube_key)), new YouTubePlayer.OnInitializedListener() {
@Override
public void onInitializationSuccess(YouTubePlayer.Provider provider, YouTubePlayer youTubePlayer,
boolean wasRestored) {
if (null == youTubePlayer) return;
mPlayer = youTubePlayer;
displayCurrentTime();
//if initialization success then load the video id to youtube player
if (!wasRestored) {
if (videoID != null) {
youTubePlayer.loadVideo(videoID, seekTime);
}
}
//set the player style here: like CHROMELESS, MINIMAL, DEFAULT
youTubePlayer.setPlayerStyle(YouTubePlayer.PlayerStyle.MINIMAL);
// Add listeners to YouTubePlayer instance
youTubePlayer.setPlayerStateChangeListener(mPlayerStateChangeListener);
youTubePlayer.setPlaybackEventListener(playbackEventListener);
}
private final YouTubePlayer.PlaybackEventListener playbackEventListener = new YouTubePlayer.PlaybackEventListener() {
@Override
public void onPlaying() {
mHandler.postDelayed(runnable, 100);
displayCurrentTime();
}
@Override
public void onPaused() {
mHandler.removeCallbacks(runnable);
}
@Override
public void onStopped() {
mHandler.removeCallbacks(runnable);
}
@Override
public void onBuffering(boolean b) {
}
@Override
public void onSeekTo(int i) {
mHandler.postDelayed(runnable, 100);
}
};
@Override
public void onInitializationFailure(YouTubePlayer.Provider arg0, YouTubeInitializationResult arg1) {
//print or show error if initialization failed
Log.e(TAG, "Youtube Player View initialization failed:" + arg1);
if (arg1.toString().equals("SERVICE_VERSION_UPDATE_REQUIRED")) {
Toast.makeText(YoutubePlayerActivity.this, "Please Update your Youtube to latest version.", Toast.LENGTH_SHORT).show();
}
}
});
}
YouTubePlayer.PlayerStateChangeListener mPlayerStateChangeListener = new YouTubePlayer.PlayerStateChangeListener() {
@Override
public void onLoading() {
}
@Override
public void onLoaded(String s) {
}
@Override
public void onAdStarted() {
}
@Override
public void onVideoStarted() {
displayCurrentTime();
}
@Override
public void onVideoEnded() {
if (null == mPlayer) return;
mPlayer.seekToMillis(0);
mPlayer.pause();
}
@Override
public void onError(YouTubePlayer.ErrorReason errorReason) {
}
};
SeekBar.OnSeekBarChangeListener mVideoSeekBarChangeListener = new SeekBar.OnSeekBarChangeListener() {
@Override
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
}
@Override
public void onStartTrackingTouch(SeekBar seekBar) {
}
@Override
public void onStopTrackingTouch(SeekBar seekBar) {
if (null == mPlayer) return;
long lengthPlayed = (mPlayer.getDurationMillis() * seekBar.getProgress()) / 100;
mPlayer.seekToMillis((int) lengthPlayed);
}
};
private void displayCurrentTime() {
//To stop video 1.2s before it ends
if (null == mPlayer) return;
if (mPlayer.getCurrentTimeMillis() >= (mPlayer.getDurationMillis() - 1200L)) {
mPlayer.seekToMillis(0);
mPlayer.pause();
}
String formattedTime = formatTime(mPlayer.getDurationMillis() - mPlayer.getCurrentTimeMillis());
mPlayTimeTextView.setText(formattedTime);
int playPercent = (int) (((float) mPlayer.getCurrentTimeMillis() / (float) mPlayer.getDurationMillis()) * 100);
// update live progress
mSeekBar.setProgress(playPercent);
}
private String formatTime(int millis) {
int seconds = millis / 1000;
int minutes = seconds / 60;
int hours = minutes / 60;
return (hours == 0 ? "" : hours + ":") + String.format("%02d:%02d", minutes % 60, seconds % 60);
}
private Runnable runnable = new Runnable() {
@Override
public void run() {
displayCurrentTime();
mHandler.postDelayed(this, 100);
}
};
@Override
public void onDestroy() {
if (mPlayer != null) {
mPlayer.release();
}
super.onDestroy();
}
@Override
protected void onPause() {
super.onPause();
if (mPlayer != null) {
seekTime = mPlayer.getCurrentTimeMillis();
}
}
@Override
protected void onStop() {
if (mPlayer != null) {
mPlayer.release();
}
super.onStop();
}
// To switch fullScreen icon when orientation changes
@Override
public void onConfigurationChanged(@NonNull Configuration newConfig) {
super.onConfigurationChanged(newConfig);
if (newConfig.orientation == Configuration.ORIENTATION_PORTRAIT) {
btFullScreen.setImageDrawable(ContextCompat.getDrawable(this, R.drawable.ic_fullscreen));
} else if(newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE){
btFullScreen.setImageDrawable(ContextCompat.getDrawable(this, R.drawable.ic_fullscreen_exit));
}
}
}
我认为直接复制粘贴和添加drawables就足够了。
如果代码仍然令人困惑,请告诉我,我会添加更多的 cmets。
我希望它适用于每个人!因为我花了一个多小时?。