将触摸事件传递给 children 的 dispatchTouchEvent 问题
dispatchTouchEvent issue for passing the touch event to children
我正在尝试为 pre-lollipop 版本创建棒棒糖工具栏图标的效果。
我写了这个扩展 LinearLayout 的 class 并在 dispatchTouchEvent 中检查哪个 child 更接近触摸位置并将该事件仅发送到那个 child,所以它忽略了 children:
的重叠区域和 z-index
public class OverlappingLayout extends LinearLayout {
public OverlappingLayout(Context context) {
super(context);
}
public OverlappingLayout(Context context, AttributeSet attrs) {
super(context, attrs);
}
int lastAction = 0;
View lastView = null;
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
if(lastView != null){
lastView.dispatchTouchEvent(ev);
if(ev.getAction() == MotionEvent.ACTION_CANCEL || ev.getAction() == MotionEvent.ACTION_UP)
lastView = null;
return true;
}
float min = 99999999f;
int minIndex = 0;
for (int i = 0; i < getChildCount(); i++) {
View child = getChildAt(i);
float centerX = child.getX() + child.getWidth() / 2;
float centerY = child.getY() + child.getHeight() / 2;
float dis = (float) Math.sqrt(
(ev.getX() - centerX) * (ev.getX() - centerX) +
(ev.getY() - centerY) * (ev.getY() - centerY)
);
if(dis < min) {
min = dis;
minIndex = i;
}
}
getChildAt(minIndex).setClickable(true);
getChildAt(minIndex).dispatchTouchEvent(ev);
lastView = getChildAt(minIndex);
lastAction = ev.getAction();
lastView.setOnTouchListener(new OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
//never works for the last child, or the right half of the second child
return false;
}
});
return true;
}
}
但问题是代码在布局的前半部分工作正常,在后半部分不起作用。
代码在调试模式下工作,找到最接近的 child 并将事件传递给它,但是 child 的可绘制对象的按下状态不起作用。
我也改成RelativeLayout,还是一样的问题
我知道问题出在哪里了。
当我将 motionEvent object 发送到 children 时,触摸的 x 和 y 位于 child 的矩形之外(如 child 所见)。所以它只适用于前半部分,因为前半部分和后半部分 child 看到了他们矩形内的触摸位置。所以我将代码更改为:
private Rect rect;
View lastView;
@Override
public boolean dispatchTouchEvent(MotionEvent event) {
for (int i = 0; i < getChildCount(); i++) {
getChildAt(i).setClickable(false);
}
if(lastView == null)
lastView = getClosestChildToTouch(event);
if (event.getAction() == MotionEvent.ACTION_DOWN) {
lastView.setPressed(true);
rect = new Rect(lastView.getLeft(), lastView.getTop(), lastView.getRight(), lastView.getBottom());
}
if (event.getAction() == MotionEvent.ACTION_UP) {
if (!rect.contains((int) event.getX(), (int) event.getY())) {
lastView.setPressed(false);
lastView=null;
} else {
lastView.performClick();
lastView.setPressed(false);
lastView=null;
}
}
if(event.getAction() == MotionEvent.ACTION_MOVE){
if(!rect.contains((int) event.getX(),(int) event.getY())){
lastView.setPressed(false);
lastView=null;
}
}
if (event.getAction() == MotionEvent.ACTION_CANCEL){
lastView.setPressed(false);
lastView=null;
}
return true;
}
public View getClosestChildToTouch(MotionEvent ev){
float min = 99999999f;
int minIndex = 0;
for (int i = 0; i < getChildCount(); i++) {
View child = getChildAt(i);
float centerX = child.getX() + child.getWidth() / 2;
float centerY = child.getY() + child.getHeight() / 2;
float dis = (float) Math.sqrt(
(ev.getX() - centerX) * (ev.getX() - centerX) +
(ev.getY() - centerY) * (ev.getY() - centerY)
);
if(dis < min) {
min = dis;
minIndex = i;
}
}
return getChildAt(minIndex);
}
我也可以修改 motionEvent object 并将 child 的左侧或右侧位置添加到 x 属性。
我正在尝试为 pre-lollipop 版本创建棒棒糖工具栏图标的效果。
我写了这个扩展 LinearLayout 的 class 并在 dispatchTouchEvent 中检查哪个 child 更接近触摸位置并将该事件仅发送到那个 child,所以它忽略了 children:
的重叠区域和 z-indexpublic class OverlappingLayout extends LinearLayout {
public OverlappingLayout(Context context) {
super(context);
}
public OverlappingLayout(Context context, AttributeSet attrs) {
super(context, attrs);
}
int lastAction = 0;
View lastView = null;
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
if(lastView != null){
lastView.dispatchTouchEvent(ev);
if(ev.getAction() == MotionEvent.ACTION_CANCEL || ev.getAction() == MotionEvent.ACTION_UP)
lastView = null;
return true;
}
float min = 99999999f;
int minIndex = 0;
for (int i = 0; i < getChildCount(); i++) {
View child = getChildAt(i);
float centerX = child.getX() + child.getWidth() / 2;
float centerY = child.getY() + child.getHeight() / 2;
float dis = (float) Math.sqrt(
(ev.getX() - centerX) * (ev.getX() - centerX) +
(ev.getY() - centerY) * (ev.getY() - centerY)
);
if(dis < min) {
min = dis;
minIndex = i;
}
}
getChildAt(minIndex).setClickable(true);
getChildAt(minIndex).dispatchTouchEvent(ev);
lastView = getChildAt(minIndex);
lastAction = ev.getAction();
lastView.setOnTouchListener(new OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
//never works for the last child, or the right half of the second child
return false;
}
});
return true;
}
}
但问题是代码在布局的前半部分工作正常,在后半部分不起作用。
代码在调试模式下工作,找到最接近的 child 并将事件传递给它,但是 child 的可绘制对象的按下状态不起作用。
我也改成RelativeLayout,还是一样的问题
我知道问题出在哪里了。 当我将 motionEvent object 发送到 children 时,触摸的 x 和 y 位于 child 的矩形之外(如 child 所见)。所以它只适用于前半部分,因为前半部分和后半部分 child 看到了他们矩形内的触摸位置。所以我将代码更改为:
private Rect rect;
View lastView;
@Override
public boolean dispatchTouchEvent(MotionEvent event) {
for (int i = 0; i < getChildCount(); i++) {
getChildAt(i).setClickable(false);
}
if(lastView == null)
lastView = getClosestChildToTouch(event);
if (event.getAction() == MotionEvent.ACTION_DOWN) {
lastView.setPressed(true);
rect = new Rect(lastView.getLeft(), lastView.getTop(), lastView.getRight(), lastView.getBottom());
}
if (event.getAction() == MotionEvent.ACTION_UP) {
if (!rect.contains((int) event.getX(), (int) event.getY())) {
lastView.setPressed(false);
lastView=null;
} else {
lastView.performClick();
lastView.setPressed(false);
lastView=null;
}
}
if(event.getAction() == MotionEvent.ACTION_MOVE){
if(!rect.contains((int) event.getX(),(int) event.getY())){
lastView.setPressed(false);
lastView=null;
}
}
if (event.getAction() == MotionEvent.ACTION_CANCEL){
lastView.setPressed(false);
lastView=null;
}
return true;
}
public View getClosestChildToTouch(MotionEvent ev){
float min = 99999999f;
int minIndex = 0;
for (int i = 0; i < getChildCount(); i++) {
View child = getChildAt(i);
float centerX = child.getX() + child.getWidth() / 2;
float centerY = child.getY() + child.getHeight() / 2;
float dis = (float) Math.sqrt(
(ev.getX() - centerX) * (ev.getX() - centerX) +
(ev.getY() - centerY) * (ev.getY() - centerY)
);
if(dis < min) {
min = dis;
minIndex = i;
}
}
return getChildAt(minIndex);
}
我也可以修改 motionEvent object 并将 child 的左侧或右侧位置添加到 x 属性。