本文共 2606 字,大约阅读时间需要 8 分钟。
在界面上点击按钮时,想想安卓是如何找到能响应事件的顶层View的?
如果给你坐标x、y, 你能找到对应的顶层View么?首先安卓布局根节点是DecorView,并呈现为多叉树结构; 每个顶层View都是一个叶节点;
需求:手指在界面上滑动时显示对应的顶层View。
Activity显示在屏幕上时, 以DecorView为根节点并呈现多叉树数据结构;
背景知识:
安卓事件分发时从最后一个子节点开始传递事件, 原因是什么呢? ViewGroup.java的dispatchTouchEvent函数在遍历子View时, 是从最后一个子View开始分发事件的, 详见第2629行;对于Android界面可以从DecorView开始查找, 如果坐标x,y在控件矩形范围内则继续向下递归,为了找到叶节点(即最上面的View)需要深度优先;
public class MainActivity extends AppCompatActivity { private TextView mTvInfo; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); mTvInfo = findViewById(R.id.tv_info); } @Override public boolean onTouchEvent(MotionEvent event) { showWidgetInfo(event.getX(), event.getY()); return super.onTouchEvent(event); } private void showWidgetInfo(float x, float y) { View view = getViewByPosition(getWindow().getDecorView(), (int)x, (int)y); if (view != null) { mTvInfo.setText(view.toString()); } else { mTvInfo.setText("没找到匹配的View"); } } private View getViewByPosition(View view, int x, int y) { if (view == null) { return null; } int[] location = new int[2]; view.getLocationInWindow(location); int left = location[0]; int top = location[1]; int right = left + view.getWidth(); int bottom = top + view.getHeight(); if (view instanceof ViewGroup) { //当前是ViewGroup容器 int childCount = ((ViewGroup)view).getChildCount(); //深度优先, 从最后一个子节点开始遍历,如果找到则返回。 先递归判断子View if (childCount > 0) { for (int i = childCount - 1; i >= 0; i--) { View topView = getViewByPosition(((ViewGroup)view).getChildAt(i), x, y); if (topView != null) { return topView; } } } //子View都没找到匹配的, 再判断自己 if (left < x && top < y && right > x && bottom > y) { return view; //当前ViewGroup就是顶层View } else { return null; //没找到匹配的 } } else { //当前是View if (left < x && top < y && right > x && bottom > y) { return view; //当前ViewGroup就是顶层View } else { return null; //没找到匹配的 } } }}
转载地址:http://gzqvb.baihongyu.com/