大家应该很喜欢这样的场景:一边打游戏一边看视频,生活娱乐两不误。这样应该怎么去实现呢?Android有提供悬浮窗API,使用悬浮窗播放视频,可以悬浮在其他应用上。有人可能会说,悬浮窗是不是会遮挡界面,导致用户体验不够好。总是有办法解决的,我们可以设计一个灵活的悬浮窗,窗口可以随时调整大小、任意拖动位置,这样就完美了。让我们看看小窗口播放效果。
首先,需要在Manifest.xml里申请权限:
private void requestAlertWindowPermission() {
Intent intent = new Intent("android.settings.action.MANAGE_OVERLAY_PERMISSION");
intent.setData(Uri.parse("package:" + getPackageName()));
startActivityForResult(intent, 123);
}
然后,创建一个FloatPlayerVView,添加到FloatWindow容器里。设置小窗口的x、y坐标位置,以及宽度、高度。开启小窗口后,视频会自动播放。差不多10多行代码:
private void floatingToPlay(){
if (FloatWindow.get() != null) {
return;
}
FloatPlayerView floatPlayerView = new FloatPlayerView(getApplicationContext());
FloatWindow
.with(getApplicationContext())
.setView(floatPlayerView)
.setWidth(Screen.width, 0.4f)
.setHeight(Screen.width, 0.4f)
.setX(Screen.width, 0.8f)
.setY(Screen.height, 0.3f)
.setMoveType(MoveType.slide)
.setFilter(false)
.setMoveStyle(500, new BounceInterpolator())
.build();
FloatWindow.get().show();
}
FloatPlayerView主要是创建一个VideoView播放器,添加到FrameLayout容器里,接着设置播放地址,开始播放。
private void init(Context context) {
mPlayer = new VideoView(context);
LayoutParams layoutParams = new LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
layoutParams.gravity = Gravity.CENTER;
addView(mPlayer, layoutParams);
mPlayer.setVideoPath(VIDEO_PATH);
mPlayer.requestFocus();
mPlayer.start();
}
FloatWindow提供设置窗口大小、窗口位置、加载悬浮窗视图,加载过程是使用LayoutInflate实现:
static View inflate(Context applicationContext, int layoutId) {
LayoutInflater inflate = (LayoutInflater) applicationContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
return inflate.inflate(layoutId, null);
}
为了实现悬浮窗任意拖动,我们可以监听FloatWindow的TouchListener事件:
getView().setOnTouchListener(new View.OnTouchListener() {
float lastX, lastY, changeX, changeY;
int newX, newY;
@Override
public boolean onTouch(View v, MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
lastX = event.getRawX();
lastY = event.getRawY();
cancelAnimator();
break;
case MotionEvent.ACTION_MOVE:
changeX = event.getRawX() - lastX;
changeY = event.getRawY() - lastY;
newX = (int) (mFloatView.getX() + changeX);
newY = (int) (mFloatView.getY() + changeY);
mFloatView.updateXY(newX, newY);
lastX = event.getRawX();
lastY = event.getRawY();
break;
case MotionEvent.ACTION_UP:
switch (mB.mMoveType) {
case MoveType.slide:
int startX = mFloatView.getX();
int endX = (startX * 2 + v.getWidth() >
Util.getScreenWidth(mB.mApplicationContext)) ?
Util.getScreenWidth(mB.mApplicationContext) - v.getWidth() : 0;
mAnimator = ObjectAnimator.ofInt(startX, endX);
mAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
int x = (int) animation.getAnimatedValue();
mFloatView.updateX(x);
}
});
startAnimator();
break;
case MoveType.back:
PropertyValuesHolder pvhX = PropertyValuesHolder.ofInt("x", mFloatView.getX(), mB.xOffset);
PropertyValuesHolder pvhY = PropertyValuesHolder.ofInt("y", mFloatView.getY(), mB.yOffset);
mAnimator = ObjectAnimator.ofPropertyValuesHolder(pvhX, pvhY);
mAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
int x = (int) animation.getAnimatedValue("x");
int y = (int) animation.getAnimatedValue("y");
mFloatView.updateXY(x, y);
}
});
startAnimator();
break;
}
break;
}
return false;
}
});
为了保证小窗口能及时退出播放,不影响其他操作。我们可以设置LifeCycleListener监听器,比如监听到用户按下Home键、返回键等事件,就退出小窗口播放:
new FloatLifecycle(mB.mApplicationContext, mB.mShow, mB.mActivities, new LifecycleListener() {
@Override
public void onShow() {
show();
}
@Override
public void onHide() {
hide();
}
@Override
public void onPostHide() {
postHide();
}
});
至此,小窗口播放功能基本实现了,我们可以开启边玩游戏边看视频的愉快之旅。