我最近被雇用来维护非常大型程序(只有两个活动,大约一百个碎片和几百个布局)。此外,布局(图像和文本)的大部分内容以及布局的显示顺序是通过公司的Web API动态确定的。
不幸的是没有文件。没有地图,纳达。在他们拥有Android程序员之前,该公司聘请了第三方来制作这个应用程序。而且代码的质量最好(甚至变量名称也令人困惑和矛盾)。
因此,我花了大约70% - 90%的时间只是为了改变Button的背景而只是搜索布局和代码。
是否有可以运行的工具(可能在Android Studio的调试器中?),它可以以某种方式吐出当前显示的布局文件的名称?
我的上司经常说“将背景纹理从黑色变为浅灰色”。我在想:改变是微不足道的,但找到 xml文件可能需要一个小时。
产生这个问题的项目不再受我的控制,所以这个问题已经没有实际意义了。但它似乎确实是一个常见问题,所以我将这个问题保持开放。也许将来会出现某种工具/解决方案。我希望这个帖子在那种情况下对其他程序员有用。
答案 0 :(得分:6)
在Android设备监视器中,您可以单击一个名为“用于UI Automator的转储视图层次结构”的按钮。
这将在设备监视器中打开UI层次结构查看器,您可以使用它查看每个视图的资源ID。
虽然这实际上并没有为您提供膨胀的XML文件的名称,但它可能非常有用。如果幸运的话,您甚至可以使用资源ID来缩小搜索XML文件的范围。
如果将鼠标悬停在查看器窗口的标题上,它将显示它创建的实际XML文件的路径。这是一种在一个地方查找所有资源ID的方法。
有关层次结构查看器的更多信息,以防它有用:
Hierarchy Viewer允许您调试和优化用户 接口。它提供了布局视图的直观表示 层次结构(布局视图)和显示器的放大检查器 (Pixel Perfect View)。
这是一篇包含有趣内容的博客文章 - 一个拦截所有观点通胀的自定义布局:
可以使用该示例代码拦截膨胀调用,并接收XML文件的资源ID :
此时,您需要将资源ID 转换为有用的名称。引用这个问题(How to get Resource Name from Resource id),您可以使用:
getResources().getResourceEntryName(int resid);
添加一些日志记录,这可能会为每个XML文件提供充值。
这可能是记录整个项目的一种很好的方式。
答案 1 :(得分:5)
还有一个适用于Android的Developer Assistant应用程序,可以在运行时检查视图层次结构,然后在屏幕上显示最可能的布局名称。涉及到启发式算法,因此它不能100%准确地工作,但仍然可以提供帮助,并且可以完全脱机工作(披露:我制作了此应用)。
一个例子:
答案 2 :(得分:3)
可能的“Android / sdk / tools / uiautomatorviewer”可以帮到你。它可以从设备转储当前屏幕,并显示视图/布局及其详细信息 - 也包括id
答案 3 :(得分:3)
您可以尝试我创建的这个工具: https://github.com/nekocode/ResourceInspector
它可以检查当前活动的所有使用的布局文件。
答案 4 :(得分:2)
受@nekocode ResourceInspector的启发,我想出了如何在AS的布局检查器捕获的屏幕截图中显示布局资源名称。
首先创建一个类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);
}
}
答案 5 :(得分:2)
在android 3.0及更高版本中,不推荐使用 Hierarchy Viewer工具。有一个独立的LayoutInspector
。您可以通过点击 Android Studio 顶部栏中的工具来访问它。
答案 6 :(得分:1)
也许这是一个太晚的答案,但不同的程序员可以利用我的答案。
Facebook的Stetho库是查看运行应用程序检查的绝佳工具。 Stetho库的工作方式类似于Chrome扩展,对许多程序员来说都是类似的。
使用Stetho也很容易看到HTTP请求/响应。 我绝对建议每个Android程序员尝试。