android悬浮窗口 关闭,Android悬浮窗的创建及关闭

最近有看到直播类App在退出房间的时候会生成小的悬浮窗,继续播放,甚至当App界面全部关闭之后,还可以悬浮到手机桌面播放。虽然我此功能感觉很流氓,但还是研究了下怎么实现这种效果。查阅相关资料后,发现这种效果是通过Android的WindowManager实现的。接下来我说明下我的研究过程,和大家共享。

1.首先我们需要知道,Android的界面绘制,都是通过WindowManager来实现的。WindowManager实现了ViewManager接口,可以通过获取WINDOW_SERVICE系统服务得到。而ViewManager接口有addView方法,我们就是通过这个方法将悬浮窗控件加入到屏幕中去。

2.要使用windowManager,很显然需要添加一些权限。

在API Level >= 23的时候,需要在AndroidManefest.xml文件中声明权限SYSTEM_ALERT_WINDOW才能在其他应用上绘制控件。

3.为了让悬浮窗和Activity隔离,使其在应用处于后台时悬浮窗仍然可以正常运行,我们使用Service来实现。

首先我们在Activity中判断一下应用是否允许开启悬浮窗

private void floatWindow() {

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

if (!Settings.canDrawOverlays(this)) {

Toast.makeText(this, "当前无权限 canDrawOverlays", Toast.LENGTH_SHORT).show();

startActivityForResult(new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION, Uri.parse("package:" + getPackageName())), REQUEST_CODE);

} else {

startService(new Intent(GuideActivity.this, FloatingService.class));

}

}

}

@TargetApi(Build.VERSION_CODES.M)

@Override

protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {

super.onActivityResult(requestCode, resultCode, data);

if (requestCode == REQUEST_CODE) {

if (!Settings.canDrawOverlays(this)) {

Toast.makeText(this, "授权失败", Toast.LENGTH_SHORT).show();

} else {

Toast.makeText(this, "授权成功", Toast.LENGTH_SHORT).show();

startService(new Intent(GuideActivity.this, FloatingService.class));

}

}

}

然后我们看一下Service

@RequiresApi(api = Build.VERSION_CODES.M)

@Override

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

showFloatingView();

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

}

/**

* 悬浮窗控件可以是任意的View的子类类型

*/

@RequiresApi(api = Build.VERSION_CODES.M)

private void showFloatingView() {

if (Settings.canDrawOverlays(getApplicationContext())) {

//WindowManager 对象

manager = (WindowManager) getSystemService(WINDOW_SERVICE);

//新建悬浮控件

//视频播放

displayView = View.inflate(getApplicationContext(), R.layout.view_display, null);

MediaPlayer mediaPlayer = new MediaPlayer();

mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);

SurfaceView surfaceView = displayView.findViewById(R.id.video_display_surfaceview);

final SurfaceHolder surfaceHolder = surfaceView.getHolder();

surfaceHolder.addCallback(new SurfaceHolder.Callback() {

@Override

public void surfaceCreated(SurfaceHolder holder) {

mediaPlayer.setDisplay(surfaceHolder);

}

@Override

public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {

}

@Override

public void surfaceDestroyed(SurfaceHolder holder) {

}

});

mediaPlayer.setOnPreparedListener(mp -> mediaPlayer.start());

try {

mediaPlayer.setDataSource(this, Uri.parse("https://raw.githubusercontent.com/dongzhong/ImageAndVideoStore/master/Bruno%20Mars%20-%20Treasure.mp4"));

mediaPlayer.prepareAsync();

} catch (IOException e) {

Toast.makeText(this, "无法打开视频源", Toast.LENGTH_LONG).show();

}

//设置layoutParams

params = new WindowManager.LayoutParams();

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

params.type = WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;

} else {

params.type = WindowManager.LayoutParams.TYPE_PHONE;

}

params.format = PixelFormat.RGBA_8888;

// params.width = WindowManager.LayoutParams.WRAP_CONTENT;

// params.height = WindowManager.LayoutParams.WRAP_CONTENT;

params.width = 600;

params.height = 400;

//设置不阻挡其他view的触摸事件

params.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;

// params.x = 500;

// params.y = 300;

// params.gravity = Gravity.CENTER_HORIZONTAL;

//添加view到windowManager

manager.addView(displayView, params);

//触摸事件

displayView.setOnTouchListener(new OnFloatingButtonTouchListener());

}

}

设置完之后,就可以看到最基本的效果了。当然这只是最基本的功能,和我们想要的效果还有差距。

接下来我们研究下如何让window跟随手势移动。我们需要重写OnTouch事件,在ACTION_MOVE中操作。

private class OnFloatingButtonTouchListener implements View.OnTouchListener {

private int x;

private int y;

@Override

public boolean onTouch(View v, MotionEvent event) {

switch (event.getAction()) {

case MotionEvent.ACTION_DOWN:

x = (int) event.getRawX();

y = (int) event.getRawY();

break;

case MotionEvent.ACTION_UP:

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;

params.x = params.x + movedX;

params.y = params.y + movedY;

manager.updateViewLayout(displayView, params);

break;

}

return false;

}

}

然后我们给View设置onTouch事件就可以拖动了。

至此添加一个view到桌面就基本完成了,我们总结下步骤

1. 声明及申请权限

2. 构建悬浮窗需要的控件

3. 将控件添加到WindowManager

4. 必要时更新WindowManager的布局

既然我们添加完成,那么有的同学会问到,这个东西怎么关闭啊 。

接下来我们实现下怎么关闭悬浮窗。

想到创建windowManager是在Service中完成的,但是我们又不能直接操作Service,那么我们换一种方式来实现,使用BindService来开启服务。BindService可以获取到Service对象,因此可以实现对Service的操作。

首先在Activity中创建ServiceConnection对象,重写方法。

然后回到Service中

private MyBinder binder = new MyBinder();

public class MyBinder extends Binder {

public FloatingService getService() {

return FloatingService.this;

}

}

然后回到ServiceConnection的onServiceConnected方法,获取到Service对象。

FloatingService.MyBinder binder = (FloatingService.MyBinder) service;

floatingService = binder.getService();

至此我们可以获取到service对象,然后在Service中创建移除window方法

public void closeWindow() {

manager.removeView(displayView);

}

然后就可以开心的关闭此窗口了。

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值