当前位置: 移动技术网 > 移动技术>移动开发>Android > Android利用WindowManager生成悬浮按钮及悬浮菜单

Android利用WindowManager生成悬浮按钮及悬浮菜单

2019年07月24日  | 移动技术网移动技术  | 我要评论

简介

本文模仿实现的是360手机卫士基础效果,同时后续会补充一些windowmanager的原理知识。

整体思路

360手机卫士的内存球其实就是一个没有画面的应用程序,整个应用程序的主体是一个service。我们的程序开始以后,启动一个service,同时关闭activity即可:

public class mainactivity extends activity {
  private static final string tag = mainactivity.class.getsimplename();
  @override
  protected void oncreate(bundle savedinstancestate) {
    super.oncreate(savedinstancestate);
    startservice(new intent(this, floatwindowservice.class));
    finish();
  }
}
import android.os.ibinder;
import android.util.log;

import java.util.timer;
import java.util.timertask;

public class floatwindowservice extends service {
  private static final string tag = floatwindowservice.class.getsimplename();

  public floatwindowservice() {
  }
  @override
  public int onstartcommand(intent intent, int flags, int startid) {
    log.d(tag, "on start command");
    floatwindowmanager.instance(getapplicationcontext()).createfloatwindow();
    return super.onstartcommand(intent, flags, startid);
  }

  @override
  public ibinder onbind(intent intent) {
    // todo: return the communication channel to the service.
    throw new unsupportedoperationexception("not yet implemented");
  }

}

我们要注意的是,传统的service默认是运行在ui线程中的,这点与封装了一个thread和handler的intentservice不同,所以我们可以直接在service中更改ui相关的内容。

再来看一下floatwindowmanager中的方法:

  public void createfloatwindow() {
    if (iswindowshowing()) return;
    windowmanager windowmanager = getwindowmanger(context);
    int screenwidth = windowmanager.getdefaultdisplay().getwidth();
    int screenheight = windowmanager.getdefaultdisplay().getheight();
    if (floatlayout == null) {
      floatlayout = new floatlayout(context);
      if (smalllayoutparams == null) {
        smalllayoutparams = new windowmanager.layoutparams();
        smalllayoutparams.type = windowmanager.layoutparams.type_phone;
        smalllayoutparams.format = pixelformat.rgba_8888;
        smalllayoutparams.flags = windowmanager.layoutparams.flag_not_touch_modal
            | windowmanager.layoutparams.flag_not_focusable;
        smalllayoutparams.gravity = gravity.left | gravity.top;
        smalllayoutparams.width = floatlayout.viewwidth;
        smalllayoutparams.height = floatlayout.viewheight;
        smalllayoutparams.x = screenwidth;
        smalllayoutparams.y = screenheight / 2;
      }
    }
    windowmanager.addview(floatlayout,smalllayoutparams);
  }

以及自定义的view:

<?xml version="1.0" encoding="utf-8"?>
<linearlayout xmlns:android="http://schemas.android.com/apk/res/android"
  android:id="@+id/small_layout"
  android:background="@drawable/bg_small"
  android:orientation="vertical" android:layout_width="60dip"
  android:layout_height="25dip">
<textview
  android:layout_width="match_parent"
  android:gravity="center"
  android:text="悬浮窗"
  android:layout_height="match_parent" />
</linearlayout>
public class floatlayout extends linearlayout {
  public static int viewwidth;
  public static int viewheight;
  private windowmanager windowmanager;
  public floatlayout(final context context) {
    super(context);
    windowmanager = (windowmanager) context.getsystemservice(context.window_service);
    layoutinflater.from(context).inflate(r.layout.small_layout, this);
    view view = findviewbyid(r.id.small_layout);
    viewwidth = view.getlayoutparams().width;
    viewheight = view.getlayoutparams().height;
    setontouchlistener(new ontouchlistener() {
      @override
      public boolean ontouch(view v, motionevent event) {
        floatwindowmanager.instance(context).createfloatmenu();
        return true;
      }
    });
  }

}

自定义的view除了加载了一个布局,就是设置了一个touch监听器,用于点击悬浮窗弹出菜单。注意这里要使用 view.getlayoutparams() 来获取视图的宽和高,因为在构造方法中,这个view并没有被measure完成,所以采用view.getheight得到的宽高是0。

创建菜单的方法类似,同样通过windowmanager:

  public void createfloatmenu() {
    if (menulayout != null) return;
    log.d(tag, "create float menu");
    windowmanager windowmanager = getwindowmanger(context);
    if (menulayout == null){
      menulayout = new menulayout(context);
      menulayoutparams = new windowmanager.layoutparams();
      menulayoutparams.type = windowmanager.layoutparams.type_phone;
      menulayoutparams.format = pixelformat.rgba_8888;

    }
    windowmanager.addview(menulayout,menulayoutparams);

  }

自定义的菜单将背景设置成半透明,同时分成上下两部分,上部分点击删除菜单,下部分是一些展示的内容:

<?xml version="1.0" encoding="utf-8"?>
<linearlayout xmlns:android="http://schemas.android.com/apk/res/android"
  android:orientation="vertical" android:layout_width="match_parent"
  android:background="#96000000"
  android:layout_height="match_parent">
<linearlayout
  android:layout_width="match_parent"
  android:id="@+id/trans_part"
  android:orientation="horizontal"
  android:layout_weight="1"
  android:layout_height="0dp"></linearlayout>
  <linearlayout
    android:layout_width="match_parent"
    android:layout_weight="1"
    android:background="@color/colorprimary"
    android:layout_height="0dp">
    <textview
      android:layout_width="match_parent"
      android:text="存放content"
      android:layout_height="match_parent" />

  </linearlayout>
</linearlayout>

public class menulayout extends linearlayout {
  public menulayout(final context context) {
    super(context);
    layoutinflater.from(context).inflate(r.layout.transparent_layout,this);
    view view = findviewbyid(r.id.trans_part);
    view.setonclicklistener(new onclicklistener() {
      @override
      public void onclick(view v) {
        floatwindowmanager.instance(context).removemenulayout();
      }
    });
  }
}

可以看见,实现悬浮窗,其实就是通过windowmanager.addview 时,在layoutparam 的type设置为type_phone,这样你的视图就是系统级视图,可以覆盖在全部程序的最上面。其余的,更多的是自定义view的知识。

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

如对本文有疑问, 点击进行留言回复!!

相关文章:

验证码:
移动技术网