JiaoZiVideoPlayer是一個android下的視頻的播放軟件,主要功能是放在了播放上面,並沒有視頻的錄製和切片加水印效果,如果是想實現這種效果,需要參考其它開源框架。這款框架使用的人還是非常多的,屬於多媒體模塊,這裏將分析它的源碼,以便以後用上的時候好進行修改源碼。
一、視頻播放基礎控件介紹
TextureView控件,是一個支架性的控件,主要是用來實現承載視頻的顯示。
源碼分析
JZTextureView類
這是一個繼承的TextureView的類,在這個類裏主要重寫了setRotation,和onMeasure,這兩個方法,並且增加了setVideoSize這個方法。
這個重寫類增加和重寫的方法,主要是爲了實現視頻的旋轉以及裁剪和寬和高的設定。
實現旋轉
@Override
public void setRotation(float rotation) {
if (rotation != getRotation()) {
super.setRotation(rotation);
requestLayout();
}
}
Jzvd 類 繼承了FrameLayout類
在onTouch()方法中,實現了划動左邊亮度的增減,划動右邊聲音的增減,以及,左右滑動時進度的增減
一、進入全屏,在這裏是重寫了onclick方法,在點擊右下角的全屏按鈕時,執行了以下的代碼,
if (i == R.id.fullscreen) {
Log.i(TAG, "onClick fullscreen [" + this.hashCode() + "] ");
if (state == STATE_AUTO_COMPLETE) return;
if (screen == SCREEN_FULLSCREEN) {
//quit fullscreen
backPress();
} else {
Log.d(TAG, "toFullscreenActivity [" + this.hashCode() + "] ");
gotoScreenFullscreen();
}
}
然後執行gotoScreenFullscreen()方法,進入視頻全屏播放模式,在這裏可以看到,通過getParent()它會得到父控件,然後移動控件的所有組件,把自己的寬和高設爲match_layout,再次返回時其它控件會沒有了,因此儘量把它單獨放到一個父類中,然後再去隱藏狀態欄和虛擬鍵盤,並且修改屏幕爲橫向顯示。
public void gotoScreenFullscreen() {
ViewGroup vg = (ViewGroup) getParent();
vg.removeView(this);
cloneAJzvd(vg);
CONTAINER_LIST.add(vg);
vg = (ViewGroup) (JZUtils.scanForActivity(getContext())).getWindow().getDecorView();//和他也沒有關係
vg.addView(this, new FrameLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
setScreenFullscreen();
//隱藏狀態欄
JZUtils.hideStatusBar(getContext());
//修改屏幕爲橫向
JZUtils.setRequestedOrientation(getContext(), FULLSCREEN_ORIENTATION);
隱藏虛擬鍵盤
JZUtils.hideSystemUI(getContext());//華爲手機和有虛擬鍵的手機全屏時可隱藏虛擬鍵 issue:1326
}
二、 左側:上下滑動修改亮度,右側:上下滑動修改聲音,左右滑動修改進度
在這裏是只是給出了MotionEvent.ACTION_MOVE的源碼,這塊內容的實現就是onTouch方法,看這個方法就可以了,這裏只是一個小bug的,就是切換橫屏後,沒有修改mScreenWidth這個值,造成修改亮度不是一半,
float deltaX = x - mDownX;
float deltaY = y - mDownY;
float absDeltaX = Math.abs(deltaX);
float absDeltaY = Math.abs(deltaY);
if (screen == SCREEN_FULLSCREEN) {
if (!mChangePosition && !mChangeVolume && !mChangeBrightness) {
if (absDeltaX > THRESHOLD || absDeltaY > THRESHOLD) {
cancelProgressTimer();
if (absDeltaX >= THRESHOLD) {
// 全屏模式下的CURRENT_STATE_ERROR狀態下,不響應進度拖動事件.
// 否則會因爲mediaplayer的狀態非法導致App Crash
if (state != STATE_ERROR) {
mChangePosition = true;
mGestureDownPosition = getCurrentPositionWhenPlaying();
}
} else {
//如果y軸滑動距離超過設置的處理範圍,那麼進行滑動事件處理
if (mDownX < mScreenWidth * 0.5f) {//左側改變亮度
mChangeBrightness = true;
WindowManager.LayoutParams lp = JZUtils.getWindow(getContext()).getAttributes();
if (lp.screenBrightness < 0) {
try {
mGestureDownBrightness = Settings.System.getInt(getContext().getContentResolver(), Settings.System.SCREEN_BRIGHTNESS);
Log.i(TAG, "current system brightness: " + mGestureDownBrightness);
} catch (Settings.SettingNotFoundException e) {
e.printStackTrace();
}
} else {
mGestureDownBrightness = lp.screenBrightness * 255;
Log.i(TAG, "current activity brightness: " + mGestureDownBrightness);
}
} else {//右側改變聲音
mChangeVolume = true;
mGestureDownVolume = mAudioManager.getStreamVolume(AudioManager.STREAM_MUSIC);
}
}
}
}
}
if (mChangePosition) {
long totalTimeDuration = getDuration();
mSeekTimePosition = (int) (mGestureDownPosition + deltaX * totalTimeDuration / mScreenWidth);
if (mSeekTimePosition > totalTimeDuration)
mSeekTimePosition = totalTimeDuration;
String seekTime = JZUtils.stringForTime(mSeekTimePosition);
String totalTime = JZUtils.stringForTime(totalTimeDuration);
showProgressDialog(deltaX, seekTime, mSeekTimePosition, totalTime, totalTimeDuration);
}
if (mChangeVolume) {
deltaY = -deltaY;
int max = mAudioManager.getStreamMaxVolume(AudioManager.STREAM_MUSIC);
int deltaV = (int) (max * deltaY * 3 / mScreenHeight);
mAudioManager.setStreamVolume(AudioManager.STREAM_MUSIC, mGestureDownVolume + deltaV, 0);
//dialog中顯示百分比
int volumePercent = (int) (mGestureDownVolume * 100 / max + deltaY * 3 * 100 / mScreenHeight);
showVolumeDialog(-deltaY, volumePercent);
}
if (mChangeBrightness) {
deltaY = -deltaY;
int deltaV = (int) (255 * deltaY * 3 / mScreenHeight);
WindowManager.LayoutParams params = JZUtils.getWindow(getContext()).getAttributes();
if (((mGestureDownBrightness + deltaV) / 255) >= 1) {//這和聲音有區別,必須自己過濾一下負值
params.screenBrightness = 1;
} else if (((mGestureDownBrightness + deltaV) / 255) <= 0) {
params.screenBrightness = 0.01f;
} else {
params.screenBrightness = (mGestureDownBrightness + deltaV) / 255;
}
JZUtils.getWindow(getContext()).setAttributes(params);
//dialog中顯示百分比
int brightnessPercent = (int) (mGestureDownBrightness * 100 / 255 + deltaY * 3 * 100 / mScreenHeight);
showBrightnessDialog(brightnessPercent);
// mDownY = y;
}
JzvdStd類,添加了視頻播放的佈局getLayoutId
根據廣播來顯示電量,利用 level * 100 / scale來計算當前電量的百分比。
private BroadcastReceiver battertReceiver = new BroadcastReceiver() {
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (Intent.ACTION_BATTERY_CHANGED.equals(action)) {
//當前電量
int level = intent.getIntExtra("level", 0);
//總電量
int scale = intent.getIntExtra("scale", 100);
//電量所佔的百分比計算
int percent = level * 100 / scale;
LAST_GET_BATTERYLEVEL_PERCENT = percent;
setBatteryLevel();
try {
getContext().unregisterReceiver(battertReceiver);
} catch (Exception e) {
e.printStackTrace();
}
}
}
};
利用廣播來修改電量的圖標,實現視頻上的圖片來發生變化
public void setBatteryLevel() {
int percent = LAST_GET_BATTERYLEVEL_PERCENT;
if (percent < 15) {
batteryLevel.setBackgroundResource(R.drawable.jz_battery_level_10);
} else if (percent >= 15 && percent < 40) {
batteryLevel.setBackgroundResource(R.drawable.jz_battery_level_30);
} else if (percent >= 40 && percent < 60) {
batteryLevel.setBackgroundResource(R.drawable.jz_battery_level_50);
} else if (percent >= 60 && percent < 80) {
batteryLevel.setBackgroundResource(R.drawable.jz_battery_level_70);
} else if (percent >= 80 && percent < 95) {
batteryLevel.setBackgroundResource(R.drawable.jz_battery_level_90);
} else if (percent >= 95 && percent <= 100) {
batteryLevel.setBackgroundResource(R.drawable.jz_battery_level_100);
}
}