欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  移动技术

android实现可*移动、监听点击事件的悬浮窗

程序员文章站 2022-05-16 12:09:37
最近因为项目需要,自己实现了个可以*移动,并且长按可以跳出一个控制播放的,大的悬浮窗。 好,开始吧。首先我们先聊权限,悬浮窗需要在manifest中声明一个权限:...

最近因为项目需要,自己实现了个可以*移动,并且长按可以跳出一个控制播放的,大的悬浮窗。

好,开始吧。首先我们先聊权限,悬浮窗需要在manifest中声明一个权限:

<uses-permission android:name="android.permission.system_alert_window" />

然后呢,嗯,我们来讲讲关于悬浮窗实现的原理。

在andriod中,所有的界面元素都要通过windowmanger来实现,像activity、fragment等等这些也是在其上实现。因此,我们的悬浮窗自然要通过这个实现。

这个项目中,我们自定义了两个悬浮窗view。我们以其中一个比较简单的为例:

我们自定义一个管理可以统一管理悬浮窗的类mywindowmanager,负责创建,删除悬浮窗

/**
 * created by shiwe on 2017/3/7.
 * 悬浮窗管理
 * 创建,移除
 * 单例模式
 */

public class mywindowmanager {

 private floatnormalview normalview;
 private floatcontrolview controlview;

 private static mywindowmanager instance;

 private mywindowmanager() {
 }

 public static mywindowmanager getinstance() {
  if (instance == null)
   instance = new mywindowmanager();
  return instance;
 }


 /**
  * 创建小型悬浮窗
  */
 public void createnormalview(context context) {
  if (normalview == null)
   normalview = new floatnormalview(context);
 }

 /**
  * 移除悬浮窗
  *
  * @param context
  */
 public void removenormalview(context context) {
  if (normalview != null) {
   windowmanager windowmanager = (windowmanager) context.getsystemservice(context.window_service);
   windowmanager.removeview(normalview);
   normalview = null;
  }
 }

 /**
  * 创建小型悬浮窗
  */
 public void createcontrolview(context context) {
  if (controlview == null)
   controlview = new floatcontrolview(context);
 }

 /**
  * 移除悬浮窗
  *
  * @param context
  */
 public void removecontrolview(context context) {
  if (controlview != null) {
   windowmanager windowmanager = (windowmanager) context.getsystemservice(context.window_service);
   windowmanager.removeview(controlview);
   controlview = null;
  }
 }
}

然后看看我们自定义的一个view,其继承自linearlayout,我们在initlayoutparams初始化这个控件的位置等其他参数;在initevent方法中定义随手指移动的监听事件以及长按的监听事件。

public class floatnormalview extends linearlayout {

 private context context = null;
 private view view = null;
 private imageview ivshowcontrolview = null;
 private windowmanager.layoutparams lp = new windowmanager.layoutparams();
 private static windowmanager windowmanager;

 private float mtouchstartx;
 private float mtouchstarty;
 private float x;
 private float y;
 private boolean initviewplace = false;
 private mywindowmanager mywindowmanager;
 private boolean iscontrolviewshowing = false;

 public floatnormalview(context context) {
  super(context);
  this.context = context;
  mywindowmanager = mywindowmanager.getinstance();
  layoutinflater.from(context).inflate(r.layout.float_normal_view, this);
  view = findviewbyid(r.id.ll_float_normal);
  ivshowcontrolview = (imageview) findviewbyid(r.id.iv_show_control_view);
  windowmanager = (windowmanager) context.getsystemservice(context.window_service);
  initlayoutparams();
  initevent();
 }


 /**
  * 初始化参数
  */
 private void initlayoutparams() {
  //屏幕宽高
  int screenwidth = windowmanager.getdefaultdisplay().getwidth();
  int screenheight = windowmanager.getdefaultdisplay().getheight();

  //总是出现在应用程序窗口之上。
  lp.type = windowmanager.layoutparams.type_phone;

  // flag_not_touch_modal不阻塞事件传递到后面的窗口
  // flag_not_focusable 悬浮窗口较小时,后面的应用图标由不可长按变为可长按,不设置这个flag的话,home页的划屏会有问题
  lp.flags = windowmanager.layoutparams.flag_not_focusable
    | windowmanager.layoutparams.flag_not_touch_modal;

  //悬浮窗默认显示的位置
  lp.gravity = gravity.start | gravity.top;
  //指定位置
  lp.x = screenwidth - view.getlayoutparams().width * 2;
  lp.y = screenheight / 2 + view.getlayoutparams().height * 2;
  //悬浮窗的宽高
  lp.width = windowmanager.layoutparams.wrap_content;
  lp.height = windowmanager.layoutparams.wrap_content;
  lp.format = pixelformat.transparent;
  windowmanager.addview(this, lp);
 }

 /**
  * 设置悬浮窗监听事件
  */
 private void initevent() {
  ivshowcontrolview.setonlongclicklistener(new onlongclicklistener() {
   @override
   public boolean onlongclick(view view) {
    if (!iscontrolviewshowing) {
     mywindowmanager.createcontrolview(context);
     iscontrolviewshowing = true;
    } else {
     mywindowmanager.removecontrolview(context);
     iscontrolviewshowing = false;
    }
    return true;
   }
  });
  view.setontouchlistener(new ontouchlistener() {
   @override
   public boolean ontouch(view v, motionevent event) {
    switch (event.getaction()) {
     case motionevent.action_down:
      if (!initviewplace) {
       initviewplace = true;
       //获取初始位置
       mtouchstartx += (event.getrawx() - lp.x);
       mtouchstarty += (event.getrawy() - lp.y);
      } else {
       //根据上次手指离开的位置与此次点击的位置进行初始位置微调
       mtouchstartx += (event.getrawx() - x);
       mtouchstarty += (event.getrawy() - y);
      }
      break;
     case motionevent.action_move:
      // 获取相对屏幕的坐标,以屏幕左上角为原点
      x = event.getrawx();
      y = event.getrawy();
      updateviewposition();
      break;

     case motionevent.action_up:
      break;
    }
    return true;
   }
  });
 }

 /**
  * 更新浮动窗口位置
  */
 private void updateviewposition() {
  lp.x = (int) (x - mtouchstartx);
  lp.y = (int) (y - mtouchstarty);
  windowmanager.updateviewlayout(this, lp);
 }

最后,只需要在activity中调用mywindowmanager中调用createxxx方法就可以。

public class mainactivity extends appcompatactivity {

 mywindowmanager mywindowmanager;

 @override
 protected void oncreate(@nullable bundle savedinstancestate) {
  super.oncreate(savedinstancestate);
  setcontentview(r.layout.activity_main);
  mywindowmanager = mywindowmanager.getinstance();
  mywindowmanager.createnormalview(this.getapplicationcontext());
 }
}

最后,附上demo项目的下载地址: android实现悬浮窗

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。