Android自定义可拖拽的悬浮按钮DragFloatingActionButton
悬浮按钮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" />
如图:
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,希望对大家有所帮助