Есть ли Android-инструмент для поиска имени макета для работающего приложения?

Фон

Меня недавно наняли для работы с очень большой программой (только два вида деятельности, около ста фрагментов и несколько сотен макетов). Кроме того, большая часть содержимого макетов (изображений и текста), а также порядок их отображения динамически определяются через веб-API компании.

К сожалению, нет документации. Нет карты, нада. Компания наняла стороннего производителя для создания этого приложения задолго до того, как у них появились программисты на Android. И качество кода в лучшем случае плохое (даже имена переменных сбивают с толку и противоречивы).

Следовательно, я трачу около 70% - 90% своего времени на простой поиск макетов и кода, просто чтобы изменить фон кнопки.

Вопрос

Есть ли инструмент, который можно запустить (возможно, в отладчике Android Studio?), Который может каким-то образом выплевывать имена файлов макета, которые отображаются в данный момент?

Мои начальники часто говорят что-то вроде: "Измените текстуру фона с черного на светло-серый". И я думаю: изменение тривиально, но поиск XML файла может занять час.

--Обновить--

Проект, который породил этот вопрос, больше не находится под моим контролем, поэтому этот вопрос стал спорным. Но, похоже, это общая проблема, поэтому я оставляю этот вопрос открытым. Возможно, в будущем появится какой-нибудь инструмент/решение. Я надеюсь, что эта тема будет полезна другим программистам в этой ситуации.

Ответ 1

Инструмент просмотра иерархии

В Android-мониторе устройства есть кнопка, которую вы можете щелкнуть по названию "Иерархия дампа для пользовательского интерфейса".

Это откроет средство просмотра иерархии пользовательского интерфейса в Device Monitor, которое вы можете использовать для просмотра идентификаторов ресурсов для каждого из ваших представлений.

Хотя это фактически не дает вам имя завышенного XML файла, это может быть очень полезно. Если вам повезет, вы можете даже grep для идентификаторов ресурсов сузить поиск для тех XML файлов, на которые нужно смотреть.

И если вы наведете над заголовком окна просмотра, он покажет вам путь к фактическому файлу XML, который он создал. Это способ найти все идентификаторы ресурсов в одном месте.


Дополнительная информация о Hierarchy Viewer в случае, если это помогает:

Средство просмотра иерархии позволяет вам отлаживать и оптимизировать пользователя интерфейс. Он обеспечивает визуальное представление макета View иерархии (вид макета) и увеличенного инспектора дисплея (Pixel Perfect View).


Создайте пользовательский макет Inflater в коде

Здесь сообщение в блоге, в котором есть что-то интересное - настраиваемый накладной макет, который перехватывает все инфляции просмотров:

Возможно, можно использовать этот примерный код для перехвата вызовов раздувания и получения идентификатора ресурса XML файла:

В этот момент вам нужно будет превратить идентификатор ресурса в полезное имя. Цитируя этот вопрос (Как получить имя ресурса из идентификатора ресурса), вы можете использовать:

getResources().getResourceEntryName(int resid);

Добавьте несколько журналов, и это может дать вам каждый XML файл, поскольку он накачивается.

Это может быть очень хороший способ документировать весь проект.

Ответ 2

Существует также приложение Developer Assistant для Android, которое может проверять иерархию представлений во время выполнения и затем отображать наиболее вероятные имена макетов, видимые на экране. Эвристика включена, поэтому она не будет работать на 100% с точностью, но все же может быть полезна и работает полностью в автономном режиме (раскрытие: я сделал это приложение).

Пример:

enter image description here

Ответ 3

Возможно, "Android/sdk/tools/uiautomatorviewer" может вам помочь. Он может принимать дамп текущего экрана с устройства и показывает ваши представления/макеты с их деталями - также с идентификатором

Ответ 4

Вы можете попробовать этот инструмент, который я создал: https://github.com/nekocode/ResourceInspector

Он может проверять все используемые файлы макета текущей деятельности.

Ответ 5

Возможно, это слишком поздний ответ, но разные программисты могут воспользоваться моим ответом.

Библиотека Facebook Stetho - замечательный инструмент для просмотра инспекций для запуска приложений. Библиотека Stetho работает как расширение Chrome, и оно будет схоже для многих программистов.

Также, чтобы увидеть HTTP-запрос/ответы, очень легко использовать Stetho. Я абсолютно совет, который каждый программист попробует.

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

Ответ 6

Вдохновленный @nekocode ResourceInspector, я выяснил, как показать имя ресурса макета на скриншоте, снятом инспектором макета AS. show the layout resource name in the screenshot captured by AS's layout inspector

Сначала создайте класс 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;
    }
}

Затем создайте вспомогательный класс

    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) {

                }
            });
        }
    }

}

Наконец, вызовите LayoutIndicatorHelper.init в вашем Application onCreate

    public class MyApplication extends Application {

    @Override
    public void onCreate() {
        super.onCreate();
        LayoutIndicatorHelper.init(this);
    }
}

Ответ 7

Начиная с версии Android 3.0 и выше, инструмент просмотра иерархии устарел. Существует автономный LayoutInspector. Вы можете получить к нему доступ, нажав на инструменты в верхней панели Android Studio.