大家應該很喜歡這樣的場景:一邊打遊戲一邊看視頻,生活娛樂兩不誤。這樣應該怎麼去實現呢?Android有提供懸浮窗API,使用懸浮窗播放視頻,可以懸浮在其他應用上。有人可能會說,懸浮窗是不是會遮擋界面,導致用戶體驗不夠好。總是有辦法解決的,我們可以設計一個靈活的懸浮窗,窗口可以隨時調整大小、任意拖動位置,這樣就完美了。讓我們看看小窗口播放效果。
首先,需要在Manifest.xml裏申請權限:<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>。對於高版本API,需要動態申請權限:
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();
}
});
至此,小窗口播放功能基本實現了,我們可以開啓邊玩遊戲邊看視頻的愉快之旅。