摘要
1 前言
2 AS 规范
3 命名规范
4 代码样式规范
5 资源文件规范
6 版本统一规范
7 第三方库规范
8 注释规范
9 测试规范
10 其他的一些规范
1 前言
为了有利于项目维护、增强代码可读性、提升 Code Review 效率以及规范团队安卓开发,故提出以下安卓开发规范,该规范结合本人多年的开发经验并吸取多家之精华,可谓是本人的呕心沥血之作,称其为当前最完善的安卓开发规范一点也不为过,如有更好建议,欢迎到 GitHub 提 issue,原文地址:Android 开发规范(完结版)。相关 Demo,可以查看我的 Android 开发工具类集合项目:Android 开发人员不得不收集的代码。后续可能会根据该规范出一个 CheckStyle 插件来检查是否规范,当然也支持在 CI 上运行。
2 AS 规范
工欲善其事,必先利其器。
尽量使用最新的稳定版的 IDE 进行开发;
编码格式统一为UTF-8;
编辑完 .java、.xml 等文件后一定要格式化,格式化,格式化(如果团队有公共的样式包,那就遵循它,否则统一使用 AS 默认模板即可);
删除多余的 import,减少警告出现,可利用 AS 的 Optimize Imports(Settings -> Keymap -> Optimize Imports)快捷键;
Android 开发者工具可以参考这里:Android 开发者工具;
3 命名规范
代码中的命名严禁使用拼音与英文混合的方式,更不允许直接使用中文的方式。正确的英文拼写和语法可以让阅读者易于理解,避免歧义。
注意:即使纯拼音命名方式也要避免采用。但alibaba、taobao、youku、hangzhou等国际通用的名称,可视同英文。
3.1 包名
包名全部小写,连续的单词只是简单地连接起来,不使用下划线,采用反域名命名规则,全部使用小写字母。一级包名是顶级域名,通常为com、edu、gov、net、org等,二级包名为公司名,三级包名根据应用进行命名,后面就是对包名的划分了,关于包名的划分,推荐采用 PBF(按功能分包 Package By Feature),一开始我们采用的也是 PBL(按层分包 Package By Layer),很坑爹。PBF 可能不是很好区分在哪个功能中,不过也比 PBL 要好找很多,且 PBF 与 PBL 相比较有如下优势:
package 内高内聚,package 间低耦合
哪块要添新功能,只改某一个 package 下的东西。
PBL 降低了代码耦合,但带来了 package 耦合,要添新功能,需要改 model、dbHelper、view、service 等等,需要改动好几个 package 下的代码,改动的地方越多,越容易产生新问题,不是吗?
PBF 的话 featureA 相关的所有东西都在 featureA 包,feature 内高内聚、高度模块化,不同 feature 之间低耦合,相关的东西都放在一起,还好找。
package 有私有作用域(package-private scope)
你负责开发这块功能,这个目录下所有东西都是你的。
PBL 的方式是把所有工具方法都放在 util 包下,小张开发新功能时候发现需要一个 xxUtil,但它又不是通用的,那应该放在哪里?没办法,按照分层原则,我们还得放在 util 包下,好像不太合适,但放在其它包更不合适,功能越来越多,util 类也越定义越多。后来小李负责开发一块功能时发现需要一个 xxUtil,同样不通用,去 util 包一看,怎么已经有了,而且还没法复用,只好放弃 xx 这个名字,改为 xxxUtil……,因为 PBL 的 package 没有私有作用域,每一个包都是 public(跨包方法调用是很平常的事情,每一个包对其它包来说都是可访问的);如果是 PBF,小张的 xxUtil 自然放在 featureA 下,小李的 xxUtil 在 featureB 下,如果觉得 util 好像是通用的,就去 util 包看看要不要把工具方法添进 xxUtil, class 命名冲突没有了。
PBF 的 package 有私有作用域,featureA 不应该访问 featureB 下的任何东西(如果非访问不可,那就说明接口定义有问题)。
很容易删除功能
统计发现新功能没人用,这个版本那块功能得去掉。
如果是 PBL,得从功能入口到整个业务流程把受到牵连的所有能删的代码和 class 都揪出来删掉,一不小心就完蛋。
如果是 PBF,好说,先删掉对应包,再删掉功能入口(删掉包后入口肯定报错了),完事。
高度抽象
解决问题的一般方法是从抽象到具体,PBF 包名是对功能模块的抽象,包内的 class 是实现细节,符合从抽象到具体,而 PBL 弄反了。
PBF 从确定 AppName 开始,根据功能模块划分 package,再考虑每块的具体实现细节,而 PBL 从一开始就要考虑要不要 dao 层,要不要 com 层等等。
只通过 class 来分离逻辑代码
PBL 既分离 class 又分离 package,而 PBF 只通过 class 来分离逻辑代码。
没有必要通过 package 分离,因为 PBL 中也可能出现尴尬的情况:
├──service
├──MainService.java
按照 PBL, service 包下的所有东西都是 Service,应该不需要 Service 后缀,但实际上通常为了方便,直接 import service 包,Service 后缀是为了避免引入的 class 和当前包下的 class 命名冲突,当然,不用后缀也可以,得写清楚包路径,比如new com.domain.service.MainService(),麻烦;而 PBF 就很方便,无需 import,直接new MainService()即可。
package 的大小有意义了
PBL 中包的大小无限增长是合理的,因为功能越添越多,而 PBF 中包太大(包里 class 太多)表示这块需要重构(划分子包)。
如要知道更多好处,可以查看这篇博文:Package by features, not layers,当然,我们大谷歌也有相应的 Sample:todo-mvp,其结构如下所示,很值得学习。
com└──example└──android└──architecture└──blueprints└──todoapp├──BasePresenter.java├──BaseView.java├──addedittask│ ├──AddEditTaskActivity.java│ ├──AddEditTaskContract.java│ ├──AddEditTaskFragment.java│ └──AddEditTaskPresenter.java├──data│ ├──Task.java│ └──source│ ├──TasksDataSource.java│ ├──TasksRepository.java│ ├──local│ │ ├──TasksDbHelper.java│ │ ├──TasksLocalDataSource.java│ │ └──TasksPersistenceContract.java│ └──remote│ └──TasksRemoteDataSource.java├──statistics│ ├──StatisticsActivity.java│ ├──StatisticsContract.java│ ├──StatisticsFragment.java│ └──StatisticsPresenter.java├──taskdetail│ ├──TaskDetailActivity.java│ ├──TaskDetailContract.java│ ├──TaskDetailFragment.java│ └──TaskDetailPresenter.java├──tasks│ ├──ScrollChildSwipeRefreshLayout.java│ ├──TasksActivity.java│ ├──TasksContract.java│ ├──TasksFilterType.java│ ├──TasksFragment.java│ └──TasksPresenter.java└──util├──ActivityUtils.java├──EspressoIdlingResource.java└──SimpleCountingIdlingResource.java
参考以上的代码结构,按功能分包具体可以这样做:
com└── domain └── app ├── App.java 定义 Application 类 ├── Config.java 定义配置数据(常量) ├── base 基础组件 ├── custom_view 自定义视图 ├── data 数据处理 │ ├── DataManager.java 数据管理器, │ ├── local 来源于本地的数据,比如 SP,Database,File │ ├── model 定义 model(数据结构以及getter/setter、compareTo、equals 等等,不含复杂操作) │ └── remote 来源于远端的数据 ├── feature 功能 │ ├── feature0 功能0│ │ ├── feature0Activity.java │ │ ├── feature0Fragment.java │ │ ├── xxAdapter.java │ │ └── ... 其他class│ └── ...其他功能 ├── injection 依赖注入 ├── util 工具类 └── widget 小部件
3.2 类名
类名都以UpperCamelCase风格编写。
类名通常是名词或名词短语,接口名称有时可能是形容词或形容词短语。现在还没有特定的规则或行之有效的约定来命名注解类型。
名词,采用大驼峰命名法,尽量避免缩写,除非该缩写是众所周知的, 比如 HTML、URL,如果类名称中包含单词缩写,则单词缩写的每个字母均应大写。
类描述例如
Activity类Activity为后缀标识欢迎页面类WelcomeActivity
Adapter类Adapter为后缀标识新闻详情适配器NewsDetailAdapter
解析类Parser为后缀标识首页解析类HomePosterParser
工具方法类Utils或Manager为后缀标识线程池管理类:ThreadPoolManager
日志工具类:LogUtils(Logger也可)
打印工具类:PrinterUtils
数据库类以DBHelper后缀标识新闻数据库:NewsDBHelper
Service类以Service为后缀标识时间服务TimeService
BroadcastReceiver类以Receiver为后缀标识推送接收JPushReceiver
ContentProvider类以Provider为后缀标识ShareProvider
自定义的共享基础类以Base开头BaseActivity,BaseFragment
测试类的命名以它要测试的类的名称开始,以 Test 结束。例如:HashTest或HashIntegrationTest。
接口(interface):命名规则与类一样采用大驼峰命名法,多以 able 或 ible 结尾,如interface Runnable、interface Accessible。
注意:如果项目采用 MVP,所有 Model、View、Presenter 的接口都以 I 为前缀,不加后缀,其他的接口采用上述命名规则。
3.3 方法名
方法名都以lowerCamelCase风格编写。
方法名通常是动词或动词短语。
方法说明
initXX()初始化相关方法,使用 init 为前缀标识,如初始化布局initView()
isXX(),checkXX()方法返回值为 boolean 型的请使用 is/check 为前缀标识
getXX()返回某个值的方法,使用 get 为前缀标识
setXX()设置某个属性值
handleXX(),processXX()对数据进行处理的方法
displayXX(),showXX()弹出提示框和提示信息,使用 display/show 为前缀标识
updateXX()更新数据
saveXX(),insertXX()保存或插入数据
resetXX()重置数据
clearXX()清除数据
removeXX(),deleteXX()移除数据或者视图等,如removeView()
drawXX()绘制数据或效果相关的,使用 draw 前缀标识
3.4 常量名
常量名命名模式为CONSTANT_CASE,全部字母大写,用下划线分隔单词。那到底什么算是一个常量?
每个常量都是一个static final字段,但不是所有static final字段都是常量。在决定一个字段是否是一个常量时,得考虑它是否真的感觉像是一个常量。例如,如果观测任何一个该实例的状态是可变的,则它几乎肯定不会是一个常量。只是永远不打算改变的对象一般是不够的,它要真的一直不变才能将它示为常量。
// ConstantsstaticfinalintNUMBER =5;staticfinalImmutableListNAMES = ImmutableList.of("Ed","Ann");staticfinalJoiner COMMA_JOINER = Joiner.on(',');// because Joiner is immutablestaticfinalSomeMutableType[] EMPTY_ARRAY = {};enumSomeEnum { ENUM_CONSTANT }// Not constantsstaticString nonFinal ="non-final";finalString nonStatic ="non-static";staticfinalSetmutableCollection =newHashSet();staticfinalImmutableSetmutableElements = ImmutableSet.of(mutable);staticfinalLogger logger = Logger.getLogger(MyClass.getName());staticfinalString[] nonEmptyArray = {"these","can","change"};
3.5 非常量字段名
非常量字段名以lowerCamelCase风格的基础上改造为如下风格:基本结构为scope{Type0}VariableName{Type1}、type0VariableName{Type1}、variableName{Type1}。
说明:{}中的内容为可选。
注意:所有的 VO(值对象)统一采用标准的 lowerCamelCase 风格编写,所有的 DTO(数据传输对象)就按照接口文档中定义的字段名编写。
3.5.1 scope(范围)
非公有,非静态字段命名以m开头。
静态字段命名以s开头。
其他字段以小写字母开头。
例如:
publicclassMyClass{publicintpublicField;privatestaticMyClass sSingleton;intmPackagePrivate;privateintmPrivate;protectedintmProtected;}
使用 1 个字符前缀来表示作用范围,1 个字符的前缀必须小写,前缀后面是由表意性强的一个单词或多个单词组成的名字,而且每个单词的首写字母大写,其它字母小写,这样保证了对变量名能够进行正确的断句。
3.5.2 Type0(控件类型)
考虑到 Android 众多的 UI 控件,为避免控件和普通成员变量混淆以及更好地表达意思,所有用来表示控件的成员变量统一加上控件缩写作为前缀(具体见附录UI 控件缩写表)。
例如:mIvAvatar、rvBooks、flContainer。
3.5.3 VariableName(变量名)
变量名中可能会出现量词,我们需要创建统一的量词,它们更容易理解,也更容易搜索。
例如:mFirstBook、mPreBook、curBook。
量词列表量词后缀说明
First一组变量中的第一个
Last一组变量中的最后一个
Next一组变量中的下一个
Pre一组变量中的上一个
Cur一组变量中的当前变量
3.5.4 Type1(数据类型)
对于表示集合或者数组的非常量字段名,我们可以添加后缀来增强字段的可读性,比如:
集合添加如下后缀:List、Map、Set。
数组添加如下后缀:Arr。
例如:mIvAvatarList、userArr、firstNameSet。
注意:如果数据类型不确定的话,比如表示的是很多书,那么使用其复数形式来表示也可,例如mBooks。
3.6 参数名
参数名以lowerCamelCase风格编写,参数应该避免用单个字符命名。
3.7 局部变量名
局部变量名以lowerCamelCase风格编写,比起其它类型的名称,局部变量名可以有更为宽松的缩写。
虽然缩写更宽松,但还是要避免用单字符进行命名,除了临时变量和循环变量。
即使局部变量是final和不可改变的,也不应该把它示为常量,自然也不能用常量的规则去命名它。
3.8 临时变量
临时变量通常被取名为i、j、k、m和n,它们一般用于整型;c、d、e,它们一般用于字符型。 如:for (int i = 0; i < len; i++)。
3.9 类型变量名
类型变量可用以下两种风格之一进行命名:
单个的大写字母,后面可以跟一个数字(如:E,T,X,T2)。
以类命名方式(参考3.2 类名),后面加个大写的 T(如:RequestT,FooBarT)。
更多还可参考:阿里巴巴 Java 开发手册
4 代码样式规范
4.1 使用标准大括号样式
左大括号不单独占一行,与其前面的代码位于同一行:
classMyClass{intfunc(){if(something) {// ...}elseif(somethingElse) {// ...}else{// ...} }}
我们需要在条件语句周围添加大括号。例外情况:如果整个条件语句(条件和主体)适合放在同一行,那么您可以(但不是必须)将其全部放在一行上。例如,我们接受以下样式:
if(condition) { body();}
同样也接受以下样式:
if(condition) body();
但不接受以下样式:
if(condition) body();// bad!
4.2 编写简短方法
在可行的情况下,尽量编写短小精炼的方法。我们了解,有些情况下较长的方法是恰当的,因此对方法的代码长度没有做出硬性限制。如果某个方法的代码超出 40 行,请考虑是否可以在不破坏程序结构的前提下对其拆解。
4.3 类成员的顺序
这并没有唯一的正确解决方案,但如果都使用一致的顺序将会提高代码的可读性,推荐使用如下排序:
常量
字段
构造函数
重写函数和回调
公有函数
私有函数
内部类或接口
例如:
publicclassMainActivityextendsActivity{privatestaticfinalString TAG = MainActivity.class.getSimpleName();privateString mTitle;privateTextView mTextViewTitle;@OverridepublicvoidonCreate(){ ... }publicvoidsetTitle(String title){ mTitle = title; }privatevoidsetUpView(){ ... }staticclassAnInnerClass{ }}
如果类继承于 Android 组件(例如Activity或Fragment),那么把重写函数按照他们的生命周期进行排序是一个非常好的习惯,例如,Activity实现了onCreate()、onDestroy()、onPause()、onResume(),它的正确排序如下所示:
publicclassMainActivityextendsActivity{//Order matches Activity lifecycle@OverridepublicvoidonCreate(){}@OverridepublicvoidonResume(){}@OverridepublicvoidonPause(){}@OverridepublicvoidonDestroy(){}}
4.4 函数参数的排序
在 Android 开发过程中,Context在函数参数中是再常见不过的了,我们最好把Context作为其第一个参数。
正相反,我们把回调接口应该作为其最后一个参数。
例如:
// Context always goes firstpublicUserloadUser(Context context,intuserId);// Callbacks always go lastpublicvoidloadUserAsync(Context context,intuserId, UserCallback callback);
4.5 字符串常量的命名和值
Android SDK 中的很多类都用到了键值对函数,比如SharedPreferences、Bundle、Intent,所以,即便是一个小应用,我们最终也不得不编写大量的字符串常量。
当时用到这些类的时候,我们必须将它们的键定义为static final字段,并遵循以下指示作为前缀。
类字段名前缀
SharedPreferencesPREF_
BundleBUNDLE_
Fragment ArgumentsARGUMENT_
Intent ExtraEXTRA_
Intent ActionACTION_
说明:虽然Fragment.getArguments()得到的也是Bundle,但因为这是Bundle的常用用法,所以特意为此定义一个不同的前缀。
例如:
// 注意:字段的值与名称相同以避免重复问题staticfinalString PREF_EMAIL ="PREF_EMAIL";staticfinalString BUNDLE_AGE ="BUNDLE_AGE";staticfinalString ARGUMENT_USER_ID ="ARGUMENT_USER_ID";// 与意图相关的项使用完整的包名作为值的前缀staticfinalString EXTRA_SURNAME ="com.myapp.extras.EXTRA_SURNAME";staticfinalString ACTION_OPEN_USER ="com.myapp.action.ACTION_OPEN_USER";
4.6 Activities 和 Fragments 的传参
当Activity或Fragment传递数据通过Intent或Bundle时,不同值的键须遵循上一条所提及到的。
当Activity或Fragment启动需要传递参数时,那么它需要提供一个public static的函数来帮助启动或创建它。
这方面,AS 已帮你写好了相关的 Live Templates,启动相关Activity的只需要在其内部输入starter即可生成它的启动器,如下所示:
publicstaticvoidstart(Context context, User user){ Intent starter =newIntent(context, MainActivity.class); starter.putParcelableExtra(EXTRA_USER, user); context.startActivity(starter);}
同理,启动相关Fragment在其内部输入newInstance即可,如下所示:
publicstaticMainFragmentnewInstance(User user){ Bundle args =newBundle(); args.putParcelable(ARGUMENT_USER, user); MainFragment fragment =newMainFragment(); fragment.setArguments(args);returnfragment;}
注意:这些函数需要放在onCreate()之前的类的顶部;如果我们使用了这种方式,那么extras和arguments的键应该是private的,因为它们不再需要暴露给其他类来使用。
4.7 行长限制
代码中每一行文本的长度都应该不超过 100 个字符。虽然关于此规则存在很多争论,但最终决定仍是以 100 个字符为上限,如果行长超过了 100(AS 窗口右侧的竖线就是设置的行宽末尾 ),我们通常有两种方法来缩减行长。
提取一个局部变量或方法(最好)。
使用换行符将一行换成多行。
不过存在以下例外情况:
如果备注行包含长度超过 100 个字符的示例命令或文字网址,那么为了便于剪切和粘贴,该行可以超过 100 个字符。
导入语句行可以超出此限制,因为用户很少会看到它们(这也简化了工具编写流程)。
4.7.1 换行策略
这没有一个准确的解决方案来决定如何换行,通常不同的解决方案都是有效的,但是有一些规则可以应用于常见的情况。
4.7.1.1 操作符的换行
除赋值操作符之外,我们把换行符放在操作符之前,例如:
intlongName = anotherVeryLongVariable + anEvenLongerOne - thisRidiculousLongOne + theFinalOne;
赋值操作符的换行我们放在其后,例如:
intlongName = anotherVeryLongVariable + anEvenLongerOne - thisRidiculousLongOne + theFinalOne;
4.7.1.2 函数链的换行
当同一行中调用多个函数时(比如使用构建器时),对每个函数的调用应该在新的一行中,我们把换行符插入在.之前。
例如:
Picasso.with(context).load("https://blankj.com/images/avatar.jpg").into(ivAvatar);
我们应该使用如下规则:
Picasso.with(context) .load("https://blankj.com/images/avatar.jpg") .into(ivAvatar);
4.7.1.3 多参数的换行
当一个方法有很多参数或者参数很长的时候,我们应该在每个,后面进行换行。
比如:
loadPicture(context,"https://blankj.com/images/avatar.jpg", ivAvatar,"Avatar of the user", clickListener);
我们应该使用如下规则:
loadPicture(context,"https://blankj.com/images/avatar.jpg", ivAvatar,"Avatar of the user", clickListener);
4.7.1.4 RxJava 链式的换行
RxJava 的每个操作符都需要换新行,并且把换行符插入在.之前。
例如:
publicObservablesyncLocations(){returnmDatabaseHelper.getAllLocations() .concatMap(newFunc1>() {@OverridepublicObservable call(Location location) {returnmRetrofitService.getLocation(location.id); } }) .retry(newFunc2() {@OverridepublicBooleancall(Integer numRetries, Throwable throwable){returnthrowableinstanceofRetrofitError; } });}
5 资源文件规范
资源文件命名为全部小写,采用下划线命名法。
如果想对资源文件进行分包可以参考我这篇文章:Android Studio 下对资源进行分包。
5.1 动画资源文件(anim/ 和 animator/)
安卓主要包含属性动画和视图动画,其视图动画包括补间动画和逐帧动画。属性动画文件需要放在res/animator/目录下,视图动画文件需放在res/anim/目录下。
命名规则:{模块名_}逻辑名称。
说明:{}中的内容为可选,逻辑名称可由多个单词加下划线组成。
例如:refresh_progress.xml、market_cart_add.xml、market_cart_remove.xml。
如果是普通的补间动画或者属性动画,可采用:动画类型_方向的命名方式。
例如:
名称说明
fade_in淡入
fade_out淡出
push_down_in从下方推入
push_down_out从下方推出
push_left推向左方
slide_in_from_top从头部滑动进入
zoom_enter变形进入
slide_in滑动进入
shrink_to_middle中间缩小
5.2 颜色资源文件(color/)
专门存放颜色相关的资源文件。
命名规则:类型_逻辑名称。
例如:sel_btn_font.xml。
颜色资源也可以放于res/drawable/目录,引用时则用@drawable来引用,但不推荐这么做,最好还是把两者分开。
5.3 图片资源文件(drawable/ 和 mipmap/)
res/drawable/目录下放的是位图文件(.png、.9.png、.jpg、.gif)或编译为可绘制对象资源子类型的 XML 文件,而res/mipmap/目录下放的是不同密度的启动图标,所以res/mipmap/只用于存放启动图标,其余图片资源文件都应该放到res/drawable/目录下。
命名规则:类型{_模块名}_逻辑名称、类型{_模块名}_颜色。
说明:{}中的内容为可选;类型可以是可绘制对象资源类型,也可以是控件类型(具体见附录UI 控件缩写表);最后可加后缀_small表示小图,_big表示大图。
例如:
名称说明
btn_main_about.png主页关于按键类型_模块名_逻辑名称
btn_back.png返回按键类型_逻辑名称
divider_maket_white.png商城白色分割线类型_模块名_颜色
ic_edit.png编辑图标类型_逻辑名称
bg_main.png主页背景类型_逻辑名称
btn_red.png红色按键类型_颜色
btn_red_big.png红色大按键类型_颜色
ic_head_small.png小头像图标类型_逻辑名称
bg_input.png输入框背景类型_逻辑名称
divider_white.png白色分割线类型_颜色
bg_main_head.png主页头部背景类型_模块名_逻辑名称
def_search_cell.png搜索页面默认单元图片类型_模块名_逻辑名称
ic_more_help.png更多帮助图标类型_逻辑名称
divider_list_line.png列表分割线类型_逻辑名称
sel_search_ok.xml搜索界面确认选择器类型_模块名_逻辑名称
shape_music_ring.xml音乐界面环形形状类型_模块名_逻辑名称
如果有多种形态,如按钮选择器:sel_btn_xx.xml,采用如下命名:
名称说明
sel_btn_xx作用在btn_xx上的selector
btn_xx_normal默认状态效果
btn_xx_pressedstate_pressed点击效果
btn_xx_focusedstate_focused聚焦效果
btn_xx_disabledstate_enabled不可用效果
btn_xx_checkedstate_checked选中效果
btn_xx_selectedstate_selected选中效果
btn_xx_hoveredstate_hovered悬停效果
btn_xx_checkablestate_checkable可选效果
btn_xx_activatedstate_activated激活效果
btn_xx_window_focusedstate_window_focused窗口聚焦效果
注意:使用 Android Studio 的插件 SelectorChapek 可以快速生成 selector,前提是命名要规范。
5.4 布局资源文件(layout/)
命名规则:类型_模块名、类型{_模块名}_逻辑名称。
说明:{}中的内容为可选。
例如:
名称说明
activity_main.xml主窗体类型_模块名
activity_main_head.xml主窗体头部类型_模块名_逻辑名称
fragment_music.xml音乐片段类型_模块名
fragment_music_player.xml音乐片段的播放器类型_模块名_逻辑名称
dialog_loading.xml加载对话框类型_逻辑名称
ppw_info.xml信息弹窗(PopupWindow)类型_逻辑名称
item_main_song.xml主页歌曲列表项类型_模块名_逻辑名称
5.5 菜单资源文件(menu/)
菜单相关的资源文件应放在该目录下。
命名规则:{模块名_}逻辑名称
说明:{}中的内容为可选。
例如:main_drawer.xml、navigation.xml。
5.6 values 资源文件(values/)
values/资源文件下的文件都以s结尾,如attrs.xml、colors.xml、dimens.xml,起作用的不是文件名称,而是