当前位置: 移动技术网 > 移动技术>移动开发>Android > Android实现系统级悬浮按钮

Android实现系统级悬浮按钮

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

本文实例为大家分享了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轴画一个正方形]

下面是具体实现代码:

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);

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

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

相关文章:

验证码:
移动技术网