修改可以使用
This commit is contained in:
@@ -13,21 +13,27 @@ import com.blankj.utilcode.util.ScreenUtils;
|
||||
|
||||
/**
|
||||
* 可拖拽悬浮
|
||||
* 稳定版悬浮窗控件 - 支持点击/拖动分离、适配横竖屏、自动吸附边缘
|
||||
*/
|
||||
public class FloatingMagnetView extends FrameLayout {
|
||||
|
||||
public static final int MARGIN_EDGE = 0;
|
||||
private float mOriginalRawX;
|
||||
private float mOriginalRawY;
|
||||
private float mOriginalX;
|
||||
private float mOriginalY;
|
||||
private static final int TOUCH_TIME_THRESHOLD = 150;
|
||||
private float mOriginalX, mOriginalY; // 当前 View 的坐标
|
||||
private float mOriginalRawX, mOriginalRawY; // 触摸点的原始屏幕坐标
|
||||
private long mLastTouchDownTime;
|
||||
protected MoveAnimator mMoveAnimator;
|
||||
protected int mScreenWidth;
|
||||
private boolean isDragging = false;
|
||||
private boolean isNearestLeft = true;
|
||||
|
||||
private int mScreenWidth;
|
||||
private int mScreenHeight;
|
||||
private int mStatusBarHeight;
|
||||
private boolean isNearestLeft = true;
|
||||
|
||||
private MoveAnimator mMoveAnimator;
|
||||
|
||||
private OnFloatingClickListener listener;
|
||||
|
||||
private static final int TOUCH_TIME_THRESHOLD = 150;
|
||||
private static final float CLICK_DRAG_THRESHOLD = 10;
|
||||
|
||||
public FloatingMagnetView(Context context) {
|
||||
this(context, null);
|
||||
@@ -50,58 +56,89 @@ public class FloatingMagnetView extends FrameLayout {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onTouchEvent(MotionEvent event) {
|
||||
if (event == null) {
|
||||
return false;
|
||||
protected void onConfigurationChanged(Configuration newConfig) {
|
||||
super.onConfigurationChanged(newConfig);
|
||||
updateSize();
|
||||
moveToEdge(isNearestLeft);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onInterceptTouchEvent(MotionEvent ev) {
|
||||
switch (ev.getAction()) {
|
||||
case MotionEvent.ACTION_DOWN:
|
||||
return false; // 不拦截,让子控件优先处理点击
|
||||
|
||||
case MotionEvent.ACTION_MOVE:
|
||||
float dx = Math.abs(ev.getX() - (mOriginalRawX - mOriginalX));
|
||||
float dy = Math.abs(ev.getY() - (mOriginalRawY - mOriginalY));
|
||||
if (dx > CLICK_DRAG_THRESHOLD || dy > CLICK_DRAG_THRESHOLD) {
|
||||
isDragging = true;
|
||||
}
|
||||
return isDragging;
|
||||
|
||||
case MotionEvent.ACTION_UP:
|
||||
case MotionEvent.ACTION_CANCEL:
|
||||
isDragging = false;
|
||||
return false;
|
||||
|
||||
default:
|
||||
return super.onInterceptTouchEvent(ev);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onTouchEvent(MotionEvent event) {
|
||||
if (event == null) return false;
|
||||
|
||||
switch (event.getAction()) {
|
||||
case MotionEvent.ACTION_DOWN:
|
||||
changeOriginalTouchParams(event);
|
||||
updateSize();
|
||||
mOriginalX = getX();
|
||||
mOriginalY = getY();
|
||||
mOriginalRawX = event.getRawX();
|
||||
mOriginalRawY = event.getRawY();
|
||||
mLastTouchDownTime = System.currentTimeMillis();
|
||||
mMoveAnimator.stop();
|
||||
isDragging = false;
|
||||
break;
|
||||
|
||||
case MotionEvent.ACTION_MOVE:
|
||||
updateViewPosition(event);
|
||||
if (isDragging) {
|
||||
float newX = mOriginalX + (event.getRawX() - mOriginalRawX);
|
||||
float newY = mOriginalY + (event.getRawY() - mOriginalRawY);
|
||||
|
||||
// 限制 Y 轴边界
|
||||
if (newY < mStatusBarHeight) newY = mStatusBarHeight;
|
||||
if (newY > mScreenHeight - getHeight()) newY = mScreenHeight - getHeight();
|
||||
|
||||
setX(newX);
|
||||
setY(newY);
|
||||
}
|
||||
break;
|
||||
|
||||
case MotionEvent.ACTION_UP:
|
||||
moveToEdge();
|
||||
if (isOnClickEvent()) {
|
||||
if (listener != null) {
|
||||
listener.onClick();
|
||||
}
|
||||
} else {
|
||||
moveToEdge();
|
||||
}
|
||||
isDragging = false;
|
||||
break;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
protected boolean isOnClickEvent() {
|
||||
return System.currentTimeMillis() - mLastTouchDownTime < TOUCH_TIME_THRESHOLD;
|
||||
private boolean isOnClickEvent() {
|
||||
long time = System.currentTimeMillis() - mLastTouchDownTime;
|
||||
float dx = Math.abs(getX() - mOriginalX);
|
||||
float dy = Math.abs(getY() - mOriginalY);
|
||||
return time < TOUCH_TIME_THRESHOLD && dx < CLICK_DRAG_THRESHOLD && dy < CLICK_DRAG_THRESHOLD;
|
||||
}
|
||||
|
||||
private void updateViewPosition(MotionEvent event) {
|
||||
setX(mOriginalX + event.getRawX() - mOriginalRawX);
|
||||
// 限制不可超出屏幕高度
|
||||
float desY = mOriginalY + event.getRawY() - mOriginalRawY;
|
||||
if (desY < mStatusBarHeight) {
|
||||
desY = mStatusBarHeight;
|
||||
}
|
||||
if (desY > mScreenHeight - getHeight()) {
|
||||
desY = mScreenHeight - getHeight();
|
||||
}
|
||||
setY(desY);
|
||||
}
|
||||
|
||||
private void changeOriginalTouchParams(MotionEvent event) {
|
||||
mOriginalX = getX();
|
||||
mOriginalY = getY();
|
||||
mOriginalRawX = event.getRawX();
|
||||
mOriginalRawY = event.getRawY();
|
||||
mLastTouchDownTime = System.currentTimeMillis();
|
||||
}
|
||||
|
||||
protected void updateSize() {
|
||||
mScreenWidth = (ScreenUtils.getScreenWidth() - this.getWidth());
|
||||
private void updateSize() {
|
||||
mScreenWidth = ScreenUtils.getScreenWidth() - getWidth();
|
||||
mScreenHeight = ScreenUtils.getScreenHeight();
|
||||
}
|
||||
|
||||
@@ -110,69 +147,55 @@ public class FloatingMagnetView extends FrameLayout {
|
||||
}
|
||||
|
||||
public void moveToEdge(boolean isLeft) {
|
||||
float moveDistance = isLeft ? MARGIN_EDGE : mScreenWidth - MARGIN_EDGE;
|
||||
mMoveAnimator.start(moveDistance, getY());
|
||||
float moveX = isLeft ? MARGIN_EDGE : mScreenWidth - MARGIN_EDGE;
|
||||
mMoveAnimator.start(moveX, getY());
|
||||
isNearestLeft = isLeft;
|
||||
}
|
||||
|
||||
protected boolean isNearestLeft() {
|
||||
int middle = mScreenWidth / 2;
|
||||
isNearestLeft = getX() < middle;
|
||||
return isNearestLeft;
|
||||
return getX() < middle;
|
||||
}
|
||||
|
||||
|
||||
protected class MoveAnimator implements Runnable {
|
||||
|
||||
private Handler handler = new Handler(Looper.getMainLooper());
|
||||
private float destinationX;
|
||||
private float destinationY;
|
||||
private long startingTime;
|
||||
private float destinationX, destinationY;
|
||||
private long startTime;
|
||||
|
||||
void start(float x, float y) {
|
||||
this.destinationX = x;
|
||||
this.destinationY = y;
|
||||
startingTime = System.currentTimeMillis();
|
||||
this.startTime = System.currentTimeMillis();
|
||||
handler.post(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
if (getRootView() == null || getRootView().getParent() == null) {
|
||||
return;
|
||||
}
|
||||
float progress = Math.min(1, (System.currentTimeMillis() - startingTime) / 400f);
|
||||
if (getRootView() == null || getRootView().getParent() == null) return;
|
||||
|
||||
float progress = Math.min(1f, (System.currentTimeMillis() - startTime) / 400f);
|
||||
float deltaX = (destinationX - getX()) * progress;
|
||||
float deltaY = (destinationY - getY()) * progress;
|
||||
move(deltaX, deltaY);
|
||||
if (progress < 1) {
|
||||
|
||||
setX(getX() + deltaX);
|
||||
setY(getY() + deltaY);
|
||||
|
||||
if (progress < 1f) {
|
||||
handler.post(this);
|
||||
}
|
||||
}
|
||||
|
||||
private void stop() {
|
||||
void stop() {
|
||||
handler.removeCallbacks(this);
|
||||
}
|
||||
}
|
||||
|
||||
private void move(float deltaX, float deltaY) {
|
||||
setX(getX() + deltaX);
|
||||
setY(getY() + deltaY);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onConfigurationChanged(Configuration newConfig) {
|
||||
super.onConfigurationChanged(newConfig);
|
||||
updateSize();
|
||||
moveToEdge(isNearestLeft);
|
||||
}
|
||||
|
||||
public void setListener(OnFloatingClickListener listener) {
|
||||
public void setOnFloatingClickListener(OnFloatingClickListener listener) {
|
||||
this.listener = listener;
|
||||
}
|
||||
|
||||
private OnFloatingClickListener listener;
|
||||
|
||||
public interface OnFloatingClickListener {
|
||||
void onClick();
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user