Android buried point selection

Preface

In the previous blog , we implemented the seamless burying point based on the Didi DroidAssist plug-in, so that the user's operation path is uploaded to the server through the interface, and then the chart is made in the background, and the product manager analyzes and optimizes the subsequent experience of the product. The operation of the product provides data support. But now the View paths we upload are all like this MainActivity/LinearLayout[0]/LinearLayout[7]/TextView[2], these paths are not understood by others, so you need to set aliases for these paths, and you can know what control the user clicked by looking at the path aliases.

Implementation steps

  • Add a small dot on the screen, slide the small dot to get the current screen coordinate P
  • Recursively traverse all subviews under DecorView
  • Get all touchable views under the View (including yourself), traverse to get the position, width and height to determine whether the P coordinate is on the View
  • If the P coordinate is in this View, it means that we have selected this View, and then set the foreground color for this View
  • Get the path according to the selected View

Achieve effect

It is very simple to add a small dot View on the screen. You can add View to the screen through WindowManager, and then set the OnTouchListener to listen for events and move the dot. The position of the center point of the small dot is the coordinates we obtained. Next, select the corresponding View according to the coordinates. The specific code is as follows:

public class ViewUtil {
    
    
    //根据坐标获取相对应的子控件
    public static View getViewAtActivity(Activity activity, int x, int y) {
    
    
        View root = activity.getWindow().getDecorView();
        return findViewByXY(root, x, y);
    }
    public static View getViewAtViewGroup(View view, int x, int y) {
    
    
        return findViewByXY(view, x, y);
    }

    private static View findViewByXY(View view, int x, int y) {
    
    
        View targetView = null;
        if (view instanceof ViewGroup) {
    
    
            ViewGroup v = (ViewGroup) view;
            for (int i = 0; i < v.getChildCount(); i++) {
    
    
                targetView = getTouchTarget(v.getChildAt(i), x, y);
                if (targetView != null) {
    
    
                    break;
                } else {
    
    
                    findViewByXY(v.getChildAt(i), x, y);
                }
            }
        } else {
    
    
            targetView = getTouchTarget(view, x, y);
        }
        return targetView;
    }

  	//获取View下所有可触摸视图
    private static View getTouchTarget(View view, int x, int y) {
    
    
        View targetView = null;
        ArrayList<View> touchableViews = view.getTouchables();
        for (View child : touchableViews) {
    
    
            if (isTouchPointInView(child, x, y)) {
    
    
                targetView = child;
            }
        }
        return targetView;
    }

  	//判断坐标点是否在View上
    private static boolean isTouchPointInView(View view, int x, int y) {
    
    
        int[] location = new int[2];
        view.getLocationOnScreen(location);
        int left = location[0];
        int top = location[1];
        int right = left + view.getMeasuredWidth();
        int bottom = top + view.getMeasuredHeight();
        if (view.isClickable() && y >= top && y <= bottom && x >= left
                && x <= right) {
    
    
            return true;
        }
        return false;
    }
}

Once we get the View, we can traverse up layer by layer according to the View, get the name and index of the View, and then stitch them together until we traverse to the top of the view. The specific code is as follows:

public static String parsePath(View view) {
    
    
        String path = "";
        //循环获取ViewPath
        do {
    
    
            String simpleName = view.getClass().getSimpleName();
            ViewParent parent = view.getParent();
            if (parent instanceof ViewGroup) {
    
    
                int index = ((ViewGroup) parent).indexOfChild(view);
                path = String.format(Locale.CHINA, "%s[%d]/%s", simpleName, index, path);
                view = (ViewGroup) parent;
            }

        } while (view.getParent() instanceof View);

        //替换掉页面相同路径,分两种情况
        path = path
          //当页面设置fitsSystemWindows=true时
          .replaceFirst("LinearLayout\\[0]/FrameLayout\\[1]/FitWindowsLinearLayout\\[0]/ContentFrameLayout\\[1]","")
          //当页面设置fitsSystemWindows=false时
                .replaceFirst("LinearLayout\\[0]/FrameLayout\\[1]","");
        path = path.substring(0, path.lastIndexOf('/'));
        path = path.substring(1);
        return path;
    }

We have finished filtering the View and resolving the path, and now we have reached the final stage that is to set up aliases. We pass the parsed path to a page, fill in the alias to connect to the server interface and it is done, so that the collected path clicked by the user will know what it means in the background.

Reference article

Android get control method based on coordinates

Guess you like

Origin blog.csdn.net/ZYJWR/article/details/96508268