Android Lint是SDK Tools 16(ADT 16)之后Google提供的新工具,它是一个代码扫描工具,能够帮助我们识别代码结构存在的问题,比如:

  1. 无效布局,多重嵌套等
  2. 未使用的冗余资源文件
  3. 国际化的问题(未翻译的文本)
  4. 数组资源大小不一致
  5. 图标缺失、重复、错误尺寸
  6. AndroidManifest.xml中的错误

Lint

Lint工具在/AndroidSDK/tools/文件夹下

使用

  • lint <project directory> lint命令后添加工程目录
  • lint --html <project directory> 生成html格式的报告
  • lint --simplehtml <project directory> 生成简单html格式的报告
  • lint --xml <project directory> 生成xml格式的报告
  • lint --check "UnusedResources" <project directory> 清理冗余资源文件
  • lint --show可获得详细问题列表
    • AdapterViewChildren 确保没有在XML文件中定义它的子view
    • OnClick 确保XML文件声明的OnClick的调用函数在代码中实际存在
    • SuspiciousImport 可疑import的检查
    • UsesMinSdkAttributes 检查是否在AndroidManifest.xml文件中定义了minimum SDK 和 target SDK这两个属性
    • WrongViewCast View强转换的检查
    • MissingRegistered 检查manifest文件中声明的类实际是否存在于工程或工程的libraries中
    • NamespaceTypo 命名空间拼写检查
    • Proguard 混淆配置检查
    • ScrollViewCount 检查ScrollViews是否只有一个child
    • ……

自定义属性类型

1.reference

  • 资源类型
1
<attr name="backimg" format="reference"/>

2.color

  • 颜色类型
1
<attr name="textColor" format="color" />

3.boolean

  • 布尔类型
1
<attr name="focusable" format="boolean" />

4.dimension

  • 尺寸类型
1
<attr name="layout_width" format="dimension" />

5.float

  • 浮点类类型
1
<attr name="toAlpha" format="float"/>

6.integer

  • 整数类型
1
<attr name="visible" format="integer" />

7.string

  • 字符串类型
1
<attr name="key" format="string" />

8.fraction

  • 百分比类型
1
<attr name="pivotX" format="fraction" />

9.enum

  • 枚举类型
1
2
3
4
<attr name="orientation">
<enum name="horizontal" value="0" />
<enum name="vertical" value="1" />
</attr>

10.flag

  • 标记类型(可以与或运算)
1
2
3
4
<attr name="mode">
<flag name="stateVisible" value="0x01">
<flag name="statePan" value="0x10">
</attr>

注意

属性可以同时指定多个类型值

1
<attr name="background" format="referce|color" />

闭包(closure)包含了可执行程序(跟方法主体(statements)类似)以及接收(capture)的参数,格式如下

1
2
3
{(parameters) -> return type in
statements
}
  • 闭包可以省略参数的类型和返回值的类型,如果省略了参数类型也要省略in关键字
  • 闭包省略参数,可以使用$0,$1,$2……来引用出现的第一个、第二个、得三个……参数
  • 如果闭包只包含一个表达式,那么表达式就会自动成为该闭包的返回值
1
2
3
4
5
6
7
8
9
10
11
12
13
func1 {
(x:Int,y:Int) -> Int in
return x + y
}

func2 {
(x,y) in
return x + y
}

func3 {return $0 + $1}

func4 { $0 + $1 }

判断是否存在虚拟键

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
private final static String SHOW_NAV_BAR_RES_NAME = "config_showNavigationBar";
public String getNavBarOverride() {
if (Build.VERSION>SDK_INT >= Build.VERSION_CODES.KITKAT) {
WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
try {
// 相当于SystemProperties.get
Class c = Class.forName("android.os.SystemProperties");
Method m = c.getDeclaredMethod("get",String.class);
m.setAccessible(true);
return (String)m.invoke(null,"qemu.hw.mainkeys");
} catch (Throwable e) {
return null;
}
}
}

@TargetApi(14)
private boolean hasNavBar(Context context) {
Resources res = context.getResources();
// resources.getIdenttifier() 方法可以获取指定报名下的资源文件ID,后两个参数表示资源类型和默认报名
int resourceId = res.getIdentifier(SHOW_NAV_BAR_RES_NAME,"bool","android");
if(resourceId != 0){
if ("1".equals(getNavBarOverride())) {
hasNav = false;
} else if ("0".equals(getNavBarOverride())) {
hasNav = true;
}
return hasNav;
} else {
return !ViewConfiguration.get(context).hasPermanentMenuKey();
}
}

获取虚拟键的高度

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
public int getNavigationBarHeight(Context context) {
Resources res = context.getResources();
int result = 0;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
if (hasNavBar(context)) {
String key;
if (context.getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT) {
key = NAV_BAR_HEIGHT_RES_NAME;
} else {
if (!isNavigationAtBottom())
return 0;
key = NAV_BAR_HEIGHT_LANDSCAPE_RES_NAME;
}
return getInternalDimensionSize(res,key);
}
}
return result;
}

private int getInternalDimensionSize(Resources res, String key) {
int result = 0;
int resourceId = res.getIdentifier(key, "dimen", "android");
if (resourceId > 0) {
result = res.getDimensionPixelSize(resourceId);
}
return result;
}

private boolean isNavigationAtBottom() {
WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
float mSmallestWidthDp = getSmallestWidthDp(wm);
return (mSmallestWidthDp >= 600 || mInPortrait);
}

private float getSmallestWidthDp(WindowManager wm) {
DisplayMetrics metrics = new DisplayMetrics();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
wm.getDefaultDisplay().getRealMetrics(metrics);
} else {
//this is not correct, but we don't really care pre-kitkat
wm.getDefaultDisplay().getMetrics(metrics);
}
float widthDp = metrics.widthPixels / metrics.density;
float heightDp = metrics.heightPixels / metrics.density;
return Math.min(widthDp, heightDp);
}

  1. PreferenceScreen 设置页面,可嵌套二级设置页面
  2. PreferenceCategory 设置类,可用Title参数设置标题
  3. CheckBoxPreference 可选设置,可用Title设置标题,用summaryOn和summaryOff参数设置控件选中和未选中时的提示,可用defaultValue设置缺省值
  4. ListPreference 下拉框选择设置,可用Title设置标题,用Summary参数设置说明,点击出现下拉框,用dialogTitle设置下拉框的标题,entries和entryValues分别表示显示的值和代码中获取的真正的值
  5. EditTextPreference 输入框设置,点击输入字符串设置,用Title参数设置标题,Summary参数设置说明,dialogTitle参数设置输入框的标题
  6. RingtonePreference 铃声选择设置,点击后可选择系统铃声,用Title参数设置标题,Summary参数设置说明,dialogTitle参数设置铃声选择的标题