封版羽声,作为最后的基础版
This commit is contained in:
@@ -0,0 +1,245 @@
|
||||
package com.qxcm.moduleutil.widget.floatingView;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.res.Configuration;
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
import android.util.AttributeSet;
|
||||
import android.util.Log;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.FrameLayout;
|
||||
|
||||
import com.blankj.utilcode.util.BarUtils;
|
||||
|
||||
/**
|
||||
* @Author $
|
||||
* @Time $ $
|
||||
* @Description $
|
||||
*/
|
||||
public class Floa extends FrameLayout {
|
||||
public static final int MARGIN_EDGE = 13;
|
||||
private float mOriginalRawX;
|
||||
private float mOriginalRawY;
|
||||
private float mOriginalX;
|
||||
private float mOriginalY;
|
||||
private MagnetViewListener mMagnetViewListener;
|
||||
private static final int TOUCH_TIME_THRESHOLD = 150;
|
||||
private long mLastTouchDownTime;
|
||||
protected MoveAnimator mMoveAnimator;
|
||||
protected int mScreenWidth;
|
||||
private int mScreenHeight;
|
||||
private int mStatusBarHeight;
|
||||
private boolean isNearestLeft = true;
|
||||
private float mPortraitY;
|
||||
|
||||
public void setMagnetViewListener(MagnetViewListener magnetViewListener) {
|
||||
this.mMagnetViewListener = magnetViewListener;
|
||||
}
|
||||
public interface MagnetViewListener {
|
||||
void onClick(Floa floa);
|
||||
}
|
||||
public Floa(Context context) {
|
||||
this(context, null);
|
||||
}
|
||||
|
||||
public Floa(Context context, AttributeSet attrs) {
|
||||
this(context, attrs, 0);
|
||||
}
|
||||
|
||||
public Floa(Context context, AttributeSet attrs, int defStyleAttr) {
|
||||
super(context, attrs, defStyleAttr);
|
||||
init();
|
||||
}
|
||||
|
||||
private void init() {
|
||||
mMoveAnimator = new MoveAnimator();
|
||||
mStatusBarHeight = BarUtils.getStatusBarHeight();
|
||||
setClickable(true);
|
||||
updateSize();
|
||||
// 确保视图初始位置在屏幕内
|
||||
setX(Math.max(0, Math.min(getX(), mScreenWidth - getWidth())));
|
||||
setY(Math.max(mStatusBarHeight, Math.min(getY(), mScreenHeight - getHeight())));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onTouchEvent(MotionEvent event) {
|
||||
if (event == null) {
|
||||
return false;
|
||||
}
|
||||
switch (event.getAction()) {
|
||||
case MotionEvent.ACTION_DOWN:
|
||||
changeOriginalTouchParams(event);
|
||||
updateSize();
|
||||
mMoveAnimator.stop();
|
||||
break;
|
||||
case MotionEvent.ACTION_MOVE:
|
||||
Log.d("FloatView", "ACTION_MOVE: " + event.getX() + ", " + event.getY()); // 添加日志
|
||||
updateViewPosition(event);
|
||||
break;
|
||||
case MotionEvent.ACTION_UP:
|
||||
clearPortraitY();
|
||||
moveToEdge();
|
||||
if (isOnClickEvent()) {
|
||||
dealClickEvent();
|
||||
}
|
||||
break;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
protected void dealClickEvent() {
|
||||
if (mMagnetViewListener != null) {
|
||||
mMagnetViewListener.onClick(this);
|
||||
}
|
||||
}
|
||||
|
||||
protected boolean isOnClickEvent() {
|
||||
return System.currentTimeMillis() - mLastTouchDownTime < TOUCH_TIME_THRESHOLD;
|
||||
}
|
||||
|
||||
private void updateViewPosition(MotionEvent event) {
|
||||
// 计算新的Y位置
|
||||
float desY = mOriginalY + event.getRawY() - mOriginalRawY;
|
||||
// 限制Y位置不超出屏幕高度
|
||||
if (desY < mStatusBarHeight) {
|
||||
desY = mStatusBarHeight;
|
||||
}
|
||||
if (desY > mScreenHeight - getHeight()) {
|
||||
desY = mScreenHeight - getHeight();
|
||||
}
|
||||
|
||||
// 计算新的X位置
|
||||
float desX = mOriginalX + event.getRawX() - mOriginalRawX;
|
||||
// 限制X位置不超出屏幕宽度
|
||||
if (desX < 0) {
|
||||
desX = 0;
|
||||
}
|
||||
if (desX > mScreenWidth - getWidth()) {
|
||||
desX = mScreenWidth - getWidth();
|
||||
}
|
||||
|
||||
// 设置视图的新位置
|
||||
setX(desX);
|
||||
setY(desY);
|
||||
|
||||
// 确保视图可见
|
||||
if (getVisibility() != VISIBLE) {
|
||||
setVisibility(VISIBLE);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void changeOriginalTouchParams(MotionEvent event) {
|
||||
mOriginalX = getX();//getX()相对于控件X坐标的距离
|
||||
mOriginalY = getY();
|
||||
mOriginalRawX = event.getRawX();//getRawX()指控件在屏幕上的X坐标
|
||||
mOriginalRawY = event.getRawY();
|
||||
mLastTouchDownTime = System.currentTimeMillis();
|
||||
}
|
||||
|
||||
protected void updateSize() {
|
||||
ViewGroup viewGroup = (ViewGroup) getParent();
|
||||
if (viewGroup != null) {
|
||||
mScreenWidth = viewGroup.getWidth() ;
|
||||
mScreenHeight = viewGroup.getHeight();
|
||||
}else {
|
||||
// 如果父视图为空,使用屏幕的实际宽度和高度
|
||||
mScreenWidth = getResources().getDisplayMetrics().widthPixels;
|
||||
mScreenHeight = getResources().getDisplayMetrics().heightPixels;
|
||||
}
|
||||
// mScreenWidth = (SystemUtils.getScreenWidth(getContext()) - this.getWidth());
|
||||
// mScreenHeight = SystemUtils.getScreenHeight(getContext());
|
||||
}
|
||||
|
||||
public void moveToEdge() {
|
||||
moveToEdge(isNearestLeft(), false);
|
||||
}
|
||||
|
||||
public void moveToEdge(boolean isLeft, boolean isLandscape) {
|
||||
float moveDistance = isLeft ? MARGIN_EDGE : mScreenWidth - MARGIN_EDGE;
|
||||
float y = getY();
|
||||
if (!isLandscape && mPortraitY != 0) {
|
||||
y = mPortraitY;
|
||||
clearPortraitY();
|
||||
}
|
||||
mMoveAnimator.start(moveDistance, Math.min(Math.max(0, y), mScreenHeight - getHeight()));
|
||||
}
|
||||
|
||||
private void clearPortraitY() {
|
||||
mPortraitY = 0;
|
||||
}
|
||||
|
||||
protected boolean isNearestLeft() {
|
||||
int middle = mScreenWidth / 2;
|
||||
isNearestLeft = getX() < middle;
|
||||
return isNearestLeft;
|
||||
}
|
||||
|
||||
public void onRemove() {
|
||||
if (mMagnetViewListener != null) {
|
||||
mMagnetViewListener=null;
|
||||
}
|
||||
}
|
||||
|
||||
protected class MoveAnimator implements Runnable {
|
||||
|
||||
private Handler handler = new Handler(Looper.getMainLooper());
|
||||
private float destinationX;
|
||||
private float destinationY;
|
||||
private long startingTime;
|
||||
|
||||
void start(float x, float y) {
|
||||
this.destinationX = x;
|
||||
this.destinationY = y;
|
||||
startingTime = 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);
|
||||
float deltaX = (destinationX - getX()) * progress;
|
||||
float deltaY = (destinationY - getY()) * progress;
|
||||
move(deltaX, deltaY);
|
||||
if (progress < 1) {
|
||||
handler.post(this);
|
||||
}
|
||||
}
|
||||
|
||||
private 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);
|
||||
if (getParent() != null) {
|
||||
final boolean isLandscape = newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE;
|
||||
markPortraitY(isLandscape);
|
||||
((ViewGroup) getParent()).post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
updateSize();
|
||||
moveToEdge(isNearestLeft, isLandscape);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private void markPortraitY(boolean isLandscape) {
|
||||
if (isLandscape) {
|
||||
mPortraitY = getY();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user