适用场景:
1.不需要判断Window权限,也许用户不同意权限,窗口会失效
2.悬浮菜单在APP使用的每一个界面都存(可以选择星的不存在,例如登录页面)
3.当APP退出时,悬浮菜单会消失
4.松手悬浮窗口会自动贴边
实现思路:
https://github.com/153437803/FloatMenu
1.ViewDragHelper,官方提供的一个处理拖拽滑动事件的类库,不明白的可以自行度娘一下
2.在BaseActivity里面动态判断那些界面需要悬浮窗口,那些界面不需要悬浮窗口, 动态设置布局
3.在onResume(),onPause(),onBackPressed()根据业务需求,动态判断什么时候需要结束隐藏显示浮动窗口
/**
* description: 可拖拽的父容器
* created by kalu on 2017/3/14 15:32
*/
public class FloatLayout extends FrameLayout {
private final int TEMP_SIZE = DeviceUtil.dp2px(5);
private final int SCR_WIDTH = DeviceUtil.getScreenWidth();
private final int SCR_HEIGHT = DeviceUtil.getScreenHeight() - DeviceUtil.getStatusHeight() - DeviceUtil.dp2px(55);
/**********************************************************************************************/
private FloatSub floatSub;
private ViewDragHelper helper;
/**********************************************************************************************/
private SharedPreferences sp = APP.getInstance().getApplicationContext().getSharedPreferences("FloatLayout", Context.MODE_PRIVATE);
private SharedPreferences.Editor editor = sp.edit();
private final String FLOAT_SUB_TOP = "FLOAT_SUB_TOP";
private final String FLOAT_SUB_LEFT = "FLOAT_SUB_LEFT";
/**********************************************************************************************/
public FloatLayout(Context context) {
this(context, null, 0);
}
public FloatLayout(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public FloatLayout(final Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
setBackgroundColor(Color.TRANSPARENT);
floatSub = new FloatSub(APP.getInstance().getApplicationContext());
floatSub.setVisibility(View.GONE);
addView(floatSub);
setFloatSubSize(DeviceUtil.dp2px(168), DeviceUtil.dp2px(100));
helper = ViewDragHelper.create(this, 1.0f, new ViewDragHelper.Callback() {
@Override
public boolean tryCaptureView(View child, int pointerId) {
// Log.e(TAG, "tryCaptureView ==> ");
return null == child ? false : child instanceof FloatSub;
}
/**
* 手指触摸水平位移
* @param child
* @param left
* @param dx
* @return
*/
@Override
public int clampViewPositionHorizontal(View child, int left, int dx) {
// Log.e(TAG, "clampViewPositionHorizontal ==> ");
// final int leftBound = getPaddingLeft();
// final int rightBound = getWidth() - child.getWidth() - leftBound;
// final int newLeft = Math.min(Math.max(left, leftBound), rightBound);
// return newLeft;
return left;
}
/**
* 手指触摸竖直位移
* @param child
* @param top
* @param dy
* @return
*/
@Override
public int clampViewPositionVertical(View child, int top, int dy) {
// Log.e(TAG, "clampViewPositionVertical ==> ");
// final int topBound = getPaddingTop();
// final int bottomBound = getHeight() - child.getHeight() - dp2px(context, 50);
// final int newTop = Math.min(Math.max(top, topBound), bottomBound);
// return newTop;
return top;
}
/**
* 水平方向可以滑动的距离
* @param child
* @return
*/
@Override
public int getViewHorizontalDragRange(View child) {
// Log.e(TAG, "getViewHorizontalDragRange ==> ");
// return getMeasuredWidth() - child.getMeasuredWidth();
return getMeasuredWidth();
}
/**
* 竖直方向可以滑动的距离
* @param child
* @return
*/
@Override
public int getViewVerticalDragRange(View child) {
// Log.e(TAG, "getViewVerticalDragRange ==> ");
// return getMeasuredHeight() - child.getMeasuredHeight() - dp2px(context, 50);
return getMeasuredHeight();
}
@Override
public void onViewDragStateChanged(int state) {
super.onViewDragStateChanged(state);
if (null == floatSub) return;
// 更新坐标
float x = floatSub.getX();
float y = floatSub.getY();
editor.putFloat(FLOAT_SUB_TOP, x);
editor.putFloat(FLOAT_SUB_LEFT, y);
editor.commit();
}
@Override
public void onViewReleased(View releasedChild, float xvel, float yvel) {
// Log.e(TAG, "onViewReleased ==> ");
if (null == floatSub || releasedChild != floatSub) return;
int width = releasedChild.getWidth();
int height = releasedChild.getHeight();
int left = releasedChild.getLeft();
int top = releasedChild.getTop();
int right = releasedChild.getRight();
int bottom = releasedChild.getBottom();
// 移到屏幕外边
if (left < 0 || top < 0 || right > SCR_WIDTH || bottom > SCR_HEIGHT) {
// 左侧出边
if (left < 0) {
left = TEMP_SIZE;
if (top < 0) {
top = TEMP_SIZE;
}
if (bottom > SCR_HEIGHT) {
top = SCR_HEIGHT - height;
}
}
// 右侧出边
else if (right > SCR_WIDTH) {
int rightMax = SCR_WIDTH - TEMP_SIZE;
left = rightMax - width;
if (top < 0) {
top = TEMP_SIZE;
}
if (bottom > SCR_HEIGHT) {
top = SCR_HEIGHT - height;
}
}
// 上侧出边
else if (top < 0) {
top = TEMP_SIZE;
}
// 下侧出边
else if (bottom > SCR_HEIGHT) {
top = SCR_HEIGHT - height;
}
}
// 处于屏幕里面
else {
// 距离左侧距离
int ragneLeft = left;
int ragneRight = SCR_WIDTH - right;
int ragneTop = top;
int rangeBottom = SCR_HEIGHT - bottom;
int temp1 = Math.min(ragneLeft, ragneRight);
int temp2 = Math.min(temp1, ragneTop);
int ragneMin = Math.min(temp2, rangeBottom);
if (ragneMin == ragneLeft) {
left = TEMP_SIZE;
} else if (ragneMin == ragneRight) {
int rightMax = SCR_WIDTH - TEMP_SIZE;
left = rightMax - width;
} else if (ragneMin == ragneTop) {
top = TEMP_SIZE;
} else {
top = SCR_HEIGHT - height;
}
}
helper.settleCapturedViewAt(left, top);
invalidate();
}
});
}
/**********************************************************************************************/
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
return helper.shouldInterceptTouchEvent(ev);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
helper.processTouchEvent(event);
return true;
}
@Override
public void computeScroll() {
if (!helper.continueSettling(true)) return;
invalidate();
}
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
super.onLayout(changed, left, top, right, bottom);
if (null != floatSub && floatSub.getVisibility() == View.VISIBLE) {
// 读取保存的位置
float x = sp.getFloat(FLOAT_SUB_TOP, -1);
float y = sp.getFloat(FLOAT_SUB_LEFT, -1);
// LogUtil.e(TAG, "onLayout ==> x = " + x + ", y = " + y);
if (x == -1 && y == -1) return;
int measuredWidth = floatSub.getMeasuredWidth();
int maxX = DeviceUtil.getScreenWidth() - measuredWidth - DeviceUtil.dp2px(5);
if (x > maxX) {
x = maxX;
sp.edit().putFloat(FLOAT_SUB_TOP, x).commit();
}
int measuredHeight = floatSub.getMeasuredHeight();
int maxY = DeviceUtil.getScreenHeight() - DeviceUtil.dp2px(80) - measuredHeight;
if (y > maxY) {
y = maxY;
sp.edit().putFloat(FLOAT_SUB_LEFT, y).commit();
}
floatSub.layout((int) x, (int) y, (int) x + measuredWidth, (int) y + measuredHeight);
}
}
/**********************************************************************************************/
public void clearData() {
if (null == sp) return;
sp.edit().clear().commit();
}
public FloatSub getFloatSub() {
return floatSub;
}
public void setFloatSubSize(int width, int height) {
if (null == floatSub) return;
LayoutParams params = (LayoutParams) floatSub.getLayoutParams();
params.width = width;
params.height = height;
params.gravity = Gravity.RIGHT | Gravity.TOP;
params.topMargin = DeviceUtil.dp2px(170);
params.rightMargin = DeviceUtil.dp2px(5);
}
public void addFloatSub(View view) {
if (null == floatSub) return;
floatSub.addView(view, 0);
floatSub.setVisibility(View.VISIBLE);
}
public void setOnFloatSubClickListener(OnFloatClickListener listener) {
if (null == floatSub) return;
floatSub.setOnFloatClickListener(listener);
}
/**********************************************************************************************/
}
/**
* description: 可拖拽的显示容器
* created by kalu on 2017/3/14 17:35
*/
public class FloatSub extends FrameLayout {
private final String TAG = FloatSub.class.getSimpleName();
/**********************************************************************************************/
public FloatSub(Context context) {
this(context, null, 0);
}
public FloatSub(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public FloatSub(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
setBackgroundColor(Color.BLACK);
// 1.添加蒙层
View cover = new View(APP.getInstance().getApplicationContext());
cover.setClickable(true);
addView(cover);
cover.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
if (null == onFloatClickListener) return;
onFloatClickListener.onClickFloatWindow(v);
}
});
// 2.添加关闭
ImageView image = new ImageView(APP.getInstance().getApplicationContext());
image.setClickable(true);
int pading = DeviceUtil.dp2px(5);
image.setPadding(pading, pading, pading, pading);
image.setImageResource(R.drawable.ic_vector_close);
image.setBackgroundColor(getResources().getColor(R.color.black_40));
addView(image);
LayoutParams params = (LayoutParams) image.getLayoutParams();
int size = DeviceUtil.dp2px(25);
params.width = size;
params.height = size;
params.gravity = Gravity.TOP | Gravity.RIGHT;
image.setLayoutParams(params);
image.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
if (null == onFloatClickListener) return;
onFloatClickListener.onClickFloatClose();
}
});
// 3.添加转圈
LoadingView loading = new LoadingView(APP.getInstance().getApplicationContext());
loading.setId(R.id.float_sub_loading);
loading.setVisibility(View.GONE);
addView(loading);
FrameLayout.LayoutParams paramsLoading = (LayoutParams) loading.getLayoutParams();
paramsLoading.width = DeviceUtil.dp2px(25);
paramsLoading.height = DeviceUtil.dp2px(25);
paramsLoading.gravity = Gravity.CENTER;
}
public void setLoading(int playStatus) {
LogUtil.e(TAG, "setLoading ==> playStatus = " + playStatus);
View sub = findViewById(R.id.float_sub_loading);
if (null != sub) {
if (playStatus == PlayerManager.PLAYER_LOADING) {
sub.setVisibility(View.VISIBLE);
LogUtil.e(TAG, "setLoading ==> 显示");
} else {
sub.setVisibility(View.GONE);
LogUtil.e(TAG, "setLoading ==> 隐藏");
}
}
}
/*********************************************************************************************/
// public void changeLoadingState(int visibility) {
// View loading = findViewById(R.id.float_sub_loading);
//
// if (null == loading) return;
// loading.setVisibility(visibility);
// }
/*********************************************************************************************/
private OnFloatClickListener onFloatClickListener;
public void setOnFloatClickListener(OnFloatClickListener onFloatClickListener) {
this.onFloatClickListener = onFloatClickListener;
}
}
/**
* Created by SEELE on 2017/3/14.
*/
public interface OnFloatClickListener {
/**
* 点击孩子
*
* @param view
*/
void onClickFloatWindow(View view);
/**
* 点击关闭
*/
void onClickFloatClose();
}