package com.tejia.lijin.app.util;
|
|
import android.animation.ObjectAnimator;
|
import android.annotation.SuppressLint;
|
import android.content.Context;
|
import android.util.AttributeSet;
|
import android.view.MotionEvent;
|
import android.view.ViewGroup;
|
import android.view.animation.DecelerateInterpolator;
|
import android.widget.ImageView;
|
|
import com.tejia.lijin.app.callBack.DragFloatActionInterface;
|
|
/**
|
* 可拖拽的悬浮控件按钮
|
* 直接xml布局里引用即可。
|
* 要设置setOnClickListener点击事件,即可实现拖拽和点击功能。
|
* 尺寸大小,样式及背景图片遵循ImageView即可。
|
* <p>
|
* 手指按下
|
* 首先是处理手指按压下的事件,这里我们把拖拽标识符设置为false并记录当前点击的屏幕坐标。然后我们在移动事件处。
|
* 手指移动
|
* 这里我们把拖拽标识符设置为true,因为手指移动了。然后我们需要计算手指移动了多少偏移量
|
* //计算手指移动了多少
|
* int dx=rawX-lastX;
|
* int dy=rawY-lastY;
|
* <p>
|
* 而后的两行代码表示控件需要移动的具体距离,后面有一个简单的边缘检测计算。最终通过调用setX以及setY方法实现控件的移动。
|
* 手指松开
|
* 这里如果是拖拽动作我们才需要处理自己的逻辑否则直接跳过即可。在这里我们首先恢复了按钮的按压效果,在源代码中找到setPressed(boolean)方法,
|
* 这是处理按钮点击效果用的,在这里当手指松开后我们需要恢复按钮原来的效果。然后在判断控件需要往哪边吸附,吸附的过程就是做属性动画而已,原理还是不断的改变setX方法让按钮靠边移动
|
* <p>
|
* 作者:Ggx的代码之旅
|
* 链接:https://www.jianshu.com/p/4f55bcbc1b83
|
* 来源:简书
|
* 简书著作权归作者所有,任何形式的转载都请联系作者获得授权并注明出处。
|
*/
|
@SuppressLint("AppCompatCustomView")
|
public class DragFloatActionButton extends ImageView {
|
private int parentHeigh;//起始高度
|
private int parentWidth;//起始宽度
|
private int lastX;//最后x位置
|
private int lastY;//最后y位置
|
|
private boolean isDrag;//是否拖拽
|
|
public DragFloatActionButton(Context context) {
|
super(context);
|
try {
|
setisStateReturn((DragFloatActionInterface) context);
|
} catch (ClassCastException e) {
|
|
}
|
}
|
|
public DragFloatActionButton(Context context, AttributeSet attrs) {
|
super(context, attrs);
|
try {
|
setisStateReturn((DragFloatActionInterface) context);
|
} catch (ClassCastException e) {
|
|
}
|
}
|
|
public DragFloatActionButton(Context context, AttributeSet attrs, int defStyleAttr) {
|
super(context, attrs, defStyleAttr);
|
try {//传入 回调接口
|
setisStateReturn((DragFloatActionInterface) context);
|
} catch (ClassCastException e) {
|
|
}
|
}
|
|
@Override
|
public boolean onTouchEvent(MotionEvent event) {
|
int rawX = (int) event.getRawX();//相对屏幕的X位置
|
int rawY = (int) event.getRawY();//相对屏幕的Y位置
|
switch (event.getAction() & MotionEvent.ACTION_MASK) {
|
case MotionEvent.ACTION_DOWN://按下
|
//设置按压效果
|
setPressed(true);
|
isDrag = false;//未移动
|
getParent().requestDisallowInterceptTouchEvent(true);
|
lastX = rawX;
|
lastY = rawY;
|
ViewGroup parent;
|
if (getParent() != null) {
|
parent = (ViewGroup) getParent();
|
parentHeigh = parent.getHeight();
|
parentWidth = parent.getWidth();
|
}
|
break;
|
case MotionEvent.ACTION_MOVE://移动
|
if (parentHeigh <= 0 || parentWidth == 0) {
|
isDrag = false;
|
break;
|
} else {
|
isDrag = true;//移动
|
}
|
//计算手指移动了多少
|
int dx = rawX - lastX;
|
int dy = rawY - lastY;
|
//这里修复一些华为手机无法触发点击事件
|
int distance = (int) Math.sqrt(dx * dx + dy * dy);
|
if (distance == 0) {
|
isDrag = false;
|
break;
|
}
|
//getX()和getY()获得的永远是view的触摸位置坐标
|
float x = getX() + dx;
|
float y = getY() + dy;
|
setdelete(x, y);
|
//检测是否到达边缘 左上右下
|
x = x < 0 ? 0 : x > parentWidth - getWidth() ? parentWidth - getWidth() : x;
|
y = getY() < 0 ? 0 : y + getHeight() > parentHeigh ? parentHeigh - getHeight() : y;
|
// 当移动位置大于了 边缘位置 则不计算位置
|
if (x > parentWidth - getWidth() || (y + getHeight()) > parentHeigh || ((y + getHeight()) - getHeight()) <= 0) {
|
//((y + getHeight()) - getHeight()) 去掉标题栏的高度 去掉移动状态
|
if (((y + getHeight()) - getHeight()) <= 0 && isDrag) {
|
setX(x);
|
setY(0);//重新设置 图片的高度为0
|
lastX = rawX;
|
lastY = rawY;
|
isDrag = false;
|
}
|
|
} else {
|
setX(x);
|
setY(y);
|
lastX = rawX;
|
lastY = rawY;
|
}
|
// Log.e("eee", "isDrag=" + isDrag + "getX=" + getX() + ";getY=" + getY() + ";parentWidth=" + parentWidth);
|
if (aReturn != null) {
|
aReturn.isDrag(true, true);
|
}
|
break;
|
case MotionEvent.ACTION_CANCEL://事件被上层拦截 时触发。
|
// Log.e("eee", "onTouchEvent: CANCEL");
|
ACTION_UP(rawX);
|
break;
|
case MotionEvent.ACTION_POINTER_UP:
|
// Log.e("eee", "onTouchEvent: ACTION_POINTER_UP");
|
break;
|
case MotionEvent.ACTION_UP://放开
|
// Log.e("eee", "onTouchEvent: 放开");
|
ACTION_UP(rawX);
|
break;
|
}
|
//如果是拖拽则消s耗事件,否则正常传递即可。
|
return !isNotDrag() || super.onTouchEvent(event);
|
}
|
|
/**
|
* 手指放开
|
*/
|
private void ACTION_UP(int rawX) {
|
if (!isNotDrag()) {
|
//恢复按压效果
|
setPressed(false);
|
//Log.i("getX="+getX()+";screenWidthHalf="+screenWidthHalf);
|
if (rawX >= parentWidth / 2) {
|
//靠右吸附
|
animate().setInterpolator(new DecelerateInterpolator())
|
.setDuration(100)
|
.xBy(parentWidth - getWidth() - getX())
|
.start();
|
} else {
|
//靠左吸附
|
ObjectAnimator oa = ObjectAnimator.ofFloat(this, "x", getX(), 0);
|
oa.setInterpolator(new DecelerateInterpolator());
|
oa.setDuration(100);
|
oa.start();
|
}
|
}
|
//传入 没有移动状态 并且判断是否移动到指定区域(移动到指定区域不隐藏,否则隐藏)
|
if (aReturn != null) {
|
//未移动到指定区域
|
if (!isImgWidthboolean && !isImgHeighboolean) {
|
aReturn.isDrag(false, false);
|
} else if (isImgWidthboolean && isImgHeighboolean) {
|
//移动到指定区域
|
aReturn.isDrag(false, true);
|
} else {
|
aReturn.isDrag(false, false);
|
}
|
//传入 是否移动到区域(宽高 距离判断)
|
aReturn.isReturn(isImgWidthboolean, isImgHeighboolean);
|
}
|
}
|
|
private Boolean isNotDrag() {
|
return !isDrag && (getX() == 0 || (getX() == parentWidth - getWidth()));
|
}
|
|
/**
|
* 主界面传入进来 指定区域控件的宽高
|
*
|
* @param imgwidth
|
* @param imgHeigh
|
*/
|
public void setWidthHeight(int imgwidth, int imgHeigh) {
|
isImgWidth = imgwidth;
|
isImgHeigh = imgHeigh;
|
}
|
|
/**
|
* 主界面 右下角 删除控件的宽高
|
*/
|
private int isImgWidth;//
|
private int isImgHeigh;
|
/**
|
* 移动到指定位置(宽高距离判断)
|
*/
|
private boolean isImgWidthboolean;
|
private boolean isImgHeighboolean;
|
|
/**
|
* 删除 拖动控件
|
*/
|
private void setdelete(float x, float y) {
|
//宽度大于 右下角控件的宽度
|
if (x > (parentWidth - getWidth() - isImgWidth)) {
|
isImgWidthboolean = true;
|
} else {
|
isImgWidthboolean = false;
|
}
|
//宽度大于 右下角控件的宽度
|
if ((y + getHeight()) > (parentHeigh - isImgHeigh)) {
|
isImgHeighboolean = true;
|
} else {
|
isImgHeighboolean = false;
|
}
|
}
|
|
private DragFloatActionInterface aReturn;
|
|
/**
|
* 接口绑定
|
*
|
* @param stateReturn 拖动控件 位置(是否进入指定区域), 判断移动状态
|
*/
|
private void setisStateReturn(DragFloatActionInterface stateReturn) {
|
aReturn = stateReturn;
|
}
|
|
}
|