Is there an Android tool to find the name of a layout for a running app?

后端 未结 7 1596
刺人心
刺人心 2021-02-07 03:37

Background

I have been recently hired to maintain a very large program (only two Activities, about a hundred Fragments, and several hundred layouts).

相关标签:
7条回答
  • 2021-02-07 04:03

    Hierarchy Viewer tool

    In the Android Device Monitor, there is a button you can click on called "Dump View Hierarchy for UI Automator".

    This will open a UI Hierarchy viewer in the Device Monitor, which you can use to see the resource IDs of each of your views.

    While this does not actually give you the name of the inflated XML file, it could be very useful. If you are lucky, you can even grep for the resource IDs to narrow down the search for which XML files to look at.

    And if you hover over the title of the viewer window, it will show you the path to the actual XML file that it created. This is a way to find all the resource IDs in one place.


    Some more info on Hierarchy Viewer in case it helps:

    • How to get to Hierarchy View while running the app on my device connected to android studio
    • or straight from the Android Developer docs

    The Hierarchy Viewer allows you to debug and optimize your user interface. It provides a visual representation of the layout's View hierarchy (the Layout View) and a magnified inspector of the display (the Pixel Perfect View).


    Create a custom Layout Inflater in code

    Here's a blog post that contains something interesting - a custom layout inflater that intercepts all view inflation:

    • http://android-activities.blogspot.co.za/2014/03/add-custom-behavior-to-standard-android.html

    It may be possible to use that sample code to intercept the inflate calls, and receive the resource ID of the XML file:

    • LayoutInflator.inflate() on Android Developer docs

    At this point, you will need to turn the resource id into a useful name. Quoting from this question (How to get Resource Name from Resource id), you can use:

    getResources().getResourceEntryName(int resid);
    

    Add some logging and this may give you each XML file as it is being inflated.

    This could be quite a nice way to document your entire project.

    0 讨论(0)
  • 2021-02-07 04:04

    Maybe it is a too late answer but different programmers can take advantage of my answer.

    Facebook's Stetho library is wonderful tool to see inspects for running apps. Stetho library works like a Chrome extension and it will similar for many programmers.

    Also to see HTTP request/responses are very easy by using Stetho. I absolutely advice every Android programmer try.

    http://facebook.github.io/stetho/

    0 讨论(0)
  • 2021-02-07 04:09

    Inspired by @nekocode ResourceInspector, I figure out how to show the layout resource name in the screenshot captured by AS's layout inspector.

    Firstly create a class LayoutIndicatorInflater

    public class LayoutIndicatorInflater extends LayoutInflater {
    
        private LayoutInflater mOriginalInflater;
        private String mAppPackageName;
    
        protected LayoutIndicatorInflater(LayoutInflater original, Context newContext) {
            super(original, newContext);
            mOriginalInflater = original;
            mAppPackageName = getContext().getPackageName();
        }
    
        @Override
        public LayoutInflater cloneInContext(Context newContext) {
            return new LayoutIndicatorInflater(mOriginalInflater.cloneInContext(newContext), newContext);
        }
    
        @Override
        public void setFactory(Factory factory) {
            super.setFactory(factory);
            mOriginalInflater.setFactory(factory);
        }
    
        @Override
        public void setFactory2(Factory2 factory) {
            super.setFactory2(factory);
            mOriginalInflater.setFactory2(factory);
        }
    
        @Override
        public View inflate(int resourceId, ViewGroup root, boolean attachToRoot) {
            Resources res = getContext().getResources();
    
            String packageName = "";
            try {
                packageName = res.getResourcePackageName(resourceId);
            } catch (Exception e) {}
    
    
            String resName = "";
            try {
                resName = res.getResourceEntryName(resourceId);
            } catch (Exception e) {}
    
            View view = mOriginalInflater.inflate(resourceId, root, attachToRoot);
    
            if (!mAppPackageName.equals(packageName)) {
                return view;
            }
    
            View targetView = view;
            if (root != null && attachToRoot) {
                targetView = root.getChildAt(root.getChildCount() - 1);
            }
    
            targetView.setContentDescription("layout res:" + resName);
    
            if (targetView instanceof ViewGroup) {
                ViewGroup viewGroup = (ViewGroup) targetView;
                for (int i = 0; i < viewGroup.getChildCount(); i++) {
                    View child = viewGroup.getChildAt(i);
                    if (TextUtils.isEmpty(child.getContentDescription())) {
                        child.setContentDescription("layout res:" + resName);
                    }
                }
            }
    
            return view;
        }
    }
    

    Then create a helper class

        public class LayoutIndicatorHelper {
    
        public static void init(Application application) {
            if (BuildConfig.DEBUG) {
                application.registerActivityLifecycleCallbacks(new Application.ActivityLifecycleCallbacks() {
                    @Override
                    public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
                        try {
                            Field inflaterField = ContextThemeWrapper.class.getDeclaredField("mInflater");
                            inflaterField.setAccessible(true);
                            LayoutInflater inflater = (LayoutInflater) inflaterField.get(activity);
                            LayoutInflater proxyInflater = null;
                            if (inflater != null) {
                                proxyInflater = new LayoutIndicatorInflater(inflater, activity);
                                inflaterField.set(activity, proxyInflater);
                            }
    
                            Class phoneWindowClass = Class.forName("com.android.internal.policy.PhoneWindow");
                            Field phoneWindowInflater = phoneWindowClass.getDeclaredField("mLayoutInflater");
                            phoneWindowInflater.setAccessible(true);
                            inflater = (LayoutInflater) phoneWindowInflater.get(activity.getWindow());
                            if (inflater != null && proxyInflater != null) {
                                phoneWindowInflater.set(activity.getWindow(), proxyInflater);
                            }
                        } catch (Exception e) {
                            e.printStackTrace();
                        }
                    }
    
                    @Override
                    public void onActivityStarted(Activity activity) {
    
                    }
    
                    @Override
                    public void onActivityResumed(Activity activity) {
    
                    }
    
                    @Override
                    public void onActivityPaused(Activity activity) {
    
                    }
    
                    @Override
                    public void onActivityStopped(Activity activity) {
    
                    }
    
                    @Override
                    public void onActivitySaveInstanceState(Activity activity, Bundle outState) {
    
                    }
    
                    @Override
                    public void onActivityDestroyed(Activity activity) {
    
                    }
                });
            }
        }
    
    }
    

    Finally call LayoutIndicatorHelper.init in your Application's onCreate

        public class MyApplication extends Application {
    
        @Override
        public void onCreate() {
            super.onCreate();
            LayoutIndicatorHelper.init(this);
        }
    }
    
    0 讨论(0)
  • 2021-02-07 04:13

    There is also the Developer Assistant app for Android which can inspect a view hierarchy at runtime and then display the most probable layout names visible on the screen. The heuristic is involved so it won't work 100% accurate, but still can be helpful and works completely offline (a disclosure: I made this app).

    An example:

    0 讨论(0)
  • 2021-02-07 04:15

    You can try this tool that I created: https://github.com/nekocode/ResourceInspector

    It can inspect all used layout files of current Activity.

    0 讨论(0)
  • 2021-02-07 04:25

    In android 3.0 to onward, the Hierarchy Viewer tool is now deprecated. There is a standalone LayoutInspector. You can access it by clicking on tools from top bar of your Android Studio.

    0 讨论(0)
提交回复
热议问题