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);
}
}