上例牌 github>>>>>>>> github.com/CarGuo 对,就是这个郭老司机。javascript
本期就不话唠了,周一谁有精力说话呢┑( ̄Д  ̄)┍,程序猿的周末是什么?java
看太小喵上一篇视频相关文章的应该知道小喵手贱的用了两种实现方式,一种是基于懒人的系统层模式;一种是基于单例的UI逻辑播放器的模式的ListVideoUtil。至于为何是两种呢?由于手贱啊。(ノಠ益ಠ)ノ彡┻━┻,本文若有不明之处可结合前文一块儿食用:《Android 实现视屏播放器、边播边缓存功能、外加铲屎(IJKPlayer)》。android
伟人曾经说过,每个Activity都有一个本身的默认布局,这里面又包含有了一个com.android.internal.R.id.content,并且是一个FrameLayout(请无视上面的废话),如此看来用来做为咱们全屏显示的父布局妥妥的。此处手贱的加入了动画效果的支持,一直以为5.0的过渡动画挺高大上的,做为一个material design的应用必须有这样的逼格(什么?你说兼容?这里美女太多我听不到····)。git
做为一只内向的程序猿,语言组织能力有限,咱们仍是从代码上来,从代码上去吧,注释满满的,顺序看下去不难理解(前提是你看的下,确实长♂了点)。github
怎么样,看起来是否是有些混乱?(ノಠ益ಠ)ノ彡┻━┻,我就说程序猿仍是看代码好沟通是吧,虽然很长就是。api
//得到com.android.internal.R.id.content
private ViewGroup getViewGroup() {
return (ViewGroup) (CommonUtil.scanForActivity(getContext())).findViewById(Window.ID_ANDROID_CONTENT);
}
···此处省略无数只草泥马
//这两个是TextureView的回调,在这remove和onPause还有add的时候基本会进入
@Override
public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) {
//更新数据到这个surface上渲染
mSurface = new Surface(surface);
GSYVideoManager.instance().setDisplay(mSurface);
}
@Override
public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) {
//告诉数据管理器这个渲染控件放弃了
GSYVideoManager.instance().setDisplay(null);
surface.release();
return true;
}
···此处省略无数只草泥马
这个开始全屏的页面逻辑
//将播放的视频渲染控件移除,进入上面的回调,让新的逻辑播放器能够接入
if (mTextureViewContainer.getChildCount() > 0) {
mTextureViewContainer.removeAllViews();
}
//保存全屏以前的状态栏和
saveLocationStatus(context, statusBar, actionBar);
try {
//生成一个播放器,由于继承关系,会建立一个当前列表item同样的UI逻辑播放器
//这些逻辑都是写在GSYBaseVideoPlayer这个抽象类下
Constructor<GSYBaseVideoPlayer> constructor = (Constructor<GSYBaseVideoPlayer>) GSYBaseVideoPlayer.this.getClass().getConstructor(Context.class);
final GSYBaseVideoPlayer gsyVideoPlayer = constructor.newInstance(getContext());
//给它一个固定的id,在这样移除的时候就知道在哪里
gsyVideoPlayer.setId(FULLSCREEN_ID);
//获取屏幕的高度
WindowManager wm = (WindowManager) getContext().getSystemService(Context.WINDOW_SERVICE);
final int w = wm.getDefaultDisplay().getWidth();
final int h = wm.getDefaultDisplay().getHeight();
//建立一个层用于加入都window层中,设置为黑色,用于包含著播放器
FrameLayout.LayoutParams lpParent = new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
FrameLayout frameLayout = new FrameLayout(context);
frameLayout.setBackgroundColor(Color.BLACK);
//若是5.0的机器就执行动画,这里其实能够用VauleAnimaton兼容5.0如下的
if (mShowFullAnimation && Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
//先把播放器的位置设置为在列表中同样位置
FrameLayout.LayoutParams lp = new FrameLayout.LayoutParams(getWidth(), getHeight());
lp.setMargins(mListItemRect[0], mListItemRect[1], 0, 0);
frameLayout.addView(gsyVideoPlayer, lp);
vp.addView(frameLayout, lpParent);
//稍微延时执行动画
mHandler.postDelayed(new Runnable() {
@Override
public void run() {
//开启5.0动画
TransitionManager.beginDelayedTransition(vp);
//将播放器跳转为充满居中,系统自动过渡
resolveFullVideoShow(context, gsyVideoPlayer, h, w);
}
}, 300);
} else {
//非5.0的直接将播放器的布局加入到布局下
FrameLayout.LayoutParams lp = new FrameLayout.LayoutParams(getWidth(), getHeight());
frameLayout.addView(gsyVideoPlayer, lp);
vp.addView(frameLayout, lpParent);
//将播放器跳转为充满居中
resolveFullVideoShow(context, gsyVideoPlayer, h, w);
}
//设置全屏逻辑播放器和当前列表的逻辑状态一致
gsyVideoPlayer.setUp(mUrl, mCache, mObjects);
gsyVideoPlayer.setStateAndUi(mCurrentState);
//添加上渲染控件,通知数据加载管理器是用这个渲染
gsyVideoPlayer.addTextureView();
//配置对应UI
gsyVideoPlayer.getFullscreenButton().setImageResource(R.drawable.video_shrink);
gsyVideoPlayer.getFullscreenButton().setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
clearFullscreenLayout();
}
});
gsyVideoPlayer.getBackButton().setVisibility(VISIBLE);
gsyVideoPlayer.getBackButton().setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
clearFullscreenLayout();
}
});
//将数据加载管理器的接口回到配置到全屏播放器里面
GSYVideoManager.instance().setLastListener(this);
GSYVideoManager.instance().setListener(gsyVideoPlayer);
} catch (Exception e) {
e.printStackTrace();
}
···此处省略无数只草泥马
/** * 全屏 */
private void resolveFullVideoShow(Context context, GSYBaseVideoPlayer gsyVideoPlayer) {
//清除动画的margin
FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) gsyVideoPlayer.getLayoutParams();
lp.setMargins(0, 0, 0, 0);
//居中充满
lp.height = ViewGroup.LayoutParams.MATCH_PARENT;
lp.width = ViewGroup.LayoutParams.MATCH_PARENT;
lp.gravity = Gravity.CENTER;
gsyVideoPlayer.setLayoutParams(lp);
gsyVideoPlayer.setIfCurrentIsFullscreen(true);
//加入旋转工具类
mOrientationUtils = new OrientationUtils((Activity) context, gsyVideoPlayer);
mOrientationUtils.setEnable(mRotateViewAuto);
}复制代码
既然都进去了♂,出来还难吗?因此咱们只须要反着来就好了,下面直接长代码,有注释。(男人长一点有什么错┑( ̄Д  ̄)┍)缓存
/** * 退出系统层播放全屏效果 */
public void clearFullscreenLayout() {
//须要判断当前是否横屏,是的话要转为界面以后稍等一会在退回,这样才不会界面抖动
int delay = mOrientationUtils.backToProtVideo();
//关闭旋转
mOrientationUtils.setEnable(false);
mHandler.postDelayed(new Runnable() {
@Override
public void run() {
backToNormal();
}
}, delay);
}
/** * 回到正常效果 */
private void backToNormal() {
//恢复状态
showSupportActionBar(mContext, mActionBar, mStatusBar);
final ViewGroup vp = getViewGroup();
//拿到content和播放器
final View oldF = vp.findViewById(FULLSCREEN_ID);
final GSYVideoPlayer gsyVideoPlayer;
if (oldF != null) {
gsyVideoPlayer = (GSYVideoPlayer) oldF;
if (mShowFullAnimation && Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
TransitionManager.beginDelayedTransition(vp);
//执行动画回到本来的列表中的位置
FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) gsyVideoPlayer.getLayoutParams();
lp.setMargins(mListItemRect[0], mListItemRect[1], 0, 0);
lp.width = mListItemSize[0];
lp.height = mListItemSize[1];
//注意配置回来,否则动画效果会不对
lp.gravity = Gravity.NO_GRAVITY;
gsyVideoPlayer.setLayoutParams(lp);
new Handler().postDelayed(new Runnable() {
@Override
public void run() {
resolveNormalVideoShow(oldF, vp, gsyVideoPlayer);
}
}, 400);
} else {
//直接移除
resolveNormalVideoShow(oldF, vp, gsyVideoPlayer);
}
} else {
//直接移除
resolveNormalVideoShow(null, vp, null);
}
}
/** * 恢复 */
private void resolveNormalVideoShow(View oldF, ViewGroup vp, GSYVideoPlayer gsyVideoPlayer) {
//移除全屏播放器
if (oldF.getParent() != null) {
ViewGroup viewGroup = (ViewGroup) oldF.getParent();
vp.removeView(viewGroup);
}
//拿回状态
mCurrentState = GSYVideoManager.instance().getLastState();
if (gsyVideoPlayer != null) {
mCurrentState = gsyVideoPlayer.getCurrentState();
}
//从新设置回调
GSYVideoManager.instance().setListener(GSYVideoManager.instance().lastListener());
GSYVideoManager.instance().setLastListener(null);
//播放器恢复
setStateAndUi(mCurrentState);
//通知数据加载播放器用回列表的渲染
addTextureView();
CLICK_QUIT_FULLSCREEN_TIME = System.currentTimeMillis();
}复制代码
整体上逻辑和上文是一致的,只是这种实如今列表中是不包含逻辑播放器,逻辑播放器和全屏逻辑播放器都是一个单例,须要你手动在list列表的最外层加多一个布局作全屏播放,在每一个item那里预留一个位置用于包容列表的播放器,还有一个播放按钮用于播放。ide
感受很麻烦是吧,耦合度又高,可是它能够在视频滑出界面的时候不被释放,一直保持在原来的位置。工具
和上面的逻辑基本一致,就不废话了(能够偷懒了),只须要注意用的时候操做方式不同,总结起来就是有些麻烦。布局
//配置好全屏布局
listVideoUtil.setFullViewContainer(videoFullContainer);
listVideoUtil.setHideStatusBar(true);
···此处省略无数只草泥马
//增长封面
ImageView imageView = new ImageView(context);
imageView.setScaleType(ImageView.ScaleType.CENTER_CROP);
imageView.setImageResource(R.mipmap.xxx1);
//将列表的位置,封面,列表的TAG,列表是的父布局,播放按键传入进去
listVideoUtil.addVideoPlayer(position, imageView, TAG, holder.videoContainer, holder.playerBtn);
holder.playerBtn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//更新其余item
notifyDataSetChanged();
//设置播放器的标志位,防止错位
listVideoUtil.setPlayPositionAndTag(position, TAG);
//url开始播放
final String url = "http://baobab.wdjcdn.com/14564977406580.mp4";
listVideoUtil.startPlay(url);
}
});复制代码
有时候咱们会想要视频滑出屏幕的时候有个小窗口在右下角,最好仍是能够关闭和拖动的(看视频的时候能够快速最小化收起来,不中止,避免尴尬对吧)。逻辑和实现全屏同样,用系统的content层来承载,不一样的是利用margin让视频出如今右下角,这样咱们拖动的时候只要改变视频的margin,就可让视频小窗体在它的父布局内移动啦。
/** * 显示小窗口 */
public void showSmallVideo(Point size, final boolean actionBar, final boolean statusBar) {
//利用content实现,和全屏同样,只是大小和背景色不同
final ViewGroup vp = getViewGroup();
removeVideo(vp, SMALL_ID);
if (mTextureViewContainer.getChildCount() > 0) {
mTextureViewContainer.removeAllViews();
}
try {
Constructor<GSYBaseVideoPlayer> constructor = (Constructor<GSYBaseVideoPlayer>) GSYBaseVideoPlayer.this.getClass().getConstructor(Context.class);
GSYBaseVideoPlayer gsyVideoPlayer = constructor.newInstance(getContext());
gsyVideoPlayer.setId(SMALL_ID);
FrameLayout.LayoutParams lpParent = new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
FrameLayout frameLayout = new FrameLayout(mContext);
FrameLayout.LayoutParams lp = new FrameLayout.LayoutParams(size.x, size.y);
int marginLeft = CommonUtil.getScreenWidth(mContext) - size.x;
int marginTop = CommonUtil.getScreenHeight(mContext) - size.y;
if (actionBar) {
marginTop = marginTop - getActionBarHeight((Activity) mContext);
}
if (statusBar) {
marginTop = marginTop - getStatusBarHeight(mContext);
}
//利用margin让视频出如今右下角,这样咱们拖动的时候只要改变margin就好啦
lp.setMargins(marginLeft, marginTop, 0, 0);
frameLayout.addView(gsyVideoPlayer, lp);
vp.addView(frameLayout, lpParent);
//继续播放
gsyVideoPlayer.setUp(mUrl, mCache, mObjects);
gsyVideoPlayer.setStateAndUi(mCurrentState);
gsyVideoPlayer.addTextureView();
gsyVideoPlayer.onClickUiToggle();
gsyVideoPlayer.setSmallVideoTextureView(new SmallVideoTouch(gsyVideoPlayer, marginLeft, marginTop));
GSYVideoManager.instance().setLastListener(this);
GSYVideoManager.instance().setListener(gsyVideoPlayer);
} catch (InstantiationException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
}
/** * 隐藏小窗口 */
public void hideSmallVideo() {
final ViewGroup vp = getViewGroup();
GSYVideoPlayer gsyVideoPlayer = (GSYVideoPlayer) vp.findViewById(SMALL_ID);
removeVideo(vp, SMALL_ID);
mCurrentState = GSYVideoManager.instance().getLastState();
if (gsyVideoPlayer != null) {
mCurrentState = gsyVideoPlayer.getCurrentState();
}
GSYVideoManager.instance().setListener(GSYVideoManager.instance().lastListener());
GSYVideoManager.instance().setLastListener(null);
setStateAndUi(mCurrentState);
addTextureView();
CLICK_QUIT_FULLSCREEN_TIME = System.currentTimeMillis();
}复制代码
这是触摸逻辑,这拖动的视频窗体的时候,经过改变margin来实现窗体的移动,注意不要跑飞了就要,加个阈值。多说无益,看代码(又省下了好多字):
public class SmallVideoTouch implements View.OnTouchListener {
private int mDownX, mDownY;
private int mMarginLeft, mMarginTop;
private int _xDelta, _yDelta;
private GSYBaseVideoPlayer mGsyBaseVideoPlayer;
public SmallVideoTouch(GSYBaseVideoPlayer gsyBaseVideoPlayer, int marginLeft, int marginTop) {
super();
mMarginLeft = marginLeft;
mMarginTop = marginTop;
mGsyBaseVideoPlayer = gsyBaseVideoPlayer;
}
@Override
public boolean onTouch(View view, MotionEvent event) {
final int X = (int) event.getRawX();
final int Y = (int) event.getRawY();
switch (event.getAction() & MotionEvent.ACTION_MASK) {
case MotionEvent.ACTION_DOWN:
mDownX = X;
mDownY = Y;
FrameLayout.LayoutParams lParams = (FrameLayout.LayoutParams) mGsyBaseVideoPlayer
.getLayoutParams();
_xDelta = X - lParams.leftMargin;
_yDelta = Y - lParams.topMargin;
break;
case MotionEvent.ACTION_UP:
if (Math.abs(mDownY - Y) < 5 && Math.abs(mDownX - X) < 5) {
return false;
} else {
return true;
}
case MotionEvent.ACTION_MOVE:
FrameLayout.LayoutParams layoutParams = (FrameLayout.LayoutParams) mGsyBaseVideoPlayer
.getLayoutParams();
layoutParams.leftMargin = X - _xDelta;
layoutParams.topMargin = Y - _yDelta;
//不能超过屏幕上下左右的位置
if (layoutParams.leftMargin >= mMarginLeft) {
layoutParams.leftMargin = mMarginLeft;
}
if (layoutParams.topMargin >= mMarginTop) {
layoutParams.topMargin = mMarginTop;
}
if (layoutParams.leftMargin <= 0) {
layoutParams.leftMargin = 0;
}
if (layoutParams.topMargin <= 0) {
layoutParams.topMargin = 0;
}
mGsyBaseVideoPlayer.setLayoutParams(layoutParams);
}
return false;
}
}复制代码
若是你看到这里,恭喜你看完了<( ̄︶ ̄)>!那么,下面还有沙发,请问您要坐一坐吗?d=====( ̄▽ ̄*)b不坐也不要紧,还有github能够去呢:github.com/CarGuo 。