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

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

程序员文章站 2023-12-22 15:58:28
简介 本文模仿实现的是360手机卫士基础效果,同时后续会补充一些windowmanager的原理知识。 整体思路 360手机卫士的内存球其实就是一个没有画面...

简介

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

Android利用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的知识。

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

上一篇:

下一篇: