android 系统级的悬浮窗实现

android系统级悬浮球的实现流程

当我们在使用的app的时候,如果需要实时观测到某个功能的实时进度并且不影响其他的操作的时候或者不影响使用其他应用的时候,系统级的悬浮球是个非常不错的选择。

首先我们需要创建一条Service服务用来承载悬浮球。

public class QueueUpFloatService extends Service {

/**

* 启动服务并传值

*

* @param activity 启动服务的activity

* @param modeBean 数据对象

*/

public static void launchService(Activity activity, ModeBean modeBean) {

try {

        Intent intent =new Intent(activity, QueueUpFloatService.class);

        Bundle bundle =new Bundle();

        bundle.putSerializable(KEY_MODEL, modeBean);

        intent.putExtras(bundle);

        activity.startService(intent);

    }catch (Exception e) {

        e.printStackTrace();

    }

}

    @Override

    public void onCreate() {

            super.onCreate();

    }

    @Override

    public IBinder onBind(Intent intent) {

            return null;

    }

    @Override

    public int onStartCommand(Intent intent, int flags, int startId) {

            return super.onStartCommand(intent, flags, startId);

    }

    @Override

    public void onDestroy() {

            super.onDestroy();

    }

}

在onCreate中初始化WindowManager  并声明相关属性 

@Override

public void onCreate() {

    super.onCreate();

    //加一点简单的动画 

    buttonScale = (ScaleAnimation) AnimationUtils.loadAnimation(this, R.anim.anim_float);

    windowManager = (WindowManager) getSystemService(WINDOW_SERVICE);

    layoutParams =new WindowManager.LayoutParams();

    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {

            layoutParams.type = WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;

    }else {

            layoutParams.type = WindowManager.LayoutParams.TYPE_PHONE;

    }

    layoutParams.format = PixelFormat.RGBA_8888;

    layoutParams.gravity = Gravity.LEFT | Gravity.TOP;

    layoutParams.flags = WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL |             WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;

    layoutParams.width = ScreenUtils.dp2px(66);

    layoutParams.height = ScreenUtils.dp2px(66);

    layoutParams.x = ScreenUtils.getRealWidth() - ScreenUtils.dp2px(60);

    layoutParams.y = ScreenUtils.deviceHeight() *2 /3;

}

在onStartCommand中初始化view 并添加到windowManager中

@Override

public int onStartCommand(Intent intent, int flags, int startId) {

    ModeBean modeBean = (ModeBean) intent.getExtras().getSerializable(KEY_MODEL);

    LayoutInflater layoutInflater = LayoutInflater.from(this);

    floatView = layoutInflater.inflate(R.layout.view_float, null);

    RelativeLayout rlFloatParent =floatView.findViewById(R.id.rl_float_parent);

    rlFloatParent.startAnimation(buttonScale);

    TextView tvIndex =floatView.findViewById(R.id.tv_queue_index);

    tvIndex.setText(modeBean.title);

    floatView.findViewById(R.id.iv_close_float).setOnClickListener(v -> stopSelf());

    //修改悬浮球的滑动实现

    floatView.setOnTouchListener(new FloatingOnTouchListener());

    windowManager.addView(floatView, layoutParams);

    return super.onStartCommand(intent, flags, startId);

}

悬浮球试图的滑动监听实现

private class FloatingOnTouchListenerimplements View.OnTouchListener {

    private int x;

    private int y;

    private long downTime;

    @SuppressLint("ClickableViewAccessibility")

    @Override

    public boolean onTouch(View view, MotionEvent event) {

            switch (event.getAction()) {

                case MotionEvent.ACTION_DOWN:

                        downTime = System.currentTimeMillis();

                        x = (int) event.getRawX();

                        y = (int) event.getRawY();

                        break;

                case MotionEvent.ACTION_MOVE:

                        int nowX = (int) event.getRawX();

                        int nowY = (int) event.getRawY();

                        int movedX = nowX -x;

                        int movedY = nowY -y;

                        x = nowX;

                        y = nowY;

                        layoutParams.x =layoutParams.x + movedX;

                        layoutParams.y =layoutParams.y + movedY;

                        windowManager.updateViewLayout(view, layoutParams);

                        break;

                case MotionEvent.ACTION_UP:

                        /* *

                        * 这里根据手指按下和抬起的时间差来判断点击事件还是滑动事件

                        * */

                        if ((System.currentTimeMillis() -downTime) <200) {

                                //检测应用在前台还是后台

                                if (AppUtils.isAppIsInBackground()) {

                                        AppUtils.moveToFront(CloseActivityUtils.activityList.get(CloseActivityUtils.activityList.size() -1).getClass());

                                } else {

                                        //检测栈顶是否为SecondActivity 不是就打开SecondActivity

                                      if (!CloseActivityUtils.activityList.get(CloseActivityUtils.activityList.size() -1)

                                                .getClass().getSimpleName().contains("SecondActivity")) {

                                        SecondActivity.launchActivity(CloseActivityUtils.activityList.get(CloseActivityUtils.activityList.size() -1));

                                }

                      }

            }

                        break;

            default:

                        break;

        }

           return false;

    }

}

在service的onDestory方法中移除视图,即关闭服务,的时候移除视图

        @Override

        public void onDestroy() {

            super.onDestroy();

            if (null ==floatView) {

                return;

               }

            windowManager.removeView(floatView);

            windowManager=null;

}

总体的实现思路比较简单,通过Service创建windowManager并添加相应试图,判断app(前后台)的状态,然后做对应操作即可,在主activity(MainActivity)的ondestory的方法中终止service,stopService(new Intent(this, QueueUpFloatService.class)),在杀掉app进程的时候及时关闭悬浮窗。

demo地址:https://github.com/javen1/FloatDemo 

你可能感兴趣的:(android 系统级的悬浮窗实现)