1. 程式人生 > >Android開發-基於ijkplayer框架開發網路電視直播播放器的實現

Android開發-基於ijkplayer框架開發網路電視直播播放器的實現

前 言

ijkplayer框架是由B站在GitHub開源的一款比較好用的開源網路播放器框架,它能支援在Android、IOS等平臺上編譯移植使用。而且支援多種視訊格式的播放,而且編碼的速度比傳統的開源網路播放器還要快。除此之外,ijkplayer框架支援網路視訊播放時彈幕的推送等功能。

開發環境

Android Studio 3.1.2
JDK 1.8

開發前準備

在Android Studio新建的專案裡引入ijkplayer框架(ijkplayer框架GitHub下載地址)作為依賴項,並在build.gradle(Module:app)引入檔案。如下圖所示。
這裡寫圖片描述

implementation project(':ijkplayer-java')

在主專案的下\app\src\main\目錄下新建一個jniLibs目錄,並把ijkplayer框架需要支援的.so檔案拷貝貼上進去,如下圖所示。
這裡寫圖片描述

編碼與實現

在Android Studio的\res\layout目錄下新建一個activity_play.xml檔案作為視訊播放器的顯示介面,佈局程式碼如下。

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools" android:id="@+id/activity_play" android:layout_width="match_parent" android:layout_height="match_parent" android:background="@android:color/black" android:gravity="center" android:keepScreenOn="true" tools:context=".player.PlayActivity"
> <FrameLayout android:id="@+id/fl_surface_container" android:layout_width="match_parent" android:layout_height="match_parent"> <com.fukaimei.cloudtv.widget.media.IjkVideoView android:id="@+id/video_view" android:layout_width="match_parent" android:layout_height="match_parent" android:layout_gravity="center" /> <RelativeLayout android:id="@+id/rl_loading_layout" android:layout_width="match_parent" android:layout_height="match_parent"> <TextView android:id="@+id/tv_loading_info" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_below="@+id/pb_loading" android:layout_centerInParent="true" android:layout_marginTop="@dimen/dimen_12dp" android:textColor="@color/white" android:textSize="@dimen/dimen_32sp" /> <!--android:indeterminate 表示不確定的進度--> <ProgressBar android:id="@+id/pb_loading" android:layout_width="@dimen/dimen_120dp" android:layout_height="@dimen/dimen_120dp" android:layout_centerInParent="true" android:layout_marginTop="@dimen/dimen_120dp" android:indeterminate="false" android:indeterminateDrawable="@drawable/video_loading" android:padding="@dimen/dimen_10dp" /> </RelativeLayout> <TextView android:id="@+id/tv_horiontal_gesture" android:layout_width="match_parent" android:layout_height="wrap_content" android:gravity="center" android:shadowColor="@color/black" android:shadowDx="1.0" android:shadowDy="1.0" android:textColor="@color/white" android:textSize="@dimen/dimen_64sp" android:textStyle="bold" android:visibility="gone" /> <TextView android:id="@+id/tv_vertical_gesture" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center" android:background="@drawable/black_bg" android:gravity="center_horizontal" android:paddingBottom="@dimen/dimen_40dp" android:paddingLeft="@dimen/dimen_100dp" android:paddingRight="@dimen/dimen_100dp" android:paddingTop="@dimen/dimen_40dp" android:textColor="@color/white" android:textSize="@dimen/dimen_64sp" android:textStyle="bold" android:visibility="gone" /> <FrameLayout android:id="@+id/fl_player_top_container" android:layout_width="match_parent" android:layout_height="@dimen/dimen_70dp" android:layout_gravity="top" android:background="@color/player_panel_background_color" android:paddingBottom="@dimen/dimen_10dp" android:paddingTop="@dimen/dimen_10dp"> <LinearLayout android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center_vertical" android:orientation="horizontal"> <ImageView android:id="@+id/iv_player_close" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center_vertical" android:paddingLeft="@dimen/dimen_15dp" android:paddingRight="@dimen/dimen_15dp" android:src="@drawable/titlebar_return_white" /> <TextView android:id="@+id/tv_player_video_name" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center_vertical" android:ellipsize="end" android:singleLine="true" android:textColor="@color/white" android:textSize="@dimen/dimen_32sp" /> </LinearLayout> <LinearLayout android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="right|center_vertical" android:orientation="horizontal"> <TextView android:id="@+id/tv_sys_time" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center_vertical" android:layout_marginRight="@dimen/dimen_10dp" android:textColor="@color/white" android:textSize="@dimen/dimen_32sp" /> <ImageView android:id="@+id/iv_battery" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center_vertical" android:layout_marginRight="@dimen/dimen_10dp" /> </LinearLayout> </FrameLayout> <ImageView android:id="@+id/iv_player_center_pause" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center" android:background="@drawable/player_pause_selector" android:visibility="gone" /> </FrameLayout> <LinearLayout android:id="@+id/ll_player_bottom_layout" android:layout_width="match_parent" android:layout_height="@dimen/dimen_70dp" android:layout_alignParentBottom="true" android:background="@color/player_panel_background_color" android:gravity="center" android:orientation="horizontal"> <CheckBox android:id="@+id/cb_play_pause" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginLeft="@dimen/dimen_26dp" android:button="@drawable/player_playbtn_selector" android:checked="true" /> <ImageView android:id="@+id/iv_next_video" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginLeft="@dimen/dimen_10dp" android:src="@drawable/panel_next_selector" /> <TextView android:id="@+id/tv_current_video_time" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center_vertical" android:textColor="@color/white" android:textSize="@dimen/dimen_20sp" /> <SeekBar android:id="@+id/sb_player_seekbar" style="@style/playerSeekBarStyle" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_gravity="center_vertical" android:layout_weight="1.0" android:indeterminate="false" android:max="100" android:paddingLeft="@dimen/dimen_10dp" android:paddingRight="@dimen/dimen_10dp" android:progress="0" android:thumbOffset="@dimen/dimen_10dp" /> <TextView android:id="@+id/tv_total_video_time" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginLeft="@dimen/dimen_10dp" android:textColor="@color/white" android:textSize="@dimen/dimen_20sp" /> <TextView android:id="@+id/tv_bitstream" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginLeft="@dimen/dimen_10dp" android:layout_marginRight="@dimen/dimen_10dp" android:textColor="@color/white" android:textSize="@dimen/dimen_20sp" /> </LinearLayout> </RelativeLayout>

在Android Studio的app\src\main\java\目錄下新建一個PlayActivity,java檔案作為處理視訊播放器的邏輯程式碼,邏輯程式碼如下。

package com.fukaimei.cloudtv.player;

import android.app.Activity;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.SharedPreferences;
import android.graphics.drawable.Drawable;
import android.media.AudioManager;
import android.net.Uri;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.support.v7.preference.PreferenceManager;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import android.widget.CheckBox;
import android.widget.FrameLayout;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.RelativeLayout;
import android.widget.SeekBar;
import android.widget.TextView;

import com.fukaimei.cloudtv.R;
import com.fukaimei.cloudtv.base.BaseActivity;
//import com.fukaimei.cloudtv.detail.AlbumDetailActivity;
//import com.fukaimei.cloudtv.model.sohu.Video;
import com.fukaimei.cloudtv.utils.DateUtils;
import com.fukaimei.cloudtv.utils.SysUtils;
import com.fukaimei.cloudtv.widget.media.IjkVideoView;

import java.text.NumberFormat;
import java.util.Formatter;
import java.util.Locale;

import tv.danmaku.ijk.media.player.IMediaPlayer;
import tv.danmaku.ijk.media.player.IjkMediaPlayer;

public class PlayActivity extends BaseActivity implements GestureDetectorController.IGestureListener {

    private static final String TAG = PlayActivity.class.getSimpleName();
    private static final int CHECK_TIME = 1;
    private static final int CHECK_BATTERY = 2;
    private static final int CHECK_PROGRESS = 3;
    private static final int AUTO_HIDE_TIME = 10000;
    private static final int AFTER_DRAGGLE_HIDE_TIME = 3000;
    private String mUrl;
    private int mStreamType;
    private int mCurrentPosition;
//    private Video mVideo;
    private IjkVideoView mVideoView;
    private RelativeLayout mLoadingLayout;
    private TextView mLoadingText;
    private FrameLayout mTopLayout;
    private LinearLayout mBottomLayout;
    private ImageView mBackButton;
    private TextView mVideoNameView;
    private TextView mSysTimeView;
    private ImageView mBigPauseButton;
    private CheckBox mPlayOrPauseButton;
    private TextView mVideoCurrentTime;
    private TextView mVideoTotalTime;
    private TextView mBitStreamView;
    private EventHandler mEventHandler;
    private boolean mIsPanelShowing = false;
    private int mBatteryLevel;
    private ImageView mBatteryView;
    private boolean mIsMove = false;//是否在螢幕上滑動
    private SeekBar mSeekBar;
    private Formatter mFormatter;
    private StringBuilder mFormatterBuilder;
    private boolean mIsDragging;
    private GestureDetectorController mGestureController;
    private TextView mDragHorizontalView;
    private TextView mDragVerticalView;
    private long mScrollProgress;
    private boolean mIsHorizontalScroll;
    private boolean mIsVerticalScroll;
    private int mCurrentLight;
    private int mMaxLight = 255;
    private int mCurrentVolume;
    private int mMaxVolume = 10;
    private AudioManager mAudioManager;
    private String mLiveTitle;//直播節目標題

    @Override
    protected int getLayoutId() {
        return R.layout.activity_play;
    }

    @Override
    protected void onResume() {
        super.onResume();
    }

    @Override
    protected void onPause() {
        super.onPause();
        if (mBatteryReceiver != null) {
            unregisterReceiver(mBatteryReceiver);
            mBatteryReceiver = null;
        }
        //釋放audiofocus
        mAudioManager.abandonAudioFocus(null);
    }

    /**
     * 通過廣播獲取系統電量情況
     */
    private BroadcastReceiver mBatteryReceiver = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
            mBatteryLevel = intent.getIntExtra("level", 0);
            Log.d(TAG, ">> mBatteryReceiver onReceive mBatteryLevel=" + mBatteryLevel);
        }
    };

    @Override
    public void onScrollStart(GestureDetectorController.ScrollType type) {
        mIsMove = true;
        switch (type) {
            case HORIZONTAL:
                mDragHorizontalView.setVisibility(View.VISIBLE);
                mScrollProgress = -1;
                mIsHorizontalScroll = true;//水平滑動標識
                break;
            case VERTICAL_LEFT:
                setComposeDrawableAndText(mDragVerticalView, R.drawable.ic_light, this);
                mDragVerticalView.setVisibility(View.VISIBLE);
                updateVerticalText(mCurrentLight, mMaxLight);
                mIsVerticalScroll = true;
                break;
            case VERTICAL_RIGH:
                if (mCurrentVolume > 0) {
                    setComposeDrawableAndText(mDragVerticalView, R.drawable.volume_normal, this);
                } else {
                    setComposeDrawableAndText(mDragVerticalView, R.drawable.volume_no, this);
                }
                mDragVerticalView.setVisibility(View.VISIBLE);
                updateVerticalText(mCurrentVolume, mMaxVolume);
                mIsVerticalScroll = true;
                break;
        }
    }

    //用於組合圖片及文字
    private void setComposeDrawableAndText(TextView textView, int drawableId, Context context) {
        Drawable drawable = context.getResources().getDrawable(drawableId);
        //這四個引數表示把drawable繪製在矩形區域
        drawable.setBounds(0, 0, drawable.getMinimumWidth(), drawable.getMinimumHeight());
        //設定圖片在文字的上方
        //The Drawables must already have had drawable.setBounds called.
        textView.setCompoundDrawables(null, drawable, null, null);
    }

    //更新垂直方向上滑動時的百分比
    private void updateVerticalText(int current, int total) {
        NumberFormat formater = NumberFormat.getPercentInstance();
        formater.setMaximumFractionDigits(0);//設定整數部分允許最大小數位 66.5%->66%
        String percent = formater.format((double) (current) / (double) total);
        mDragVerticalView.setText(percent);
    }

    //更新水平方向seek的進度, duration表示變化後的duration
    private void updateHorizontalText(long duration) {
        String text = stringForTime((int) duration) + "/" + stringForTime(mVideoView.getDuration());
        mDragHorizontalView.setText(text);
    }

    // 更新進度
    @Override
    public void onScrollHorizontal(float x1, float x2) {
        int width = getResources().getDisplayMetrics().widthPixels;
        int MAX_SEEK_STEP = 300000;//最大滑動5分鐘
        int offset = (int) (x2 / width * MAX_SEEK_STEP) + mVideoView.getCurrentPosition();
        long progress = Math.max(0, Math.min(mVideoView.getDuration(), offset));
        mScrollProgress = progress;
        updateHorizontalText(progress);
    }

    @Override
    public void onScrollVerticalLeft(float y1, float y2) {
        int height = getResources().getDisplayMetrics().heightPixels;
        int offset = (int) (mMaxLight * y1) / height;
        if (Math.abs(offset) > 0) {
            mCurrentLight += offset;//得到變化後的亮度
            mCurrentLight = Math.max(0, Math.min(mMaxLight, mCurrentLight));
            // 更新系統亮度
            SysUtils.setBrightness(this, mCurrentLight);
            SharedPreferences.Editor editor = PreferenceManager.getDefaultSharedPreferences(this).edit();
            editor.putInt("shared_preferences_light", mCurrentLight);
            editor.commit();
            updateVerticalText(mCurrentLight, mMaxLight);
        }
    }

    @Override
    public void onScrollVerticalRight(float y1, float y2) {
        int height = getResources().getDisplayMetrics().heightPixels;
        int offset = (int) (mMaxVolume * y1) / height;
        if (Math.abs(offset) > 0) {
            mCurrentVolume += offset;//得到變化後的聲音
            mCurrentVolume = Math.max(0, Math.min(mMaxVolume, mCurrentVolume));
            // 更新系統聲音
            mAudioManager.setStreamVolume(AudioManager.STREAM_MUSIC, mCurrentVolume / 10, 0);
            updateVerticalText(mCurrentVolume, mMaxVolume);
        }
    }

    class EventHandler extends Handler {
        public EventHandler(Looper looper) {
            super(looper);
        }

        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            switch (msg.what) {
                case CHECK_TIME:
                    runOnUiThread(new Runnable() {
                        @Override
                        public void run() {
                            mSysTimeView.setText(DateUtils.getCurrentTime());
                        }
                    });
                    break;
                case CHECK_BATTERY:
                    runOnUiThread(new Runnable() {
                        @Override
                        public void run() {
                            setCurrentBattery();
                        }
                    });
                    break;
                case CHECK_PROGRESS:
                    runOnUiThread(new Runnable() {
                        @Override
                        public void run() {
                            long duration = mVideoView.getDuration();
                            long nowduration = (mSeekBar.getProgress() * duration) / 1000L;
                            mVideoCurrentTime.setText(stringForTime((int) nowduration));
                        }
                    });
                    break;
            }
        }
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        if (event.getAction() == MotionEvent.ACTION_UP) {
            if (mIsMove == false) {
                toggleTopAndBottomLayout();
            } else {
                mIsMove = false;
            }
            //水平方向,up時,seek到對應位置播放
            if (mIsHorizontalScroll) {
                mIsHorizontalScroll = false;
                mVideoView.seekTo((int) mScrollProgress);
                //一次down,up結束後mDragHorizontalView隱藏
                mDragHorizontalView.setVisibility(View.GONE);
            }
            if (mIsVerticalScroll) {
                mDragVerticalView.setVisibility(View.GONE);
                mIsVerticalScroll = false;
            }
        }
        return mGestureController.onTouchEvent(event);
    }

    @Override
    protected void initView() {
        mUrl = getIntent().getStringExtra("url");
//        mUrl = "http://ivi.bupt.edu.cn/hls/cctv1.m3u8";
//        mLiveTitle = "CCTV-1綜合";
        mLiveTitle = getIntent().getStringExtra("title");
        mStreamType = getIntent().getIntExtra("type", 0);
        mCurrentPosition = getIntent().getIntExtra("currentPosition", 0);
//        mVideo = getIntent().getParcelableExtra("video");
//        Log.d(TAG, ">> ulr " + mUrl + ", mStreamType " + mStreamType + ", mCurrentPosition " + mCurrentPosition);
//        Log.d(TAG, ">> video " + mVideo);
        mEventHandler = new EventHandler(Looper.myLooper());
        initAudio();
        initLight();
        initGesture();
        initTopAndBottomView();
        initCenterView();
        initListener();
        //init player
        mVideoView = bindViewId(R.id.video_view);
        IjkMediaPlayer.loadLibrariesOnce(null);
        IjkMediaPlayer.native_profileBegin("libijkplayer.so");
        mLoadingLayout = bindViewId(R.id.rl_loading_layout);
        mLoadingText = bindViewId(R.id.tv_loading_info);
        mLoadingText.setText("正在載入中...");
        mVideoView.setVideoURI(Uri.parse(mUrl));
        mVideoView.setOnPreparedListener(new IMediaPlayer.OnPreparedListener() {
            @Override
            public void onPrepared(IMediaPlayer mp) {
                mVideoView.start();
            }
        });
        mVideoView.setOnInfoListener(new IMediaPlayer.OnInfoListener() {
            @Override
            public boolean onInfo(IMediaPlayer mp, int what, int extra) {
                switch (what) {
                    case IjkMediaPlayer.MEDIA_INFO_BUFFERING_START:
                        mLoadingLayout.setVisibility(View.VISIBLE);
                        break;
                    case IjkMediaPlayer.MEDIA_INFO_VIDEO_RENDERING_START:
                    case IjkMediaPlayer.MEDIA_INFO_BUFFERING_END:
                        mLoadingLayout.setVisibility(View.GONE);
                        break;
                }
                return false;
            }
        });
        registerReceiver(mBatteryReceiver, new IntentFilter(Intent.ACTION_BATTERY_CHANGED));
        toggleTopAndBottomLayout();
    }

    private void initCenterView() {
        mDragHorizontalView = bindViewId(R.id.tv_horiontal_gesture);
        mDragVerticalView = bindViewId(R.id.tv_vertical_gesture);
    }

    private void initAudio() {
        mAudioManager = (AudioManager) getSystemService(AUDIO_SERVICE);
        setVolumeControlStream(AudioManager.STREAM_MUSIC);
        mAudioManager.requestAudioFocus(null, AudioManager.STREAM_MUSIC, AudioManager.AUDIOFOCUS_GAIN);
        mMaxVolume = mAudioManager.getStreamMaxVolume(AudioManager.STREAM_MUSIC) * 10;// 系統聲音取值是0-10,*10為了和百分比相關
        mCurrentVolume = mAudioManager.getStreamVolume(AudioManager.STREAM_MUSIC) * 10;
    }

    private void initLight() {
        mCurrentLight = SysUtils.getDefaultBrightness(this);
        if (mCurrentLight == -1) {//獲取不到亮度sharedpreferences檔案
            mCurrentLight = SysUtils.getBrightness(this);
        }
    }

    private void initGesture() {
        mGestureController = new GestureDetectorController(this, this);
    }

    private void initTopAndBottomView() {
        mTopLayout = bindViewId(R.id.fl_player_top_container);
        mBottomLayout = bindViewId(R.id.ll_player_bottom_layout);
        mBackButton = bindViewId(R.id.iv_player_close);//返回按鈕
        mVideoNameView = bindViewId(R.id.tv_player_video_name);//video標題
        mBatteryView = bindViewId(R.id.iv_battery);
        mSysTimeView = bindViewId(R.id.tv_sys_time);//系統時間
        mBigPauseButton = bindViewId(R.id.iv_player_center_pause);//螢幕中央暫停按鈕
        mPlayOrPauseButton = bindViewId(R.id.cb_play_pause);//底部播放暫停按鈕
        mVideoCurrentTime = bindViewId(R.id.tv_current_video_time);//當前播放進度
        mVideoTotalTime = bindViewId(R.id.tv_total_video_time);//視訊總時長
        mBitStreamView = bindViewId(R.id.tv_bitstream);//碼流
        mSeekBar = bindViewId(R.id.sb_player_seekbar);
        mSeekBar.setMax(1000);
        mSeekBar.setOnSeekBarChangeListener(mSeekBarChangeListener);
        mFormatterBuilder = new StringBuilder();
        mFormatter = new Formatter(mFormatterBuilder, Locale.getDefault());
    }

    private void initListener() {
        mBackButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                finish();
            }
        });
        mBigPauseButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                mVideoView.start();
                updatePlayPauseStatus(true);
            }
        });
        mPlayOrPauseButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                handlePlayPause();
            }
        });
    }

    private void toggleTopAndBottomLayout() {
        if (mIsPanelShowing) {
            hideTopAndBottomLayout();
        } else {
            showTopAndBottomLayout();
            //先顯示,沒有任何操作,就5s後隱藏
            mEventHandler.postDelayed(new Runnable() {
                @Override
                public void run() {
                    hideTopAndBottomLayout();
                }
            }, AUTO_HIDE_TIME);
        }
    }

    private void showTopAndBottomLayout() {
        mIsPanelShowing = true;
        mTopLayout.setVisibility(View.VISIBLE);
        mBottomLayout.setVisibility(View.VISIBLE);
        updateProgress();
        if (mEventHandler != null) {
            mEventHandler.removeMessages(CHECK_TIME);
            Message msg = mEventHandler.obtainMessage(CHECK_TIME);
            mEventHandler.sendMessage(msg);

            mEventHandler.removeMessages(CHECK_BATTERY);
            Message batterymsg = mEventHandler.obtainMessage(CHECK_BATTERY);
            mEventHandler.sendMessage(batterymsg);

            mEventHandler.removeMessages(CHECK_PROGRESS);
            Message progressmsg = mEventHandler.obtainMessage(CHECK_PROGRESS);
            mEventHandler.sendMessage(progressmsg);
        }
        switch (mStreamType) {
//            case AlbumDetailActivity.StreamType.SUPER:
//                mBitStreamView.setText(getResources().getString(R.string.stream_super));
//                break;
//            case AlbumDetailActivity.StreamType.NORMAL:
//                mBitStreamView.setText(getResources().getString(R.string.stream_normal));
//                break;
//            case AlbumDetailActivity.StreamType.HIGH:
//                mBitStreamView.setText(getResources().getString(R.string.stream_high));
//                break;
            default:
                break;
        }
    }

    private void hideTopAndBottomLayout() {
        if (mIsDragging == true) {
            return;
        }
        mIsPanelShowing = false;
        mTopLayout.setVisibility(View.GONE);
        mBottomLayout.setVisibility(View.GONE);
    }

    private void handlePlayPause() {
        //TODO
        if (mVideoView.isPlaying()) {//視訊正在播放
            mVideoView.pause();
            updatePlayPauseStatus(false);
        } else {
            mVideoView.start();
            updatePlayPauseStatus(true);
        }
    }

    private void updatePlayPauseStatus(boolean isPlaying) {
        mBigPauseButton.setVisibility(isPlaying ? View.GONE : View.VISIBLE);
        mPlayOrPauseButton.invalidate();
        mPlayOrPauseButton.setChecked(isPlaying);
        mPlayOrPauseButton.refreshDrawableState();
    }


    @Override
    protected void initData() {
//        Log.d(TAG, ">> initData mVideo=" + mVideo);
//        if (mVideo != null) {
//            Log.d(TAG, ">> initData mVideoName" + mVideo.getVideoName());
//            mVideoNameView.setText(mVideo.getVideoName());
//        }
        if (mLiveTitle != null) {
            mVideoNameView.setText(mLiveTitle);
        }
    }

    private void setCurrentBattery() {
        Log.d(TAG, ">> setCurrentBattery level " + mBatteryLevel);
        if (0 < mBatteryLevel && mBatteryLevel <= 10) {
            mBatteryView.setBackgroundResource(R.drawable.ic_battery_10);
        } else if (10 < mBatteryLevel && mBatteryLevel <= 20) {
            mBatteryView.setBackgroundResource(R.drawable.ic_battery_20);
        } else if (20 < mBatteryLevel && mBatteryLevel <= 50) {
            mBatteryView.setBackgroundResource(R.drawable.ic_battery_50);
        } else if (50 < mBatteryLevel && mBatteryLevel <= 80) {
            mBatteryView.setBackgroundResource(R.drawable.ic_battery_80);
        } else if (80 < mBatteryLevel && mBatteryLevel <= 100) {
            mBatteryView.setBackgroundResource(R.drawable.ic_battery_100);
        }
    }

    @Override
    protected void onStop() {
        super.onStop();
        if (mVideoView != null) {
            mVideoView.stopPlayback();
        }
    }

    private SeekBar.OnSeekBarChangeListener mSeekBarChangeListener = new SeekBar.OnSeekBarChangeListener() {
        // seekbar進度發生變化時回撥
        @Override
        public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
            if (!fromUser) {
                return;
            }
            long duration = mVideoView.getDuration();//視訊時長
            long nowPosition = (duration * progress) / 1000L;
            mVideoCurrentTime.setText(stringForTime((int) nowPosition));
        }

        // seekbar開始拖動時回撥
        @Override
        public void onStartTrackingTouch(SeekBar seekBar) {
            mIsDragging = true;
        }

        // seekbar拖動完成後回撥
        @Override
        public void onStopTrackingTouch(SeekBar seekBar) {
            mIsDragging = false;
            int progress = seekBar.getProgress();//最後拖動停止的進度
            long duration = mVideoView.getDuration();//視訊時長
            long newPosition = (duration * progress) / 1000L;//當前的進度
            mVideoView.seekTo((int) newPosition);
            mEventHandler.postDelayed(new Runnable() {
                @Override
                public void run() {
                    hideTopAndBottomLayout();
                }
            }, AFTER_DRAGGLE_HIDE_TIME);
        }
    };

    private void updateProgress() {
        int currentPosition = mVideoView.getCurrentPosition();//當前的視訊位置
        int duration = mVideoView.getDuration();//視訊時長
        if (mSeekBar != null) {
            if (duration > 0) {
                //轉成long型,避免溢位
                long pos = currentPosition * 1000L / duration;
                mSeekBar.setProgress((int) pos);
            }
            int perent = mVideoView.getBufferPercentage();//已經緩衝的進度
            mSeekBar.setSecondaryProgress(perent);//設定緩衝進度
            mVideoCurrentTime.setText(stringForTime(currentPosition));
            mVideoTotalTime.setText(stringForTime(duration));
        }
    }


    private String stringForTime(int timeMs) {
        int totalSeconds = timeMs / 1000;
        int seconds = totalSeconds % 60; //換成秒
        int minutes = (totalSeconds / 60) % 60;
        int hours = (totalSeconds / 3600);
        mFormatterBuilder.setLength(0);
        if (hours > 0) {
            return mFormatter.format("%d:%02d:%02d", hours, minutes, seconds).toString();
        } else {
            return mFormatter.format("%02d:%02d", minutes, seconds).toString();
        }
    }

    //從直播模組跳轉過來
    public static void launch(Activity activity, String url, String title) {
        Intent intent = new Intent(activity, PlayActivity.class);
        intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
        intent.putExtra("url", url);
        intent.putExtra("title", title);
        activity.startActivity(intent);
    }

}

在該檔案的邏輯程式碼中onScrollStart()方法主要實現的是對一些手勢在裝置螢幕上滑動進行進行監聽,比如在裝置橫屏的情況左下上下滑調節螢幕的亮度,右上下滑調節音量的大小;setComposeDrawableAndText()方法主要實現的是用於組合圖片及文字;updateVerticalText()方法主要實現的是用於更新垂直方向上滑動時的百分比;onScrollHorizontal()方法主要實現的是更新播放視訊的進度。

測試與執行截圖

這裡寫圖片描述
這裡寫圖片描述
這裡寫圖片描述

APK下載

apk Demo下載體驗地址
這裡寫圖片描述