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

Android实现系统级悬浮按钮

程序员文章站 2023-11-12 21:55:34
本文实例为大家分享了android系统级悬浮按钮的具体代码,供大家参考,具体内容如下 具体的需求 1、就是做一个系统级的悬浮按钮,就像iphone 桌面的那个悬浮按...

本文实例为大家分享了android系统级悬浮按钮的具体代码,供大家参考,具体内容如下

具体的需求

1、就是做一个系统级的悬浮按钮,就像iphone 桌面的那个悬浮按钮效果一样,能随意拖动,并且手一放开,悬浮按钮就自动靠边。
2、可以点击并且可以随意拖动。
3、悬浮按钮自动靠边的时候,或者移动到边上的时候,自动隐藏半边。
4、横竖屏切换都兼容

1、就在windowmanager 里面添加view,这个view通过自定义控件来实现。
2、在ontouch里的motionevent.action_move事件里头,通过控制悬浮按钮的具体坐标来实现随意移动。
3、在ontouch里的motionevent.action_up事件里头,来控制悬浮按钮自动靠边,并且自动隐藏半边,不过在这里ontouch和onclick这两个事件是一起触发的,不过这也有解决办法,你可以在手放开的瞬间,通过移动的距离,来决定是否触发点击事件,,如果返回false,就会触发点击事件,如果返回true就会触发点击事件
4、通过自定义控件onlayout方法,来捕获横竖屏切换事件,
5、还有一个靠哪边停靠的问题,通过坐标来判读更靠近哪一边。就靠哪边停靠。
![以中间这个中心点为准,以更短的x轴画一个正方形]

Android实现系统级悬浮按钮

下面是具体实现代码:

import android.content.context;
import android.graphics.canvas;
import android.graphics.point;
import android.graphics.rect;
import android.util.attributeset;
import android.view.motionevent;
import android.view.view;
import android.view.windowmanager;
import android.widget.imageview;

import com.iapppay.openid.channel.loginresultcallback;
import com.iapppay.openid.channel.openidapplication;
import com.iapppay.openid.channel.util.displayutil;
import com.iapppay.openid.channel.util.logutil;
import com.iapppay.openid.channel.util.res;

/**
 * created by huangtiebing 2017/2/14.
 */

public class dragfloatactionbutton extends imageview implements view.ontouchlistener, view.onclicklistener {

  public static string tag = "dragfloatactionbutton";
  private context context;

  float lastx, lasty;
  float originx, originy;
  int screenwidth;
  int screenheight;
  private int originwidth;

  private windowmanager windowmanager;
  //  // 此windowmanagerparams变量为获取的全局变量,用以保存悬浮窗口的属性
  private windowmanager.layoutparams windowmanagerparams;

  private loginresultcallback resultcallback; //悬浮按钮点击回调

  public dragfloatactionbutton(context context, boolean isforcelogin, loginresultcallback resultcallback) {
    this(context, null);
    openidapplication.getinstance().setforcelogin(isforcelogin);
    this.resultcallback = resultcallback;
  }

  public dragfloatactionbutton(context context, attributeset attrs) {
    this(context, attrs, 0);
  }

  public dragfloatactionbutton(context context, attributeset attrs, int defstyleattr) {
    super(context, attrs, defstyleattr);
    this.context = context;

    point screensize = displayutil.getscreensize(context);
    screenwidth = screensize.x;
    screenheight = screensize.y;
    setimageresource(res.drawable(context, "ipay_float_btn_bg"));
    setontouchlistener(this);
    setonclicklistener(this);

    windowmanager = (windowmanager) getcontext().getapplicationcontext().getsystemservice(context.window_service);
  }

  public int getoriginwidth() {
    return originwidth;
  }

  public void setoriginwidth(int originwidth) {
    this.originwidth = originwidth;
  }

  @override
  public boolean ontouch(view v, motionevent event) {
    windowmanagerparams = (windowmanager.layoutparams) this.getlayoutparams();
    //获取到状态栏的高度
    rect frame = new rect();
    getwindowvisibledisplayframe(frame);
    int ea = event.getaction();
    switch (ea) {
      case motionevent.action_down:
        lastx = event.getrawx();// 获取触摸事件触摸位置的原始x坐标
        lasty = event.getrawy();
        originx = lastx;
        originy = lasty;
        break;
      case motionevent.action_move:
        float dx = event.getrawx() - lastx;
        float dy = event.getrawy() - lasty;
        windowmanagerparams.x += dx;
        windowmanagerparams.y += dy;
        logutil.d(tag, "移动距离:dx=" + dx + ",dy=" + dy);
        showallbtn();
        lastx = (int) event.getrawx();
        lasty = (int) event.getrawy();
        break;
      case motionevent.action_up:
        float lastmovedx = math.abs(event.getrawx() - originx);
        float lastmovedy = math.abs(event.getrawy() - originy);
        logutil.d(tag, "松开时,移动距离:lastmovedx=" + lastmovedx + ", lastmovedy=" + lastmovedy);
        if (lastmovedx < 10 && lastmovedy < 10) { //移动距离太小,视为点击,
          return false;
        } else {
          updateviewlayout(event);
          isfirstclick = true;
          return true;
        }
    }
    return false;
  }

  /**
   * 显示整个图标
   */
  public void showallbtn() {
    windowmanagerparams.width = originwidth;
    windowmanagerparams.height = originwidth;
    setimageresource(res.drawable(context, "ipay_float_btn_bg"));
    windowmanager.updateviewlayout(this, windowmanagerparams); // 刷新显示
  }

  /**
   * 悬浮按钮显示在左边
   */
  private void showinleft() {
    windowmanagerparams.x = 0;
    windowmanagerparams.width = originwidth / 2;
    windowmanagerparams.height = originwidth;
    setimageresource(res.drawable(context, "ipay_float_btn_left_hidden"));
    windowmanager.updateviewlayout(this, windowmanagerparams); // 刷新显示
  }

  /**
   * 悬浮按钮显示在右边
   */
  private void showinright() {
    windowmanagerparams.width = originwidth / 2;
    windowmanagerparams.height = originwidth;
    windowmanagerparams.x = screenwidth - windowmanagerparams.width;
    setimageresource(res.drawable(context, "ipay_float_btn_right_hidden"));
    windowmanager.updateviewlayout(this, windowmanagerparams); // 刷新显示
  }

  /**
   * 悬浮按钮显示在上面
   */
  private void showintop() {
    windowmanagerparams.y = 0;
    windowmanagerparams.width = originwidth;
    windowmanagerparams.height = originwidth / 2;
    setimageresource(res.drawable(context, "ipay_float_btn_top_hidden"));
    windowmanager.updateviewlayout(this, windowmanagerparams); // 刷新显示
  }

  /**
   * 悬浮按钮显示在下面
   */
  private void showinbottom() {
    windowmanagerparams.width = originwidth;
    windowmanagerparams.height = originwidth / 2;
    windowmanagerparams.y = screenheight - windowmanagerparams.width;
    setimageresource(res.drawable(context, "ipay_float_btn_bottom_hidden"));
    windowmanager.updateviewlayout(this, windowmanagerparams); // 刷新显示
  }

  /**
   * 更新悬浮图标
   *
   * @param event 手动移动事件
   */
  public void updateviewlayout(motionevent event) {
    point center = new point(screenwidth / 2, screenheight / 2); //屏幕中心点
    float xoffset, yoffset;//以屏幕中心点为原点,x轴和y轴上的偏移量
    if (event != null) {//手动移动的
      xoffset = event.getrawx() - center.x;
      yoffset = event.getrawy() - center.y;
    } else {//自动隐藏
      xoffset = lastx - center.x;
      yoffset = lasty - center.y;
    }
    if (math.abs(xoffset) >= math.abs(yoffset)) {//向左或向右缩进隐藏
      if (xoffset <= 0) { //向左缩进
        showinleft();
      } else {
        showinright();
      }
    } else {//向上或向下缩进隐藏
      if (yoffset <= 0) {//向上缩进
        showintop();
      } else {
        showinbottom();
      }
    }
  }

  @override
  protected void onmeasure(int widthmeasurespec, int heightmeasurespec) {
    super.onmeasure(widthmeasurespec, heightmeasurespec);
  }

  @override
  protected void onlayout(boolean changed, int left, int top, int right, int bottom) {
    super.onlayout(changed, left, top, right, bottom);
    point screensize = displayutil.getscreensize(context);
    if (screenwidth != screensize.x) {//屏幕旋转切换
      screenwidth = screensize.x;
      screenheight = screensize.y;
      lasty = windowmanagerparams.x;
      lastx = windowmanagerparams.y;
      windowmanagerparams.x = (int) lastx;
      windowmanagerparams.y = (int) lasty;
      updateviewlayout(null);
    }
  }

  private boolean isfirstclick = true;

  @override
  protected void ondraw(canvas canvas) {
    super.ondraw(canvas);
  }

  @override
  public void onclick(view v) {
    logutil.d(tag, "执行点击事件");
    if (!isfirstclick) {
      openidapplication.getinstance().floatbtnclick(context, openidapplication.getinstance().isforcelogin(), resultcallback);
    } else {//半隐藏状态,点击显示全部
      isfirstclick = false;
      showallbtn();
    }
  }

}

调用实现代码,这里注意有个问题,弹出系统级的悬浮窗,需要配置权限:

并且android 6.0以上的手机,还要弹出对话框问用户是否运行,如果这个用户拒绝了,就不能弹出系统级的悬浮窗了,还有个别手机厂商修改了android源码,还需要进系统设置里去允许这个应用弹出悬浮窗。这样的话就体验感非常不好,不过这里有个小技巧,按下面方式设置为toast类型就完全解决,既不用配置权限,也不弹出窗来向用户获取权限,完全解决问题。

windowmanager.layoutparams windowmanagerparams = new windowmanager.layoutparams(windowmanager.layoutparams.type_toast, 
windowmanager.layoutparams.flag_not_touch_modal | windowmanager.layoutparams.flag_not_focusable, 
pixelformat.translucent);

具体实现代码如下:

dragfloatactionbutton floatbtn = new dragfloatactionbutton(context, isforcelogin, mresultcallback);

   windowmanager windowmanager = (windowmanager) context.getsystemservice(context.window_service);
   // 设置layoutparams(全局变量)相关参数
   windowmanager.layoutparams windowmanagerparams = new windowmanager.layoutparams(windowmanager.layoutparams.type_toast,
     windowmanager.layoutparams.flag_not_touch_modal | windowmanager.layoutparams.flag_not_focusable,
     pixelformat.translucent);
   /**
    * 注意,flag的值可以为:
    * 下面的flags属性的效果形同“锁定”。
    * 悬浮窗不可触摸,不接受任何事件,同时不影响后面的事件响应。
    * layoutparams.flag_not_touch_modal 不影响后面的事件
    * layoutparams.flag_not_focusable 不可聚焦
    * layoutparams.flag_not_touchable 不可触摸
    */
   // 调整悬浮窗口至左上角,便于调整坐标
   windowmanagerparams.gravity = gravity.left | gravity.top;
   // 以屏幕左上角为原点,设置x、y初始值
   windowmanagerparams.x = 0;
   windowmanagerparams.y = 0;
   // 设置悬浮窗口长宽数据
   floatbtn.measure(0, 0);
   floatbtn.setoriginwidth(floatbtn.getmeasuredwidth() - 50);
   windowmanagerparams.width = floatbtn.getoriginwidth();
   windowmanagerparams.height = windowmanagerparams.width;
   // 显示myfloatview图像
   windowmanager.addview(floatbtn, windowmanagerparams);

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