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

Android自定义可拖拽的悬浮按钮DragFloatingActionButton

程序员文章站 2022-10-10 12:02:52
悬浮按钮floatingactionbutton是android 5.0系统添加的新控件,floatingactionbutton是继承至imageview,所以float...

悬浮按钮floatingactionbutton是android 5.0系统添加的新控件,floatingactionbutton是继承至imageview,所以floatingactionbutton拥有imageview的所有属性。本文讲解的是一个实现了可拖拽的悬浮按钮,并为此添加了类似于qq的吸附边框的功能。在此之前,先了解下其简单的使用方式吧:

首先你得添加其依赖

compile 'com.android.support:design:25.3.1'

然后在布局文件中使用。

<android.support.design.widget.floatingactionbutton
   android:layout_width="wrap_content"
   android:layout_height="wrap_content"
   android:layout_gravity="right|bottom"
   android:src="@drawable/ic_launcher"
   />

如图:

Android自定义可拖拽的悬浮按钮DragFloatingActionButton 

floatingactionbutton正常显示的情况下有个填充的颜色,有个阴影;点击的时候会有一个ripplecolor,并且阴影的范围可以增大。其中:

1、填充的颜色默认使用就是style当中的coloraccent。

2、ripplecolor默认取的是theme当中的colorcontrolhighlight。

3、elevation和pressedtranslationz,前者用户设置正常显示的阴影大小;后者是点击时显示的阴影大小。

好了,现在介绍本文的重点:可拖拽的,有吸附功能的悬浮按钮

先上代码。

import android.animation.objectanimator;
import android.content.context;
import android.support.design.widget.floatingactionbutton;
import android.util.attributeset;
import android.util.log;
import android.view.motionevent;
import android.view.animation.decelerateinterpolator;
public class dragfloatactionbutton extends floatingactionbutton {
 private int screenwidth;
 private int screenheight;
 private int screenwidthhalf;
 private int statusheight;
 private int virtualheight;
 public dragfloatactionbutton(context context) {
  super(context);
  init();
 }
 public dragfloatactionbutton(context context, attributeset attrs) {
  super(context, attrs);
  init();
 }
 public dragfloatactionbutton(context context, attributeset attrs, int defstyleattr) {
  super(context, attrs, defstyleattr);
  init();
 }
 private void init() {
  screenwidth = screenutils.getscreenwidth(getcontext());
  screenwidthhalf = screenwidth / 2;
  screenheight = screenutils.getscreenheight(getcontext());
  statusheight = screenutils.getstatusheight(getcontext());
  virtualheight=screenutils.getvirtualbarheigh(getcontext());
 }
 private int lastx;
 private int lasty;
 private boolean isdrag;
 @override
 public boolean ontouchevent(motionevent event) {
  int rawx = (int) event.getrawx();
  int rawy = (int) event.getrawy();
  switch (event.getaction() & motionevent.action_mask) {
   case motionevent.action_down:
    isdrag = false;
    getparent().requestdisallowintercepttouchevent(true);
    lastx = rawx;
    lasty = rawy;
    log.e("down---->", "getx=" + getx() + ";screenwidthhalf=" + screenwidthhalf);
    break;
   case motionevent.action_move:
    isdrag = true;
    //计算手指移动了多少
    int dx = rawx - lastx;
    int dy = rawy - lasty;
    //这里修复一些手机无法触发点击事件的问题
    int distance= (int) math.sqrt(dx*dx+dy*dy);
    log.e("distance---->",distance+"");
    if(distance<3){//给个容错范围,不然有部分手机还是无法点击
     isdrag=false;
     break;
    }
    float x = getx() + dx;
    float y = gety() + dy;
    //检测是否到达边缘 左上右下
    x = x < 0 ? 0 : x > screenwidth - getwidth() ? screenwidth - getwidth() : x;
    // y = y < statusheight ? statusheight : (y + getheight() >= screenheight ? screenheight - getheight() : y);
    if (y<0){
     y=0;
    }
    if (y>screenheight-statusheight-getheight()){
     y=screenheight-statusheight-getheight();
    }
    setx(x);
    sety(y);
    lastx = rawx;
    lasty = rawy;
    log.e("move---->", "getx=" + getx() + ";screenwidthhalf=" + screenwidthhalf + " " + isdrag+" statusheight="+statusheight+ " virtualheight"+virtualheight+ " screenheight"+ screenheight+" getheight="+getheight()+" y"+y);
    break;
   case motionevent.action_up:
    if (isdrag) {
     //恢复按压效果
     setpressed(false);
     log.e("action_up---->", "getx=" + getx() + ";screenwidthhalf=" + screenwidthhalf);
     if (rawx >= screenwidthhalf) {
      animate().setinterpolator(new decelerateinterpolator())
        .setduration(500)
        .xby(screenwidth - getwidth() - getx())
        .start();
     } else {
      objectanimator oa = objectanimator.offloat(this, "x", getx(), 0);
      oa.setinterpolator(new decelerateinterpolator());
      oa.setduration(500);
      oa.start();
     }
    }
    log.e("up---->",isdrag+"");
    break;
  }
  //如果是拖拽则消耗事件,否则正常传递即可。
  return isdrag || super.ontouchevent(event);
 }
}

screenutils.java

package com.example.cmos.retrofitdemo;
import android.app.activity;
import android.content.context;
import android.graphics.rect;
import android.util.displaymetrics;
import android.view.display;
import android.view.window;
import android.view.windowmanager;
import java.lang.reflect.method;
/**
 * created by gongwq on 2017/6/14 0014.
 */
public class screenutils {
 private screenutils() {
  /* cannot be instantiated */
  throw new unsupportedoperationexception("cannot be instantiated");
 }
 /**
  * 获得屏幕高度
  *
  * @param context
  * @return
  */
 public static int getscreenwidth(context context) {
  windowmanager wm = (windowmanager) context
    .getsystemservice(context.window_service);
  displaymetrics outmetrics = new displaymetrics();
  wm.getdefaultdisplay().getmetrics(outmetrics);
  return outmetrics.widthpixels;
 }
 /**
  * 获得屏幕宽度
  *
  * @param context
  * @return
  */
 public static int getscreenheight(context context) {
  windowmanager wm = (windowmanager) context
    .getsystemservice(context.window_service);
  displaymetrics outmetrics = new displaymetrics();
  wm.getdefaultdisplay().getmetrics(outmetrics);
  return outmetrics.heightpixels;
 }
 /**
  * 获得状态栏的高度
  *
  * @param context
  * @return
  */
 public static int getstatusheight(context context) {
  int statusheight = -1;
  try {
   class<?> clazz = class.forname("com.android.internal.r$dimen");
   object object = clazz.newinstance();
   int height = integer.parseint(clazz.getfield("status_bar_height")
     .get(object).tostring());
   statusheight = context.getresources().getdimensionpixelsize(height);
  } catch (exception e) {
   e.printstacktrace();
  }
  return statusheight;
 }
 /**
  * 获取虚拟功能键高度
  */
 public static int getvirtualbarheigh(context context) {
  int vh = 0;
  windowmanager windowmanager = (windowmanager) context.getsystemservice(context.window_service);
  display display = windowmanager.getdefaultdisplay();
  displaymetrics dm = new displaymetrics();
  try {
   @suppresswarnings("rawtypes")
   class c = class.forname("android.view.display");
   @suppresswarnings("unchecked")
   method method = c.getmethod("getrealmetrics", displaymetrics.class);
   method.invoke(display, dm);
   vh = dm.heightpixels - windowmanager.getdefaultdisplay().getheight();
  } catch (exception e) {
   e.printstacktrace();
  }
  return vh;
 }
 public static int getvirtualbarheigh(activity activity) {
  int titleheight = 0;
  rect frame = new rect();
  activity.getwindow().getdecorview().getwindowvisibledisplayframe(frame);
  int statusheight = frame.top;
  titleheight = activity.getwindow().findviewbyid(window.id_android_content).gettop() - statusheight;
  return titleheight;
 }
}

上面的代码也很简单,相信看代码中的注释就可以看的明白了。但是这里还是讲下其实现原理:这个自定义的悬浮按钮,我们主要是重写了其ontouch事件,捕捉触摸事件,然后利用setx(),sety()方法将其移动。而吸附效果,主要是利用的属性动画,最后,不要忘了return 是否还在拖拽的结果,免得无法触发点击事件。

ps

最后贴一个弹出框。推荐用popmenu,相比于popwindow,这个会自动调整显示的位置,这在拖拽的悬浮按钮中很有用,因为如果用后者,你将按钮移到屏幕上方,而当你的弹出框也是设置在显示的悬浮按钮的上方,那么就有可能会遮挡弹出框的内容。

dragfloatactionbutton= (dragfloatactionbutton) findviewbyid(r.id.floatbtn);
  dragfloatactionbutton.setonclicklistener(this);
....
 @override
 public void onclick(view view) {
  switch (view.getid()) {
   case r.id.floatbtn:
    popupmenu popupmenu=new popupmenu(this,view);
    getmenuinflater().inflate(r.menu.pop_item,popupmenu.getmenu());
    popupmenu.setonmenuitemclicklistener(new popupmenu.onmenuitemclicklistener() {
     @override
     public boolean onmenuitemclick(menuitem menuitem) {
      switch (menuitem.getitemid()){
       case r.id.action_last:
        toast.maketext(testactivity.this,""+menuitem.getitemid(),toast.length_short).show();
        break;
       case r.id.action_next:
        toast.maketext(testactivity.this,""+menuitem.getitemid(),toast.length_short).show();
        break;
      }
      return false;
     }
    });
    popupmenu.show();
    log.e("****--->","float");
    // toast.maketext(this,"flaot---",toast.length_short).show();
    break;
  }
 }

     新建menu文件夹,在里面添加pop_item.xml文件

<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
 xmlns:app="http://schemas.android.com/apk/res-auto">
 <item
  android:id="@+id/action_delete"
  android:orderincategory="100"
  android:title="删除"
  app:showasaction="never" />
 <item
  android:id="@+id/action_save"
  android:orderincategory="200"
  android:title="保存"
  app:showasaction="never" />
 <item
  android:id="@+id/action_last"
  android:orderincategory="300"
  android:title="上一步"
  app:showasaction="never" />
 <item
  android:id="@+id/action_next"
  android:icon="@null"
  android:orderincategory="400"
  android:title="下一步"
  app:showasaction="never" />
</menu>

以上所述是小编给大家介绍的android自定义可拖拽的悬浮按钮dragfloatingactionbutton,希望对大家有所帮助