Search code examples
javaandroidviewviewgroup

ViewGroup How to get child view by location (x, y)?


I am making CustomLayout which can contain some child views. These child views may be overlapped each other. These child view's transform matrix be modified via setRotation setScale etc.

How we can get a child by local location (x, y)?:

class CustomLayout extends ViewGroup {
    public View getChildByLocation(int x, int y) {
        // HOW TO IMPLEMENT THIS
    }
}

As I know so far ViewGroup allow us to getChildAt(index) so I can loop through its children to find out the view I need. But it is so complicated and I want a official way to get a child by location(x,y).

Thank you in advance!


Solution

  • Use this Utils class bellow. Only 1 method call needed

    No need to subclass your layout. Call that method from main-thread, it supports translation/rotation/scale as well.

    // return null if no child at the position is found
    View outputView = Utils.findChildByPosition(theParentViewGroup, x, y)
    

    Full Source code of class Utils:

    public final class Utils {
        /**
         * find child View in a ViewGroup by its position (x, y)
         *
         * @param parent the viewgourp
         * @param x      the x position in parent
         * @param y      the y position in parent
         * @return null if not found
         */
        public static View findChildByPosition(ViewGroup parent, float x, float y) {
            int count = parent.getChildCount();
            for (int i = count - 1; i >= 0; i--) {
                View child = parent.getChildAt(i);
                if (child.getVisibility() == View.VISIBLE) {
                    if (isPositionInChildView(parent, child, x, y)) {
                        return child;
                    }
                }
            }
    
            return null;
        }
    
        private static boolean isPositionInChildView(ViewGroup parent, View child, float x, float y) {
            sPoint[0] = x + parent.getScrollX() - child.getLeft();
            sPoint[1] = y + parent.getScrollY() - child.getTop();
    
            Matrix childMatrix = child.getMatrix();
            if (!childMatrix.isIdentity()) {
                childMatrix.invert(sInvMatrix);
                sInvMatrix.mapPoints(sPoint);
            }
    
            x = sPoint[0];
            y = sPoint[1];
    
            return x >= 0 && y >= 0 && x < child.getWidth() && y < child.getHeight();
        }
    
        private static Matrix sInvMatrix = new Matrix();
        private static float[] sPoint = new float[2];
    }