GSYVideoPlayer播放器框架使用、播放元件原始碼探究(一)
在研究GSYVideoPlayer框架原始碼之前,先來探究一下如果如何使用框架提供的、基礎的視訊播器放元件類GSYVideoPlayer.java,通過層層封裝繼承,可以看出它也是繼承自FrameLayout這個幀佈局管理類,B站的IjkVideoPlayer視訊播放器元件的定義也是繼承自它。
開啟GSYVideoPlayer-master(官網例項demo)通過檢視標準的“簡單直接播放”按鈕對應的標準播放器元件StandardGSYVideoPlayer.java類,可看到下圖的繼承關係圖。
下面,為了便於分析播放器元件!為了便於分析播放器元件!為了便於分析播放器元件!重要的事情說三遍。我們的任務就是按照自定義元件的步驟流程,通過繼承框架提供的視訊播器放元件父類(GSYVideoPlayer.java),來抄一份StandardGSYVideoPlayer.java原始碼樣例,我們對這個原始碼樣例StandardGSYVideoPlayer.java進行分型,為了和這個播放器元件區分,我們建立一個空類叫MyStandardGSYVideoPlayer.java,通過Alt+Enter組合鍵可以檢視繼承GSYVideoPlayer父類所必須實現的抽象方法,也是在下面這張圖中所示的:
MyStandardGSYVideoPlayer類如下:
由於這個類是一個自定義的播放器元件類,繼承了 GSYVideoPlayer.java類,所以需要重寫、實現父類GSYVideoPlayer中定義的一些抽象方法和介面方法,以及定義元件所必須提供的3個構造方法:
/* @author: LQS
* @date: 2020/12/15
* @description:
*/
class MyStandardGSYVideoPlayer extends GSYVideoPlayer {
/**
* 自定義元件的3個構造方法
* @param context
*/
public MyStandardGSYVideoPlayer(Context context) {
super(context);
}
public MyStandardGSYVideoPlayer(Context context, AttributeSet attrs) {
super(context, attrs);
}
public MyStandardGSYVideoPlayer(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
}
可以看出主要的方法都是來自GSYVideoView和GSYVideoControlView,這兩個類。
下面就是從原始碼樣例類StandardGSYVideoPlayer.java類中複製程式碼到我們MyStandardGSYVideoPlayer.java類中去分析了。
首先第一大部分就是關於視訊播放元件關鍵的變數:
//亮度dialog
protected Dialog mBrightnessDialog;
//音量dialog
protected Dialog mVolumeDialog;
//觸控進度dialog
protected Dialog mProgressDialog;
//觸控進度條的progress
protected ProgressBar mDialogProgressBar;
//音量進度條的progress
protected ProgressBar mDialogVolumeProgressBar;
//亮度文字
protected TextView mBrightnessDialogTv;
//觸控移動顯示文字
protected TextView mDialogSeekTime;
//觸控移動顯示全部時間
protected TextView mDialogTotalTime;
//觸控移動方向icon
protected ImageView mDialogIcon;
protected Drawable mBottomProgressDrawable;
protected Drawable mBottomShowProgressDrawable;
protected Drawable mBottomShowProgressThumbDrawable;
protected Drawable mVolumeProgressDrawable;
protected Drawable mDialogProgressBarDrawable;
protected int mDialogProgressHighLightColor = -11;
protected int mDialogProgressNormalColor = -11;
關鍵的都註釋了。
第二大部分就是關於視訊播放元件佈局初初始化方法:
/**
* 繼承後重寫可替換為你需要的佈局
* @return
*/
@Override
public int getLayoutId() {
return R.layout.video_layout_standard;
}
/**
* 觸控進度dialog對話方塊的layoutId
* 螢幕中心區域:觸控快進、快退進度條
* 繼承後重寫可返回自定義
* 有自定義的實現邏輯可過載showProgressDialog方法
*/
protected int getProgressDialogLayoutId() {
return com.shuyu.gsyvideoplayer.R.layout.video_progress_dialog;
}
/**
* 觸控進度dialog對話方塊的進度條ProgressBar的id
* 螢幕中心區域:觸控快進、快退進度條
* 繼承後重寫可返回自定義,如果沒有可返回空
* 有自定義的實現邏輯可過載showProgressDialog方法
*/
protected int getProgressDialogProgressId() {
return com.shuyu.gsyvideoplayer.R.id.duration_progressbar;
}
/**
* 觸控進度dialog對話方塊的當前時間文字
* 螢幕中心區域:觸控快進、快退進度條
* 繼承後重寫可返回自定義,如果沒有可返回空
* 有自定義的實現邏輯可過載showProgressDialog方法
*/
protected int getProgressDialogCurrentDurationTextId() {
return com.shuyu.gsyvideoplayer.R.id.tv_current;
}
/**
* 觸控進度dialog對話方塊的視訊總時長時間文字
* 螢幕中心區域:觸控快進、快退進度條
* 繼承後重寫可返回自定義,如果沒有可返回空
* 有自定義的實現邏輯可過載showProgressDialog方法
*/
protected int getProgressDialogAllDurationTextId() {
return com.shuyu.gsyvideoplayer.R.id.tv_duration;
}
/**
* 觸控進度dialog對話方塊的快進、快退圖片id
* 螢幕中心區域:觸控快進、快退進度條
* 繼承後重寫可返回自定義,如果沒有可返回空
* 有自定義的實現邏輯可過載showProgressDialog方法
*/
protected int getProgressDialogImageId() {
return com.shuyu.gsyvideoplayer.R.id.duration_image_tip;
}
/**
* 音量dialog的layoutId
* 螢幕左側上下滑動控制音量的對話方塊佈局
* 繼承後重寫可返回自定義
* 有自定義的實現邏輯可過載showVolumeDialog方法
*/
protected int getVolumeLayoutId() {
return com.shuyu.gsyvideoplayer.R.layout.video_volume_dialog;
}
/**
* 音量dialog對話方塊的ProgressBar百分比進度條 id
* 螢幕左側上下滑動控制音量的對話方塊佈局
* 繼承後重寫可返回自定義,如果沒有可返回空
* 有自定義的實現邏輯可過載showVolumeDialog方法
*/
protected int getVolumeProgressId() {
return com.shuyu.gsyvideoplayer.R.id.volume_progressbar;
}
/**
* 亮度dialog對話方塊的layoutId
* 螢幕右側上下滑動控制亮度的對話方塊佈局
* 繼承後重寫可返回自定義
* 有自定義的實現邏輯可過載showBrightnessDialog方法
*/
protected int getBrightnessLayoutId() {
return com.shuyu.gsyvideoplayer.R.layout.video_brightness;
}
/**
* 亮度dialog的百分比text id
* 螢幕右側上下滑動控制亮度的對話方塊佈局
* 繼承後重寫可返回自定義,如果沒有可返回空
* 有自定義的實現邏輯可過載showBrightnessDialog方法
*/
protected int getBrightnessTextId() {
return com.shuyu.gsyvideoplayer.R.id.app_video_brightness;
}
第三大部分就是關於視訊播放元件根據播放狀態顯示/隱藏UI按鈕方法:
這裡需要給出示意圖說明一下播放元件的螢幕組成結構:①頂部返回按鈕和視訊標題;②中部的播放/暫停按鈕、觸控滑動時的快進/快退圖示和進度條;③底部的當前播放點時間文字/視訊總時長和進度條;④以及最下面的視訊預載入進度條;
這裡面會有一個邏輯當點選螢幕時所有的UI按鈕、圖示、文字資訊和都會顯示,2.5秒之內再次點選螢幕,所有的UI按鈕、圖示、文字資訊會立即隱藏,若2.5秒之後不點選螢幕也會自動隱藏(這個後面會說),程式碼如下:
/********************************根據播放狀態控制UI顯示的方法*********************************************/
@Override
protected void changeUiToPlayingShow() {
Debuger.printfLog("changeUiToPlayingShow");
//頂部返回按鈕和標題根佈局
setViewShowState(mTopContainer, VISIBLE);
//底部播放時長文字和進度條、全屏按鈕的根佈局
setViewShowState(mBottomContainer, VISIBLE);
//螢幕中間區域開始、暫停按鈕
setViewShowState(mStartButton, VISIBLE);
//螢幕中心區域,和播放/暫停按鈕同一位置處的Loading載入中圖示,它是一個自定義的元件moe.codeest.enviews.ENDownloadView
setViewShowState(mLoadingProgressBar, INVISIBLE);
//視訊封面的父佈局,這個即使一個空的相對佈局
setViewShowState(mThumbImageViewLayout, INVISIBLE);
//底部進度調,應該是快取進度條
setViewShowState(mBottomProgressBar, INVISIBLE);
setViewShowState(mLockScreen, (mIfCurrentIsFullscreen && mNeedLockFull) ? VISIBLE : GONE);
if (mLoadingProgressBar instanceof ENDownloadView) {
((ENDownloadView) mLoadingProgressBar).reset();
}
updateStartImage();
}
/*下面的所有方法註釋不再給出,因為和上面播放時控制的變數都是一樣的,只不過顯示/隱藏值不同罷了
*/
/**
* 點選觸控顯示和隱藏邏輯
* 在控制播放按鈕自動隱藏前,通過點選螢幕使得控制播放按鈕隱藏、顯示的邏輯
*/
@Override
protected void onClickUiToggle() {
if (mIfCurrentIsFullscreen && mLockCurScreen && mNeedLockFull) {
setViewShowState(mLockScreen, VISIBLE);
return;
}
if (mCurrentState == CURRENT_STATE_PREPAREING) {
if (mBottomContainer != null) {
if (mBottomContainer.getVisibility() == View.VISIBLE) {
changeUiToPrepareingClear();
} else {
changeUiToPreparingShow();
}
}
} else if (mCurrentState == CURRENT_STATE_PLAYING) {
if (mBottomContainer != null) {
if (mBottomContainer.getVisibility() == View.VISIBLE) {
changeUiToPlayingClear();
} else {
changeUiToPlayingShow();
}
}
} else if (mCurrentState == CURRENT_STATE_PAUSE) {
if (mBottomContainer != null) {
if (mBottomContainer.getVisibility() == View.VISIBLE) {
changeUiToPauseClear();
} else {
changeUiToPauseShow();
}
}
} else if (mCurrentState == CURRENT_STATE_AUTO_COMPLETE) {
if (mBottomContainer != null) {
if (mBottomContainer.getVisibility() == View.VISIBLE) {
changeUiToCompleteClear();
} else {
changeUiToCompleteShow();
}
}
} else if (mCurrentState == CURRENT_STATE_PLAYING_BUFFERING_START) {
if (mBottomContainer != null) {
if (mBottomContainer.getVisibility() == View.VISIBLE) {
changeUiToPlayingBufferingClear();
} else {
changeUiToPlayingBufferingShow();
}
}
}
}
@Override
protected void changeUiToNormal() {
Debuger.printfLog("changeUiToNormal");
//設定元件顯示、隱藏:view.setVisibility(visibility);
setViewShowState(mTopContainer, VISIBLE);
setViewShowState(mBottomContainer, INVISIBLE);
setViewShowState(mStartButton, VISIBLE);
setViewShowState(mLoadingProgressBar, INVISIBLE);
setViewShowState(mThumbImageViewLayout, VISIBLE);
setViewShowState(mBottomProgressBar, INVISIBLE);
setViewShowState(mLockScreen, (mIfCurrentIsFullscreen && mNeedLockFull) ? VISIBLE : GONE);
updateStartImage();
if (mLoadingProgressBar instanceof ENDownloadView) {
((ENDownloadView) mLoadingProgressBar).reset();
}
}
@Override
protected void changeUiToPreparingShow() {
Debuger.printfLog("changeUiToPreparingShow");
setViewShowState(mTopContainer, VISIBLE);
setViewShowState(mBottomContainer, VISIBLE);
setViewShowState(mStartButton, INVISIBLE);
setViewShowState(mLoadingProgressBar, VISIBLE);
setViewShowState(mThumbImageViewLayout, INVISIBLE);
setViewShowState(mBottomProgressBar, INVISIBLE);
setViewShowState(mLockScreen, GONE);
if (mLoadingProgressBar instanceof ENDownloadView) {
ENDownloadView enDownloadView = (ENDownloadView) mLoadingProgressBar;
if (enDownloadView.getCurrentState() == ENDownloadView.STATE_PRE) {
((ENDownloadView) mLoadingProgressBar).start();
}
}
}
@Override
protected void changeUiToPauseShow() {
Debuger.printfLog("changeUiToPauseShow");
setViewShowState(mTopContainer, VISIBLE);
setViewShowState(mBottomContainer, VISIBLE);
setViewShowState(mStartButton, VISIBLE);
setViewShowState(mLoadingProgressBar, INVISIBLE);
setViewShowState(mThumbImageViewLayout, INVISIBLE);
setViewShowState(mBottomProgressBar, INVISIBLE);
setViewShowState(mLockScreen, (mIfCurrentIsFullscreen && mNeedLockFull) ? VISIBLE : GONE);
if (mLoadingProgressBar instanceof ENDownloadView) {
((ENDownloadView) mLoadingProgressBar).reset();
}
updateStartImage();
updatePauseCover();
}
@Override
protected void changeUiToCompleteShow() {
Debuger.printfLog("changeUiToCompleteShow");
setViewShowState(mTopContainer, VISIBLE);
setViewShowState(mBottomContainer, VISIBLE);
setViewShowState(mStartButton, VISIBLE);
setViewShowState(mLoadingProgressBar, INVISIBLE);
setViewShowState(mThumbImageViewLayout, VISIBLE);
setViewShowState(mBottomProgressBar, INVISIBLE);
setViewShowState(mLockScreen, (mIfCurrentIsFullscreen && mNeedLockFull) ? VISIBLE : GONE);
if (mLoadingProgressBar instanceof ENDownloadView) {
((ENDownloadView) mLoadingProgressBar).reset();
}
updateStartImage();
}
@Override
protected void changeUiToError() {
Debuger.printfLog("changeUiToError");
setViewShowState(mTopContainer, INVISIBLE);
setViewShowState(mBottomContainer, INVISIBLE);
setViewShowState(mStartButton, VISIBLE);
setViewShowState(mLoadingProgressBar, INVISIBLE);
setViewShowState(mThumbImageViewLayout, INVISIBLE);
setViewShowState(mBottomProgressBar, INVISIBLE);
setViewShowState(mLockScreen, (mIfCurrentIsFullscreen && mNeedLockFull) ? VISIBLE : GONE);
if (mLoadingProgressBar instanceof ENDownloadView) {
((ENDownloadView) mLoadingProgressBar).reset();
}
updateStartImage();
}
@Override
protected void changeUiToPlayingBufferingShow() {
Debuger.printfLog("changeUiToPlayingBufferingShow");
setViewShowState(mTopContainer, VISIBLE);
setViewShowState(mBottomContainer, VISIBLE);
setViewShowState(mStartButton, INVISIBLE);
setViewShowState(mLoadingProgressBar, VISIBLE);
setViewShowState(mThumbImageViewLayout, INVISIBLE);
setViewShowState(mBottomProgressBar, INVISIBLE);
setViewShowState(mLockScreen, GONE);
if (mLoadingProgressBar instanceof ENDownloadView) {
ENDownloadView enDownloadView = (ENDownloadView) mLoadingProgressBar;
if (enDownloadView.getCurrentState() == ENDownloadView.STATE_PRE) {
((ENDownloadView) mLoadingProgressBar).start();
}
}
}
/********************************根據播放狀態控制UI隱藏的方法*********************************************/
/**
* 隱藏所有關於播放的資訊:頂部返回按鈕和標題欄,中部的開始/暫停/載入中按鈕圖示,底部的播放時長文字、進度條和全屏按鈕
*/
@Override
protected void hideAllWidget() {
setViewShowState(mBottomContainer, INVISIBLE);
setViewShowState(mTopContainer, INVISIBLE);
setViewShowState(mBottomProgressBar, VISIBLE);
setViewShowState(mStartButton, INVISIBLE);
}
protected void changeUiToPrepareingClear() {
Debuger.printfLog("changeUiToPrepareingClear");
setViewShowState(mTopContainer, INVISIBLE);
setViewShowState(mBottomContainer, INVISIBLE);
setViewShowState(mStartButton, INVISIBLE);
setViewShowState(mLoadingProgressBar, INVISIBLE);
setViewShowState(mThumbImageViewLayout, INVISIBLE);
setViewShowState(mBottomProgressBar, INVISIBLE);
setViewShowState(mLockScreen, GONE);
if (mLoadingProgressBar instanceof ENDownloadView) {
((ENDownloadView) mLoadingProgressBar).reset();
}
}
protected void changeUiToPlayingClear() {
Debuger.printfLog("changeUiToPlayingClear");
changeUiToClear();
setViewShowState(mBottomProgressBar, VISIBLE);
}
protected void changeUiToPauseClear() {
Debuger.printfLog("changeUiToPauseClear");
changeUiToClear();
setViewShowState(mBottomProgressBar, VISIBLE);
updatePauseCover();
}
protected void changeUiToPlayingBufferingClear() {
Debuger.printfLog("changeUiToPlayingBufferingClear");
setViewShowState(mTopContainer, INVISIBLE);
setViewShowState(mBottomContainer, INVISIBLE);
setViewShowState(mStartButton, INVISIBLE);
setViewShowState(mLoadingProgressBar, VISIBLE);
setViewShowState(mThumbImageViewLayout, INVISIBLE);
setViewShowState(mBottomProgressBar, VISIBLE);
setViewShowState(mLockScreen, GONE);
if (mLoadingProgressBar instanceof ENDownloadView) {
ENDownloadView enDownloadView = (ENDownloadView) mLoadingProgressBar;
if (enDownloadView.getCurrentState() == ENDownloadView.STATE_PRE) {
((ENDownloadView) mLoadingProgressBar).start();
}
}
updateStartImage();
}
protected void changeUiToClear() {
Debuger.printfLog("changeUiToClear");
setViewShowState(mTopContainer, INVISIBLE);
setViewShowState(mBottomContainer, INVISIBLE);
setViewShowState(mStartButton, INVISIBLE);
setViewShowState(mLoadingProgressBar, INVISIBLE);
setViewShowState(mThumbImageViewLayout, INVISIBLE);
setViewShowState(mBottomProgressBar, INVISIBLE);
setViewShowState(mLockScreen, GONE);
if (mLoadingProgressBar instanceof ENDownloadView) {
((ENDownloadView) mLoadingProgressBar).reset();
}
}
protected void changeUiToCompleteClear() {
Debuger.printfLog("changeUiToCompleteClear");
setViewShowState(mTopContainer, INVISIBLE);
setViewShowState(mBottomContainer, INVISIBLE);
setViewShowState(mStartButton, VISIBLE);
setViewShowState(mLoadingProgressBar, INVISIBLE);
setViewShowState(mThumbImageViewLayout, VISIBLE);
setViewShowState(mBottomProgressBar, VISIBLE);
setViewShowState(mLockScreen, (mIfCurrentIsFullscreen && mNeedLockFull) ? VISIBLE : GONE);
if (mLoadingProgressBar instanceof ENDownloadView) {
((ENDownloadView) mLoadingProgressBar).reset();
}
updateStartImage();
}
/**
* 根據播放狀態設定對應的播放、暫停、出錯狀態
*/
protected void updateStartImage() {
if (mStartButton instanceof ENPlayView) {
ENPlayView enPlayView = (ENPlayView) mStartButton;
enPlayView.setDuration(500);
if (mCurrentState == CURRENT_STATE_PLAYING) {
enPlayView.play();
} else if (mCurrentState == CURRENT_STATE_ERROR) {
enPlayView.pause();
} else {
enPlayView.pause();
}
} else if (mStartButton instanceof ImageView) {
ImageView imageView = (ImageView) mStartButton;
if (mCurrentState == CURRENT_STATE_PLAYING) {
imageView.setImageResource(com.shuyu.gsyvideoplayer.R.drawable.video_click_pause_selector);
} else if (mCurrentState == CURRENT_STATE_ERROR) {
imageView.setImageResource(com.shuyu.gsyvideoplayer.R.drawable.video_click_error_selector);
} else {
imageView.setImageResource(com.shuyu.gsyvideoplayer.R.drawable.video_click_play_selector);
}
}
}
第四大部分就是關於視訊播放元件如果有自定義Drawable時對其初始化設定的:
/**
* 正常小屏時,如果有自定義Drawable,則初始化設定這些Drawable
*/
@Override
protected void init(Context context) {
super.init(context);
//增加自定義ui
if (mBottomProgressDrawable != null) {
mBottomProgressBar.setProgressDrawable(mBottomProgressDrawable);
}
if (mBottomShowProgressDrawable != null) {
mProgressBar.setProgressDrawable(mBottomProgressDrawable);
}
if (mBottomShowProgressThumbDrawable != null) {
mProgressBar.setThumb(mBottomShowProgressThumbDrawable);
}
}
/**
* 全屏的UI邏輯
*/
private void initFullUI(MyStandardGSYVideoPlayer myStandardGSYVideoPlayer) {
if (mBottomProgressDrawable != null) {
myStandardGSYVideoPlayer.setBottomProgressBarDrawable(mBottomProgressDrawable);
}
if (mBottomShowProgressDrawable != null && mBottomShowProgressThumbDrawable != null) {
myStandardGSYVideoPlayer.setBottomShowProgressBarDrawable(mBottomShowProgressDrawable,
mBottomShowProgressThumbDrawable);
}
if (mVolumeProgressDrawable != null) {
myStandardGSYVideoPlayer.setDialogVolumeProgressBar(mVolumeProgressDrawable);
}
if (mDialogProgressBarDrawable != null) {
myStandardGSYVideoPlayer.setDialogProgressBar(mDialogProgressBarDrawable);
}
if (mDialogProgressHighLightColor >= 0 && mDialogProgressNormalColor >= 0) {
myStandardGSYVideoPlayer.setDialogProgressColor(mDialogProgressHighLightColor, mDialogProgressNormalColor);
}
}
/**
* 底部進度條-彈出的
*/
public void setBottomShowProgressBarDrawable(Drawable drawable, Drawable thumb) {
mBottomShowProgressDrawable = drawable;
mBottomShowProgressThumbDrawable = thumb;
if (mProgressBar != null) {
mProgressBar.setProgressDrawable(drawable);
mProgressBar.setThumb(thumb);
}
}
/**
* 底部進度條-非彈出
*/
public void setBottomProgressBarDrawable(Drawable drawable) {
mBottomProgressDrawable = drawable;
if (mBottomProgressBar != null) {
mBottomProgressBar.setProgressDrawable(drawable);
}
}
/**
* 聲音進度條
*/
public void setDialogVolumeProgressBar(Drawable drawable) {
mVolumeProgressDrawable = drawable;
}
/**
* 中間進度條
*/
public void setDialogProgressBar(Drawable drawable) {
mDialogProgressBarDrawable = drawable;
}
/**
* 中間進度條字型顏色
*/
public void setDialogProgressColor(int highLightColor, int normalColor) {
mDialogProgressHighLightColor = highLightColor;
mDialogProgressNormalColor = normalColor;
}
第五部分亮度Dialog、音量Dialog、快進快退Dialog顯示隱藏及數值變化邏輯的控制:
效果圖如下:
程式碼如下:
/************************************* 亮度Dialog、音量Dialog、快進快退Dialog顯示隱藏及數值變化邏輯的控制****************************************/
/**
* 顯示wifi確定框,如需要自定義繼承重寫即可
*/
@Override
protected void showWifiDialog() {
if (!NetworkUtils.isAvailable(mContext)) {
//Toast.makeText(mContext, getResources().getString(R.string.no_net), Toast.LENGTH_LONG).show();
startPlayLogic();
return;
}
AlertDialog.Builder builder = new AlertDialog.Builder(getActivityContext());
builder.setMessage(getResources().getString(R.string.tips_not_wifi));
builder.setPositiveButton(getResources().getString(R.string.tips_not_wifi_confirm), new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
dialog.dismiss();
startPlayLogic();
}
});
builder.setNegativeButton(getResources().getString(R.string.tips_not_wifi_cancel), new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
dialog.dismiss();
}
});
builder.create().show();
}
/**
* 觸控顯示滑動進度dialog,如需要自定義繼承重寫即可,記得重寫dismissProgressDialog
* 滑動快進、快退進度條、當前播放時間文字、總時長文字
* 在protected void touchSurfaceMove(float deltaX, float deltaY, float y)中呼叫
*/
@Override
@SuppressWarnings("ResourceType")
protected void showProgressDialog(float deltaX, String seekTime, int seekTimePosition, String totalTime, int totalTimeDuration) {
if (mProgressDialog == null) {
View localView = LayoutInflater.from(getActivityContext()).inflate(getProgressDialogLayoutId(), null);
//ProgressBar 滑動進度條
if (localView.findViewById(getProgressDialogProgressId()) instanceof ProgressBar) {
mDialogProgressBar = ((ProgressBar) localView.findViewById(getProgressDialogProgressId()));
if (mDialogProgressBarDrawable != null) {
mDialogProgressBar.setProgressDrawable(mDialogProgressBarDrawable);
}
}
//當前視訊播放時間文字
if (localView.findViewById(getProgressDialogCurrentDurationTextId()) instanceof TextView) {
mDialogSeekTime = ((TextView) localView.findViewById(getProgressDialogCurrentDurationTextId()));
}
//視訊總時長文字
if (localView.findViewById(getProgressDialogAllDurationTextId()) instanceof TextView) {
mDialogTotalTime = ((TextView) localView.findViewById(getProgressDialogAllDurationTextId()));
}
if (localView.findViewById(getProgressDialogImageId()) instanceof ImageView) {
mDialogIcon = ((ImageView) localView.findViewById(getProgressDialogImageId()));
}
//初始化一個帶樣式的對話方塊
mProgressDialog = new Dialog(getActivityContext(), R.style.video_style_dialog_progress);
mProgressDialog.setContentView(localView);
mProgressDialog.getWindow().addFlags(Window.FEATURE_ACTION_BAR);
mProgressDialog.getWindow().addFlags(32);
mProgressDialog.getWindow().addFlags(16);
mProgressDialog.getWindow().setLayout(getWidth(), getHeight());
if (mDialogProgressNormalColor != -11 && mDialogTotalTime != null) {
mDialogTotalTime.setTextColor(mDialogProgressNormalColor);
}
if (mDialogProgressHighLightColor != -11 && mDialogSeekTime != null) {
mDialogSeekTime.setTextColor(mDialogProgressHighLightColor);
}
WindowManager.LayoutParams localLayoutParams = mProgressDialog.getWindow().getAttributes();
localLayoutParams.gravity = Gravity.TOP;
localLayoutParams.width = getWidth();
localLayoutParams.height = getHeight();
int location[] = new int[2];
//獲取控制元件在當前螢幕中的位置
getLocationOnScreen(location);
localLayoutParams.x = location[0];
localLayoutParams.y = location[1];
mProgressDialog.getWindow().setAttributes(localLayoutParams);
}
if (!mProgressDialog.isShowing()) {
mProgressDialog.show();
}
if (mDialogSeekTime != null) {
mDialogSeekTime.setText(seekTime);
}
if (mDialogTotalTime != null) {
mDialogTotalTime.setText(" / " + totalTime);
}
if (totalTimeDuration > 0)
if (mDialogProgressBar != null) {
mDialogProgressBar.setProgress(seekTimePosition * 100 / totalTimeDuration);
}
if (deltaX > 0) {
if (mDialogIcon != null) {
mDialogIcon.setBackgroundResource(R.drawable.video_forward_icon);
}
} else {
if (mDialogIcon != null) {
mDialogIcon.setBackgroundResource(R.drawable.video_backward_icon);
}
}
}
@Override
protected void dismissProgressDialog() {
if (mProgressDialog != null) {
mProgressDialog.dismiss();
mProgressDialog = null;
}
}
/**
* 觸控音量dialog,如需要自定義繼承重寫即可,記得重寫dismissVolumeDialog
*/
@Override
protected void showVolumeDialog(float deltaY, int volumePercent) {
if (mVolumeDialog == null) {
View localView = LayoutInflater.from(getActivityContext()).inflate(getVolumeLayoutId(), null);
if (localView.findViewById(getVolumeProgressId()) instanceof ProgressBar) {
mDialogVolumeProgressBar = ((ProgressBar) localView.findViewById(getVolumeProgressId()));
if (mVolumeProgressDrawable != null && mDialogVolumeProgressBar != null) {
mDialogVolumeProgressBar.setProgressDrawable(mVolumeProgressDrawable);
}
}
mVolumeDialog = new Dialog(getActivityContext(), R.style.video_style_dialog_progress);
mVolumeDialog.setContentView(localView);
mVolumeDialog.getWindow().addFlags(WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE);
mVolumeDialog.getWindow().addFlags(WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL);
mVolumeDialog.getWindow().addFlags(WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE);
mVolumeDialog.getWindow().setLayout(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
WindowManager.LayoutParams localLayoutParams = mVolumeDialog.getWindow().getAttributes();
localLayoutParams.gravity = Gravity.TOP | Gravity.START;
localLayoutParams.width = getWidth();
localLayoutParams.height = getHeight();
int location[] = new int[2];
getLocationOnScreen(location);
localLayoutParams.x = location[0];
localLayoutParams.y = location[1];
mVolumeDialog.getWindow().setAttributes(localLayoutParams);
}
if (!mVolumeDialog.isShowing()) {
mVolumeDialog.show();
}
if (mDialogVolumeProgressBar != null) {
mDialogVolumeProgressBar.setProgress(volumePercent);
}
}
@Override
protected void dismissVolumeDialog() {
if (mVolumeDialog != null) {
mVolumeDialog.dismiss();
mVolumeDialog = null;
}
}
/**
* 觸控亮度dialog,如需要自定義繼承重寫即可,記得重寫dismissBrightnessDialog
*/
@Override
protected void showBrightnessDialog(float percent) {
if (mBrightnessDialog == null) {
View localView = LayoutInflater.from(getActivityContext()).inflate(getBrightnessLayoutId(), null);
if (localView.findViewById(getBrightnessTextId()) instanceof TextView) {
mBrightnessDialogTv = (TextView) localView.findViewById(getBrightnessTextId());
}
mBrightnessDialog = new Dialog(getActivityContext(), R.style.video_style_dialog_progress);
mBrightnessDialog.setContentView(localView);
mBrightnessDialog.getWindow().addFlags(WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE);
mBrightnessDialog.getWindow().addFlags(WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL);
mBrightnessDialog.getWindow().addFlags(WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE);
mBrightnessDialog.getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_HIDE_NAVIGATION);
mBrightnessDialog.getWindow().setLayout(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
WindowManager.LayoutParams localLayoutParams = mBrightnessDialog.getWindow().getAttributes();
localLayoutParams.gravity = Gravity.TOP | Gravity.END;
localLayoutParams.width = getWidth();
localLayoutParams.height = getHeight();
int location[] = new int[2];
getLocationOnScreen(location);
localLayoutParams.x = location[0];
localLayoutParams.y = location[1];
mBrightnessDialog.getWindow().setAttributes(localLayoutParams);
}
if (!mBrightnessDialog.isShowing()) {
mBrightnessDialog.show();
}
if (mBrightnessDialogTv != null)
mBrightnessDialogTv.setText((int) (percent * 100) + "%");
}
@Override
protected void dismissBrightnessDialog() {
if (mBrightnessDialog != null) {
mBrightnessDialog.dismiss();
mBrightnessDialog = null;
}
}
@Override
protected void onDetachedFromWindow() {
super.onDetachedFromWindow();
dismissVolumeDialog();
dismissBrightnessDialog();
}
第六部分正常小屏--全屏--小屏進行切換時,需要將當前正在播放的資訊、樣式複製給將要使用的螢幕模式
/**
* 全屏-小屏-全屏相互切換時,將當前播放時間點、視訊時長、進度條百分比進度拷貝給播放器物件
* @param from
* @param to
*/
@Override
protected void cloneParams(GSYBaseVideoPlayer from, GSYBaseVideoPlayer to) {
super.cloneParams(from, to);
MyStandardGSYVideoPlayer sf = (MyStandardGSYVideoPlayer) from;
MyStandardGSYVideoPlayer st = (MyStandardGSYVideoPlayer) to;
if (st.mProgressBar != null && sf.mProgressBar != null) {
st.mProgressBar.setProgress(sf.mProgressBar.getProgress());
st.mProgressBar.setSecondaryProgress(sf.mProgressBar.getSecondaryProgress());
}
if (st.mTotalTimeTextView != null && sf.mTotalTimeTextView != null) {
st.mTotalTimeTextView.setText(sf.mTotalTimeTextView.getText());
}
if (st.mCurrentTimeTextView != null && sf.mCurrentTimeTextView != null) {
st.mCurrentTimeTextView.setText(sf.mCurrentTimeTextView.getText());
}
}
/**
* 將自定義的效果也設定到全屏
*
* @param context
* @param actionBar 是否有actionBar,有的話需要隱藏
* @param statusBar 是否有狀態bar,有的話需要隱藏
* @return
*/
@Override
public GSYBaseVideoPlayer startWindowFullscreen(Context context, boolean actionBar, boolean statusBar) {
GSYBaseVideoPlayer gsyBaseVideoPlayer = super.startWindowFullscreen(context, actionBar, statusBar);
if (gsyBaseVideoPlayer != null) {
MyStandardGSYVideoPlayer gsyVideoPlayer = (MyStandardGSYVideoPlayer) gsyBaseVideoPlayer;
gsyVideoPlayer.setLockClickListener(mLockClickListener);
gsyVideoPlayer.setNeedLockFull(isNeedLockFull());
initFullUI(gsyVideoPlayer);
//比如你自定義了返回案件,但是因為返回按鍵底層已經設定了返回事件,所以你需要在這裡重新增加的邏輯
}
return gsyBaseVideoPlayer;
}
第七部分關於截圖功能:
/************************************* 關於截圖的 ****************************************/
/**
* 獲取截圖
*/
public void taskShotPic(GSYVideoShotListener gsyVideoShotListener) {
this.taskShotPic(gsyVideoShotListener, false);
}
/**
* 獲取截圖
*
* @param high 是否需要高清的
*/
public void taskShotPic(GSYVideoShotListener gsyVideoShotListener, boolean high) {
if (getCurrentPlayer().getRenderProxy() != null) {
getCurrentPlayer().getRenderProxy().taskShotPic(gsyVideoShotListener, high);
}
}
/**
* 儲存截圖
*/
public void saveFrame(final File file, GSYVideoShotSaveListener gsyVideoShotSaveListener) {
saveFrame(file, false, gsyVideoShotSaveListener);
}
/**
* 儲存截圖
*
* @param high 是否需要高清的
*/
public void saveFrame(final File file, final boolean high, final GSYVideoShotSaveListener gsyVideoShotSaveListener) {
if (getCurrentPlayer().getRenderProxy() != null) {
getCurrentPlayer().getRenderProxy().saveFrame(file, high, gsyVideoShotSaveListener);
}
}
第八部分貼出開始播放的邏輯程式碼,這個比較少:
/**
* 開始播放
*/
@Override
public void startPlayLogic() {
if (mVideoAllCallBack != null) {
Debuger.printfLog("onClickStartThumb");
mVideoAllCallBack.onClickStartThumb(mOriginUrl, mTitle, MyStandardGSYVideoPlayer.this);
}
/**
* 父類GSYVideoControlView方法:增對列表優化,在播放前的時候才進行setup
* 優化內容:
* 設定播放URL
* @param url 播放url
* @param cacheWithPlay 是否邊播邊快取
* @param cachePath 快取路徑,如果是M3U8或者HLS,請設定為false
* @param mapHeadData 頭部資訊
* @param title title
*/
prepareVideo();
/**
* 開始對視訊播放元件上層的播放、暫停、播放進度條、播放時間/總時長等按鈕的顯示/隱藏進行控制
*
* protected int mDismissControlTime = 2500; 觸控顯示後隱藏的時間:觸控顯示控制按鈕,2.5秒後隱藏所有控制按鈕
* Runnable dismissControlTask = new Runnable(); 通過postDelayed()延時2.5秒後向訊息佇列傳送一個可執行的子執行緒訊息,去隱藏控制按鈕更新介面
*/
startDismissControlViewTimer();
}
/**
* 重新開啟進度查詢以及控制view消失的定時任務
* 用於解決GSYVideoHelper中通過removeview方式做全屏切換導致的定時任務停止的問題
* GSYVideoControlView onDetachedFromWindow()
*/
public void restartTimerTask() {
startProgressTimer();
startDismissControlViewTimer();
}
好了到這裡原始碼樣例類StandardGSYVideoPlayer.java類的程式碼基本都分析完了 ,都在註釋裡,可以細細的品!
下面來說兩部分,自動隱藏播放介面上層的UI按鈕、圖示、文字資訊和通過手勢滑動控制音量Dialog、進度條Dialog、螢幕亮度Dialog的邏輯,鑑於篇幅過長,將總結到第二篇。
GSYVideoPlayer播放器框架使用、播放元件原始碼探究(二):https://blog.csdn.net/luqingshuai_eloong/article/details/111318995
- 用Python講述:地理“經緯度”資料的4種轉換方法!
- 17 張程式設計師桌布(使用頻率很高)
- [總結]高效能人士的七個習慣
- 為什麼你的程式總是出現 bug?
- GB28181協議錯誤碼返回碼整理
- 還在用 Windows 自帶的搜尋工具嗎?你 out 啦!
- 動畫: 一個瀏覽器是如何工作的?
- 8小時刪除,這波資源碉堡了 ! @所有人
- 新手上路第一篇C語言部落格
- ESP8266_NONOS_SDK--UART實驗
- 2021-01-05
- 《軟體架構》總覽
- 修改Exchange資訊儲存佔用記憶體大小
- uart232串列埠之二——fpg內部迴環模擬
- 3.18PMP試題每日一題
- 2020-12-28
- 一文詳細講解SQL語句中可以提高執行效率的方法
- 小程式開發遇到的坑,知道下總是好的!
- 如何通過 Spring 框架進行JDBC操作?
- GSYVideoPlayer播放器框架使用、播放元件原始碼探究(一)