Android懸浮窗播放視頻

大家應該很喜歡這樣的場景:一邊打遊戲一邊看視頻,生活娛樂兩不誤。這樣應該怎麼去實現呢?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();
    }
});

至此,小窗口播放功能基本實現了,我們可以開啓邊玩遊戲邊看視頻的愉快之旅。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章