私は最近、very大規模なプログラム(2つのアクティビティ、約100のフラグメント、および数百のレイアウトのみ)を維持するために雇われました。さらに、レイアウトのコンテンツ(画像とテキスト)のほとんど、およびレイアウトが表示される順序は、会社のWebAPIを介して動的に決定されます。
残念ながら、ドキュメントはありません。地図なし、灘。同社は、Androidプログラマーが登場するずっと前に、このアプリを作成するためにサードパーティを雇いました。そして、コードの品質はせいぜい劣っています(変数名でさえ混乱して矛盾しています)。
その結果、ボタンの背景を変更するためだけに、レイアウトとコードを検索するだけで、時間の約70%から90%を費やしています。
現在表示されているレイアウトファイルの名前をなんとかして吐き出すことができるツール(おそらくAndroid Studioのデバッガーで?))はありますか?
上司はよく「背景の質感を黒からライトグレーに変えて」と言っています。そして私は考えています:変更は些細なことですが、findingxmlファイルは1時間かかる可能性があります。
この質問を生成したプロジェクトはもはや私の管理下にないので、この質問は議論の余地があります。しかし、それは一般的な問題のように思われるので、私はこの質問を開いたままにしておきます。おそらく将来、ある種のツール/ソリューションが生まれるでしょう。このスレッドがそのような状況で他のプログラマーに役立つことを願っています。
Developer Assistant アプリAndroid)もあり、実行時にビュー階層を検査して、最も可能性の高いレイアウト名を表示できます。 画面に表示されます。ヒューリスティックが含まれているため、100%正確には機能しませんが、それでも役立つ可能性があり、完全にオフラインで機能します(開示:私はこのアプリを作成しました)。
例:
Android Device Monitorには、「UIAutomatorのダンプビュー階層」と呼ばれるクリックできるボタンがあります。
これにより、デバイスモニターにUI階層ビューアーが開きます。このビューアーを使用して、各ビューのリソースIDを確認できます。
これは実際には膨らんだXMLファイルの名前を与えるものではありませんが、非常に役立つ可能性があります。運が良ければ、リソースIDをgrepして、検索するXMLファイルの検索を絞り込むこともできます。
また、ビューアウィンドウのタイトルにカーソルを合わせると、作成された実際のXMLファイルへのパスが表示されます。これは、すべてのリソースIDを1か所で見つける方法です。
役立つ場合に備えて、HierarchyViewerに関する詳細情報:
Hierarchy Viewerを使用すると、ユーザーインターフェイスをデバッグおよび最適化できます。レイアウトのビュー階層(レイアウトビュー)とディスプレイの拡大インスペクター(ピクセルパーフェクトビュー)を視覚的に表現します。
これは興味深いものを含むブログ投稿です-すべてのビューインフレーションをインターセプトするカスタムレイアウトインフレーター:
そのサンプルコードを使用して、膨張呼び出しをインターセプトし、XMLファイルのリソースIDを受け取ることができる場合があります。
この時点で、リソースIDを有用な名前に変える必要があります。この質問( リソースIDからリソース名を取得する方法 )から引用すると、次を使用できます。
getResources().getResourceEntryName(int resid);
いくつかのログを追加すると、膨張するときに各XMLファイルが得られる場合があります。
これは、プロジェクト全体を文書化するための非常に優れた方法である可能性があります。
私が作成したこのツールを試すことができます: https://github.com/nekocode/ResourceInspector
現在のアクティビティで使用されているすべてのレイアウトファイルを検査できます。
可能な「Android/sdk/tools/uiautomatorviewer」が役に立ちます。デバイスから現在の画面のダンプを取得し、ビュー/レイアウトとその詳細を表示できます-IDも表示されます
Android 3.0以降では、Hierarchy Viewerツールは非推奨になりました。スタンドアロンのLayoutInspector
があります。-をクリックするとアクセスできます。 toolsトップバーからAndroid Studio。
@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) {
}
});
}
}
}
最後に、Application
のonCreate
でLayoutIndicatorHelper.init
を呼び出します
public class MyApplication extends Application {
@Override
public void onCreate() {
super.onCreate();
LayoutIndicatorHelper.init(this);
}
}
答えが遅すぎるかもしれませんが、さまざまなプログラマーが私の答えを利用できます。
FacebookのStethoライブラリは、実行中のアプリの検査を確認するための素晴らしいツールです。 StethoライブラリはChrome拡張機能のように機能し、多くのプログラマーにとって同様です。
また、Stethoを使用すると、HTTPリクエスト/レスポンスを簡単に確認できます。私はすべてのAndroidプログラマーが試すことを絶対にアドバイスします。