Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

print a view hierarchy on a device

Debugging my app with strange results on Samsung phones, which I don't have physical access to. I'd like to ask a user to run an instrumented App to help debug. My App gets a view that has an unknown (to me ;-) hierarchy in it (ViewGroups etc). Is there a way to "walk" a View and print out to a string / ddms all the components in the View (etc ViewGroups in it etc)?

This would be akin to HierarchyViewer tool - if I had adb level access to the device.

Update: I guess I could use the method

void dumpViewHierarchyWithProperties(Context context, ViewGroup group, BufferedWriter out, int level)

from the ViewDebug.java Android OS sources ...

Anyone have a better idea?

Thanks!

like image 514
Yossi Avatar asked Jul 02 '11 20:07

Yossi


3 Answers

Here's the utility function I just made for this purpose:

public static void printViewHierarchy(ViewGroup vg, String prefix) {
    for (int i = 0; i < vg.getChildCount(); i++) {
        View v = vg.getChildAt(i);
        String desc = prefix + " | " + "[" + i + "/" + (vg.getChildCount()-1) + "] "+ v.getClass().getSimpleName() + " " + v.getId();
        Log.v("x", desc);

        if (v instanceof ViewGroup) {
            printViewHierarchy((ViewGroup)v, desc);
        }
    }
}
like image 196
felarca Avatar answered Oct 21 '22 16:10

felarca


I've created utility method which returns hierarchy in a pretty printed way with human readable view ids. Here is an example of the output:

[LinearLayout] no_id
  [CardView] com.example:id/card_view
    [RelativeLayout] no_id
      [LinearLayout] com.example:id/image_container
        [AppCompatImageView] com.example:id/incident_icon
        [CustomTextView] com.example:id/road_number
      [RelativeLayout] no_id
        [CustomTextView] com.example:id/distance_to_user
        [CustomTextView] com.example:id/obstruction_title
        [CustomTextView] com.example:id/road_direction
        [CustomTextView] com.example:id/obstruction_description
        [AppCompatImageView] com.example:id/security_related

Here is the utility method:

public static String getViewHierarchy(@NonNull View v) {
    StringBuilder desc = new StringBuilder();
    getViewHierarchy(v, desc, 0);
    return desc.toString();
}

private static void getViewHierarchy(View v, StringBuilder desc, int margin) {
    desc.append(getViewMessage(v, margin));
    if (v instanceof ViewGroup) {
        margin++;
        ViewGroup vg = (ViewGroup) v;
        for (int i = 0; i < vg.getChildCount(); i++) {
            getViewHierarchy(vg.getChildAt(i), desc, margin);
        }
    }
}

private static String getViewMessage(View v, int marginOffset) {
    String repeated = new String(new char[marginOffset]).replace("\0", "  ");
    try {
        String resourceId = v.getResources() != null ? (v.getId() > 0 ? v.getResources().getResourceName(v.getId()) : "no_id") : "no_resources";
        return repeated + "[" + v.getClass().getSimpleName() + "] " + resourceId + "\n";
    } catch (Resources.NotFoundException e) {
        return repeated + "[" + v.getClass().getSimpleName() + "] name_not_found\n";
    }
}

Tip: we use this method to add view hierarchies to some crash reports. In some cases it is really helpful.

like image 21
Oleksandr Avatar answered Oct 21 '22 16:10

Oleksandr


The format of other outputs dissatisfied my eyes and some codes where an overhead. So, I improved the output with:

/**
 * Prints the view hierarchy.
 */
public static void printViewHierarchy(ViewGroup parent, String intent) {
    for (int i = 0, max = parent.getChildCount(), numLenght = (max + "").length(); i < max; i++) {
        View child = parent.getChildAt(i);
        String childString = child.getClass().getSimpleName() + " " + child.getId();
        String format = "|— %0" + numLenght + "d/%0" + numLenght + "d %s";
        Log.d("debug", intent + String.format(format, i + 1, max, childString));

        if (child instanceof ViewGroup)
            printViewHierarchy((ViewGroup) child, intent + "  ");
    }
}

to:

|— 1/4 ScrollView 2131296482
|— 2/4 MaterialTextView 2131296445
|— 3/4 FloatingActionButton 2131296374
|— 4/4 MaterialTextView 2131296363
|— 1/4 ScrollView 2131296482
  |— 1/1 LinearLayout 2129243036
    |— 01/74 RelativeLayout 2131296449
      |— 1/4 MaterialCheckBox 2131296332
      |— 2/4 MaterialTextView 2131296547
      |— 3/4 MaterialTextView 2131296531
      |— 4/4 AppCompatImageButton 2131296462
    |— 02/74 RelativeLayout 2131296449
      |— 1/4 MaterialCheckBox 2131296332
      |— 2/4 MaterialTextView 2131296547
      |— 3/4 MaterialTextView 2131296531
      |— 4/4 AppCompatImageButton 2131296462
    |— ...
    |— 74/74 RelativeLayout 2131296449
      |— 1/4 MaterialCheckBox 2131296332
      |— 2/4 MaterialTextView 2131296547
      |— 3/4 MaterialTextView 2131296531
      |— 4/4 AppCompatImageButton 2131296462
|— 2/4 MaterialTextView 2131296445
|— 3/4 FloatingActionButton 2131296374
|— 4/4 MaterialTextView 2131296363
like image 1
ManuelTS Avatar answered Oct 21 '22 17:10

ManuelTS