一、简介

这个案例就是在桌面上开启一个悬浮窗,悬浮窗里实时显示照相机的内容,可以自由拖动,当在非桌面状态下自动隐藏.如下图所示():
PS:gif都失真了,凑合看,实际中这个窗口是不会闪烁的

我做这个是因为公司项目里在android系统的NavigationBar里显示了行车记录仪,实时录像.我想把类似的思路分享出来.通过这个可以学习TextureView和自定义悬浮窗口的知识.

二、实现

1、显示一个悬浮窗口

在MainActivity里启动一个服务,在服务里进行悬浮窗的操作

Button btn = (Button) findViewById(R.id.btn);btn.setOnClickListener(new OnClickListener() {@Overridepublic void onClick(View v) {// 启动服务,在服务里开启悬浮窗Intent intent = new Intent(MainActivity.this,MyService.class);startService(intent);}});

新建一个MyService继承自Service,在onCreate方法里加上如下代码

//对于6.0以上的设备if (Build.VERSION.SDK_INT >= 23) {//如果支持悬浮窗功能if (Settings.canDrawOverlays(getApplicationContext())) {showWindow();} else {//手动去开启悬浮窗Intent intent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION);intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);getApplicationContext().startActivity(intent);}} else {//6.0以下的设备直接开启showWindow();}}

谷歌对于6.0以上的设备,默认是把悬浮窗功能给禁了,所以需要手动去打开.我用的小米就是这样,需要手动在设置里打开显示悬浮窗的权限.

    - Settings.canDrawOverlays(context)方法是判断当前系统是否支持悬浮窗- Settings.ACTION_MANAGE_OVERLAY_PERMISSION 是跳转到打开悬浮窗的ACTION

以上两个都是在6.0以上的SDK里才有.

对于6.0一下的设备可以直接显示.
看下showWindow()里的代码

private void showWindow() {//创建MyWindow的实例myWindow = new MyWindow(getApplicationContext());//窗口管理者mWindowManager = (WindowManager) getSystemService(Service.WINDOW_SERVICE);//窗口布局参数Params = new WindowManager.LayoutParams();//布局坐标,以屏幕左上角为(0,0)Params.x = 0;Params.y = 0;//布局类型Params.type = WindowManager.LayoutParams.TYPE_SYSTEM_ALERT; // 系统类型//布局flagsParams.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE; // 无焦点Params.flags = Params.flags | WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH;Params.flags = Params.flags | WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS; //无限制布局 Params.flags |= WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED;//布局的gravityParams.gravity = Gravity.LEFT | Gravity.TOP;//布局的宽和高Params.width =  500;Params.height = 500;myWindow.setOnTouchListener(new OnTouchListener() {@Overridepublic boolean onTouch(View v, MotionEvent event) {switch (event.getAction()) {                case MotionEvent.ACTION_MOVE://在移动时更新坐标Params.x = (int) event.getRawX() - myWindow.getWidth() / 2;Params.y = (int) event.getRawY() - myWindow.getHeight() / 2;//更新布局位置mWindowManager.updateViewLayout(myWindow, Params);break;}return false;}});}

首先创建了MyWindow实例,这是一个自定义布局

public class MyWindow extends LinearLayout implements SurfaceTextureListener {......public MyWindow(Context context) {super(context);LayoutInflater.from(context).inflate(R.layout.window, this);this.context = context;initView();}......}

加载布局文件进来,这个布局里放了一个Textureview和一个TextView.
创建MyWindow 实例后,获取WindowManager和布局参数LayoutParams.

   - 设置LayoutParams的坐标x,y- 设置LayoutParams的类型为TYPE_SYSTEM_ALERT- 设置LayoutParams的flags- 设置LayoutParams的gravity- 设置LayoutParams的宽和高

这样一个Window的属性设置完了.
最后设置一个触摸监听,让悬浮窗跟随手指移动.

public class MyService extends Service {......
myWindow.setOnTouchListener(new OnTouchListener() {@Overridepublic boolean onTouch(View v, MotionEvent event) {switch (event.getAction()) {                case MotionEvent.ACTION_MOVE:Params.x = (int) event.getRawX() - myWindow.getWidth() / 2;Params.y = (int) event.getRawY() - myWindow.getHeight() / 2;//更新布局位置mWindowManager.updateViewLayout(myWindow, Params);break;}return false;}});......}

那么什么时候显示和隐藏悬浮窗呢?
我前面说了,会在非桌面界面隐藏该悬浮窗.在桌面界面显示悬浮窗.在这里我通过开启一个定时器每隔一秒发一个消息到Handler,在Handler里判断当前界面是否是桌面.

public class MyService extends Service {......
// 定时器类Timer timer = new Timer();timer.schedule(task, 1000, 1000); // 1s后执行task,经过1s再次执行......}
public class MyService extends Service {......
//定时发送message给HandlerTimerTask task = new TimerTask() {@Overridepublic void run() {Message message = new Message();handler.sendMessage(message);}};......}
public class MyService extends Service {......
private Handler handler = new Handler() {public void handleMessage(Message msg) {if (isHome()) {// 如果回到桌面,则显示悬浮窗if (!myWindow.isAttachedToWindow()) {mWindowManager.addView(myWindow, Params);}} else {// 如果在非桌面,则去掉悬浮窗if (myWindow.isAttachedToWindow()) {mWindowManager.removeView(myWindow);}}super.handleMessage(msg);};};......}

这样就完成了一个悬浮窗的显示和隐藏.进入其他应用时会隐藏该悬浮窗,回到桌面时又会自动显示出来.这里用到了两个方法.

 - 获取桌面(Launcher)的包名的方法getHomes- 判断当前是否是桌面的方法isHome
/*** @return 获取桌面(Launcher)的包名*/private List<String> getHomes() {List<String> names = new ArrayList<String>();PackageManager packageManager = this.getPackageManager();Intent intent = new Intent(Intent.ACTION_MAIN);intent.addCategory(Intent.CATEGORY_HOME);List<ResolveInfo> resolveInfo = packageManager.queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY);for (ResolveInfo info : resolveInfo) {names.add(info.activityInfo.packageName);}return names;}
/*** @return 判断当前是否是桌面*/public boolean isHome() {ActivityManager mActivityManager = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);List<RunningTaskInfo> rti = mActivityManager.getRunningTasks(1);List<String> strs = getHomes();if (strs != null && strs.size() > 0) {return strs.contains(rti.get(0).topActivity.getPackageName());} else {return false;}}

2、在窗口里显示照相机

这里要用到TextureView这个控件,它和SurfaceView是 “兄弟”.
与SurfaceView相比,TextureView并没有创建一个单独的Surface用来绘制,这使得它可以像一般的View一样执行一些变换操作,设置透明度等。另外,Textureview必须在硬件加速开启的窗口中。对应的就是这条属性

Params.flags |= WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED;

以下代码是在MyWindow里实现的

private void initView() {//初始化textureView = (TextureView) findViewById(R.id.textureView);//设置监听textureView.setSurfaceTextureListener(this);//获取WindowManagermWindowManager = (WindowManager) context.getSystemService(Service.WINDOW_SERVICE);}

设置监听需要实现SurfaceTextureListener接口,会重载下面4个方法

 - onSurfaceTextureAvailable 可用时候执行- onSurfaceTextureDestroyed 销毁的时候执行- onSurfaceTextureSizeChanged 尺寸改变时执行- onSurfaceTextureUpdated 更新的时候执行-

这里在onSurfaceTextureAvailable方法执行一下操作.
1.创建Camera实例(我这里用的是旧版本的Camera,已经过时了).
2.通过setPreviewTexture把内容渲染到SurfaceTexture 上
3.通过.setDisplayOrientation设置角度
这里用到了SetDegree(context)方法

private int SetDegree(MyWindow myWindow) { // 获得手机的方向int rotation = mWindowManager.getDefaultDisplay().getRotation();int degree = 0;// 根据手机的方向计算相机预览画面应该选择的角度switch (rotation) {case Surface.ROTATION_0:degree = 90;break;case Surface.ROTATION_90:degree = 0;break;case Surface.ROTATION_180:degree = 270;break;case Surface.ROTATION_270:degree = 180;break;}return degree;}

4.通过startPreviewd开始渲染


@Overridepublic void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) {if (myCamera == null) {// 创建Camera实例myCamera = Camera.open();try {// 设置预览在textureView上myCamera.setPreviewTexture(surface);myCamera.setDisplayOrientation(SetDegree(MyWindow.this));// 开始预览myCamera.startPreview();} catch (IOException e) {e.printStackTrace();}}}

最后onSurfaceTextureDestroyed方法里进行停止渲染和释放资源操作.

@Overridepublic boolean onSurfaceTextureDestroyed(SurfaceTexture surface) {myCamera.stopPreview(); //停止预览myCamera.release();     // 释放相机资源myCamera = null;return false;}

最后别忘了加上这三个权限:

 <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" /><uses-permission android:name="android.permission.GET_TASKS" /><uses-permission android:name="android.permission.CAMERA" />

三、结束

有兴趣可以自己去丰富内容,比如加拍照的功能等等.最后附上Demo:
点击打开
密码:362r

精彩案例-悬浮在桌面上的照相机相关推荐

  1. 删除桌面上淘宝商城,高清电影,精彩小游戏图标

    症状:最近很多朋友反应,桌面上多了高清电影.精彩小游戏.淘宝商城这三个图标,右击这几个图标,只有打开主页或创建快捷方式的选项,无法删除. 经过测试,解决方法如下: 首先在桌面上右击,依次点击排列图标, ...

  2. java swing 悬浮_[Java教程]JAVA Swing窗口在桌面上浮动_星空网

    JAVA Swing窗口在桌面上浮动 2012-02-15 0 1 class Util { 2 private Toolkit tool; 3 private int width; 4 privat ...

  3. (Mirage系列之四)Mirage经典案例之集中桌面管理

    在前面的博客如何搭建Mirage的环境中我们介绍了如何搭建一套Mirage的环境.从本博客开始,我们来讲VMware Horizon Mirage的经典用户用例及真实案例分析里介绍的第一个Mirage ...

  4. 翻转课堂十大精彩案例

    秦陇纪2010信息数据精简的微博http://weibo.com/INXCN欢迎大家粉我,跟帖,点赞,互粉(100以上才可以申请V,打扰大家了请多包涵).秦陇大田地信息数据方面的思考研究者.程序设计员 ...

  5. surface怎么将计算机放到桌面,笔者帮您win10系统把此电脑和控制面板在桌面上显示的妙计...

    大家在使用电脑工作的时候会遇到win10系统把此电脑和控制面板在桌面上显示的问题,想必很多朋友都遇到过win10系统把此电脑和控制面板在桌面上显示的问题吧.解决win10系统把此电脑和控制面板在桌面上 ...

  6. Systrace 流畅性实战 2 :案例分析: MIUI 桌面滑动卡顿分析

    当我们说 流畅度 的时候,我们说的是什么?不同的人对流畅性(卡顿掉帧)有不同的理解,对卡顿阈值也有不同的感知,所以有必要在开始这个系列文章之前,先把涉及到的内容说清楚,防止出现不同的理解,也方便大家带 ...

  7. YYDatav的数据可视化大屏《精彩案例汇总》(PythonEcharts源码)

    一. 资源下载 [1-10]套Python+Echarts数据可视化大屏案例(共10套)-企业管理文档类资源-CSDN下载第1篇https://yydatav.blog.csdn.net/articl ...

  8. 如何在桌面上显示我的计算机,怎么在桌面显示我的电脑 - 卡饭网

    Win10我的电脑怎么放在桌面上?Win10桌面显示我的电脑方法图解 Win10我的电脑怎么放在桌面上?Win10桌面显示我的电脑方法图解 随着Win10正式发布,如今很多电脑爱好者朋友都下载安装了最 ...

  9. 第五章:iptables应用案例分析(代理服务器上设置iptables)

    一.代理服务器架设的位置如上图 实验环境如下: 1)局域网网段:192.168.1.0/24,该网段内有2台服务器和1台客户端 (1)WEB服务器:192.168.1.3/24 (2)FTP服务器:1 ...

最新文章

  1. 说一说activity
  2. 形象标识 新松机器人_东莞市81个乡村振兴重点项目集中启用,树立统一标识牌302块...
  3. Function in loop and closure
  4. ubuntu13.04下安装jdk7
  5. 期末小作品图片_三年级语文期末满分作文《我想变成什么》,老师:你真懂事...
  6. 基于ffmpeg和libvlc的视频剪辑、播放器
  7. C++获取本机的ip地址程序
  8. python集合应用场景_python 集合的应用
  9. 切记!职场邮件需注意的细节
  10. java栈顶元素_栈在Java类库中的实现
  11. 迁移学习 Transfer Learning—通俗易懂地介绍(常见网络模型pytorch实现)
  12. 别再这么写代码了,这几个方法不香吗?
  13. iBATIS date MySQL_LocalDateTime与mysql日期类型的交互(基于mybatis)
  14. mysql conflict语句_详细解读MySQL事务
  15. HDU2537 8球胜负【水题】
  16. 从键盘输入3个整数,输出其中最大数
  17. ssh配置config文件,实现vscode免密登陆
  18. Unity3D -- 天空盒(图文)
  19. 什么是域名备案?为什么要进行备案?备案后你将会获得下列益处
  20. 堡垒机(运维审计系统)的基本原理与部署方式

热门文章

  1. unity游戏解包和修改代码
  2. AngularJS实战第一章
  3. 李飞飞AI100报告提出14大AI机遇与挑战
  4. 你在网上献的爱心,估值400亿
  5. Linux系统下创建普通用户并更改用户组
  6. Ubuntu 18.04 更换源
  7. html 有条件显示标签,HTML的标签大全
  8. 用VBS操作Excel常见方法总结!
  9. python零基础一
  10. 【MySQL】MySQL复制架构