【问题标题】:What is the best way of using Youtube API in Android?在 Android 中使用 Youtube API 的最佳方式是什么?
【发布时间】:2021-07-28 13:12:20
【问题描述】:

我想在我的 Android 应用中实现 YouTube API,但有一些限制。

  1. 播放/暂停按钮应该可见。 (DEFAULT 和 MINIMAL 风格都支持这个)

  2. 时间栏应该是可见且可操作的。 (在MINIMAL风格的情况下,时间条是可见的,但点击它没有任何效果)

  3. 用户不应直接分享视频或打开 YouTube 应用。 (在这种情况下,DEFAULT 不是选项)

MINIMAL 样式本来可以完美运行,但时间栏不起作用。我曾考虑使用自定义 Seekbar,但当视频处于全屏模式时它不可见。有什么建议吗?

【问题讨论】:

  • 您可以通过将 seekBar 放在带有 YouTube 播放器的 LinearLayout 中并在全屏定向期间处理其生命周期来使 seekBar 在全屏中可见。
  • 它不允许任何其他小部件或布局在其之上...而且它需要足够的屏幕空间。
  • 是的,我也遇到过这个问题!我想我能帮上忙……

标签: android youtube youtube-api android-youtube-api


【解决方案1】:

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。

我希望它适用于每个人!因为我花了一个多小时?。

【讨论】:

  • 横向视图会不会产生任何问题,因为还提到 youtubeview 应该有足够的空间来容纳视频?
  • 不,不会,我们只需要确保我们不会通过在其上方创建任何视图来中断 youtube 视图。该代码已通过方向更改进行了全面测试,并且不会中断播放视频的连续性。
  • 是的,如果有效,别忘了点赞。 ?
猜你喜欢
  • 2020-06-21
  • 1970-01-01
  • 2016-09-29
  • 1970-01-01
  • 2020-06-23
  • 1970-01-01
  • 2013-11-29
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多