悬浮球踩过的坑
公司的SDK要写一个登录界面,里面涉及到悬浮球,但是不能申请权限。起初想到的方案是利用游戏的一个Activity,通过下面代码找到容器view
View root = activity.findViewById(android.R.id.content);
然后再把悬浮球View添加进去
View root = activity.findViewById(android.R.id.content);
if (root instanceof FrameLayout) {
FrameLayout content = (FrameLayout) root;
View viewById = content.findViewById(R.id.float_ball_icon);
if (viewById != null) {
return;
}
floatView = View.inflate(gameActivity, R.layout.view_float_ball, null);
FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
ImageView floatBallIcon = floatView.findViewById(R.id.float_ball_icon);
params.gravity = Gravity.START;
params.topMargin = 100;
ValueAnimator animator = ValueAnimator.ofFloat(0, -UIUtils.getUIUtils().getWidth(43)).setDuration(500);
animator.setInterpolator(new LinearInterpolator());
animator.addUpdateListener(animation -> {
floatView.setX((Float) animation.getAnimatedValue());
floatView.bringToFront();
});
animator.start();
floatView.setLayoutParams(params);
floatView.bringToFront();
content.addView(floatView);
}
整体代码就是上面的,在本地跑没什么事,但是入到游戏里面发现悬浮球移动一下就消失了,后来找到原因是游戏的surfaceView会挡住,解决方法: floatView.bringToFront(); View的这个方法,在每次移动的时候都得加这行代码,保证这个View在最前面。
但是还是会有问题,就是移动的时候有时会有穿墙的效果出现。这样放到游戏里面肯定不会
后来经过思考:第二种实现应用内悬浮球的方法popwindow,经过实验果不其然比上面方法好多了,操作还简单。
主要方法:代码如下
public FloatPopup(Context context) {
//记录当前悬浮按钮显示位置
View floatView = View.inflate(gameActivity, R.layout.view_float_ball, null);
pointView = floatView.findViewById(R.id.float_ball_point);
pointView.setVisibility(View.GONE);
ImageView floatBallIcon = floatView.findViewById(R.id.float_ball_icon);
setContentView(floatView);
setWidth(size);
setHeight(size);
setFocusable(false);
setClippingEnabled(false);
setBackgroundDrawable(new ColorDrawable(0x00000000));
setOutsideTouchable(false);
setTouchInterceptor(new View.OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
x = event.getRawX();
y = event.getRawY();
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
downX = event.getRawX();
downY = event.getRawY();
isClickState = true;
break;
case MotionEvent.ACTION_MOVE:
if (Math.abs(x - downX) < clickLimitValue && Math.abs(y - downY) < clickLimitValue && isClickState) {
isClickState = true;
} else {
isClickState = false;
float offX = event.getRawX() - downX;
float offY = event.getRawY() - downY;
Log.e("floatball", "offX: " + offX + " offY:" + offY);
update((int) (lastX + offX), (int) (offY + lastY));
}
break;
case MotionEvent.ACTION_UP:
if (x - downX < clickLimitValue && isClickState) {
getContentView().performClick();
break;
}
if (x < screenWidth / 2.f) {
showX = 0;
showY = lastY + event.getRawY() - downY;
} else {
showX = screenWidth - size;
showY = lastY + event.getRawY() - downY;
}
if (showY <= 0) {
showY = 0;
} else if (showY >= screenHeight) {
showY = screenHeight - size;
}
update((int) showX, (int) showY);
lastX = showX;
lastY = showY;
v.removeCallbacks(runnable);
v.postDelayed(runnable, 300);
break;
}
return true;
}
});
}
这之间也碰到几个问题:
1:popwindow不能移动到屏幕外 setClippingEnabled(false);加入这行代码
2:拖拽popwindows的时候,位移处理,这里就有点小技巧,就是利用初始位置+移动偏移位置就可以完美解决。
3:还有就是旋转屏幕的时候popwindow显示不出来问题,这就要监听游戏activity的生命周期,然后做相应的重建操作
4:有时游戏界面是固定方向的,但是切换字体的时候出会让activity重建,这个时候如果我们监听了activity生命周期,在onResume的时候进行重建,这时你会发现popwindwo重建不了。解决方法:利用handler发送一个延时消息。完美解决。