安卓-关于setOnTouchListener和setOnClickListener冲突的问题
我在我的应用上搞了一个悬浮窗,我希望可以自由拖动它,并且在悬浮窗里面设计了一个按钮。
windowManager = (WindowManager) getSystemService(WINDOW_SERVICE);
WindowManager.LayoutParams params = new WindowManager.LayoutParams(
WindowManager.LayoutParams.WRAP_CONTENT,
WindowManager.LayoutParams.WRAP_CONTENT,
Build.VERSION.SDK_INT >= Build.VERSION_CODES.O ?
WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY :
WindowManager.LayoutParams.TYPE_SYSTEM_ALERT,
WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE |
WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL,
PixelFormat.TRANSLUCENT
);
floatView = LayoutInflater.from(this).inflate(R.layout.float_window, null);
windowManager.addView(floatView, params);
正常来说就是很平常地用windowmanager添加悬浮的窗口,随后对floatView添加监听器
但是当我获取完其内部的按钮并且设置监听的时候出现了问题:按钮一直点不动
研究了一番发现,是event被floatView上的监听器消耗了。
一般来说,在TouchListener内的onTouch是有一个布尔类型的返回值的,true表示我当前重写的函数已经处理了event时间;false则表示没有处理。
那这里就可以做文章了:如果我希望按钮可以正常点击,那我就在touch内调整,让它在不是滑动的情况下返回个false,把事件丢回去不就可以了
于是乎,有了如下代码
package com.rizuiyou.trickycandidate;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewConfiguration;
import android.view.WindowManager;
public class ItemViewTouchListener implements View.OnTouchListener {
private final WindowManager.LayoutParams layoutParams;
private final WindowManager windowManager;
private int initialX;
private int initialY;
private float initialTouchX;
private float initialTouchY;
private boolean isDragging = false;
private final int touchSlop;
public ItemViewTouchListener(WindowManager.LayoutParams layoutParams, WindowManager windowManager, View view) {
this.layoutParams = layoutParams;
this.windowManager = windowManager;
this.touchSlop = ViewConfiguration.get(view.getContext()).getScaledTouchSlop();
}
@Override
public boolean onTouch(View v, MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
initialX = layoutParams.x;
initialY = layoutParams.y;
initialTouchX = event.getRawX();
initialTouchY = event.getRawY();
isDragging = false;
return false; // 关键点:不拦截 DOWN 事件
case MotionEvent.ACTION_MOVE:
float dx = Math.abs(event.getRawX() - initialTouchX);
float dy = Math.abs(event.getRawY() - initialTouchY);
if (!isDragging && (dx > touchSlop || dy > touchSlop)) {
isDragging = true;
}
if (isDragging) {
layoutParams.x = initialX + (int)(event.getRawX() - initialTouchX);
layoutParams.y = initialY + (int)(event.getRawY() - initialTouchY);
windowManager.updateViewLayout(v, layoutParams);
return true; // 拖动时拦截事件
}
return false; // 未拖动时不拦截
case MotionEvent.ACTION_UP:
if (isDragging) {
isDragging = false;
return true; // 拖动结束,拦截 UP 事件
}
return false; // 未拖动时允许点击
default:
return false;
}
}
public boolean isDragging() {
return isDragging;
}
}
但是发现,又出问题了:窗口拖不动了;这是因为我的窗口里面只有一个按钮,event时间跑到按钮身上了,所以这时候再也它加一个setOnTouchListener,然后把这个交给整个窗口的touch来处理(并且加一个标签保证按钮只有在不拖动的时候才处理点击):
// 设置根容器触摸监听
View container = floatView.findViewById(R.id.float_container);
ItemViewTouchListener touchListener = new ItemViewTouchListener(params, windowManager, container);
container.setOnTouchListener(touchListener);
// 设置按钮的触摸监听(将事件传递给根容器)
Button btnAnswer = floatView.findViewById(R.id.btn_answer);
btnAnswer.setOnTouchListener((v, event) -> {
// 直接调用容器的触摸监听器,避免递归
return touchListener.onTouch(container, event);
});
// 设置按钮点击监听
btnAnswer.setOnClickListener(v -> {
if (!touchListener.isDragging()) {
// Toast.makeText(this, "点击了按钮", Toast.LENGTH_SHORT).show();
if (!responseReceiver.getServiceEnabled()) {
return ;
}
long currentTime = System.currentTimeMillis();
if (currentTime - lastTime > 5000) {
lastTime = currentTime;
}
answer();
// test();
}
});
至此告成