您的位置 首页 科技

荐一份 Android 代码规范建议文档

本文作者作者: Android轮子哥 https://github.com/getActivity/AndroidCodeStandard 之前推送过: Android 代码规范大全 , 可以一起学习。

本文作者

作者: Android轮子哥

https://github.com/getActivity/AndroidCodeStandard

之前推送过:Android 代码规范大全 , 可以一起学习。

开源几年了,被很多人夸过,你的代码写得比较规范,甚至有人质疑自己代码的写法,但是迟迟没有出一个代码规范,说来惭愧,只是因为我早几年写的代码还不够规范,不敢出来误导大家,而代码规范是后续才慢慢养成的,在这个过程中,我不仅参考了大公司出的代码规范文档,也研究了很多关于谷歌源码的编码规范,同时我也在无时不刻在思考,如何能写出让别人更好理解的代码,自打入行以来,我就在一直在这个问题上面探索。

为什么要做成一个开源项目?因为项目会长期更新,大家如果对里面一些规范表示不能理解的或者感觉写得不太对的,又或者有什么想要补充的,随时欢迎你通过 issue 反馈给我,大家的建议很重要,是我做好这件事的关键,我会认真对待和思考提出的每一个建议。同时我也相信一份好的代码规范经得住大众的反复推敲和不断实践,在这里也欢迎你提出自己的想法和建议。

前言

代码规范是我们每个程序员要做的事,假设我们按照自己的喜好来写代码,那么很可能出现的问题就是我看不懂你的代码或者你看不懂我的代码,这样会给后续维护形成巨大的障碍。

这个时候问题来了,如何让代码不分你我,或许只需要一套规则,你和我都认可并且遵守的代码规范守则。

那么你的疑问可能又来了,怎么样才能算好的代码规范,答案只有一个,真正好的代码规范就是别人的代码你一眼就能看懂,更不需要反复去看。

之所以这样并不是因为看的人 Review 代码的能力有多强,而是写代码的人愿意遵守规则,他知道自己想这么写,但是知道你会看不懂,所以换了一种方式来写,这种方式就是代码规范。

代码规范:一个好的代码规范可以帮助我们快速了解和熟悉相关的业务,降低后续维护的成本(二次开发或者排查问题)。

代码不规范:代码不规范会导致项目可读性变差,具体表现为难分辨和难理解,在无形之中加大项目后续维护的成本。

经验总结:编码不规范,同行泪两行

常规规范

不用 0dp,而用 0px,这样就可以在获取时避免系统进行换算,提升代码的执行效率。

字符串比较,应该用 “xxx”.equals(object),而不应该用 object.equals(“xxx”),因为 object 对象可能为空,我们应该把不为空的条件放置在表达式的前面。

尽量采用 switch case 来判断,如果不能实现则再考虑用 if else,因为在多条件下使用 switch case 语句判断会更加简洁。

不推荐用 layout_marginLeft,而应该用 layout_marginStart;不推荐用 layout_marginRight,而应该用 layout_marginEnd,原因有两个,一个是适配 Android 4.4 反方向特性(可在开发者选项中开启),第二个是 XML 布局中使用 layout_marginLeft 和 layout_marginRight 会有代码警告,padding 属性也是同理,这里不再赘述。

如果在 layout_marginStart 和 layout_marginEnd 的值相同的情况下,请替换使用 layout_marginHorizontal,而 layout_marginTop 和 layout_marginBottom 也同理,请替换使用 layout_marginVertical,能用一句代码能做的事不要写两句,padding 属性也是同理,这里不再赘述。

过期 和 高版本 的 API 一定要做对应的兼容(包含 Java 代码和 XML 属性),如果不需要兼容处理的,需要对警告进行抑制。

在能满足需求的情况下,尽量用 invisible 来代替 gone,因为 gone 会触发当前整个 View 树进行重新测量和绘制。

api 和 implementation,在能满足使用的情况下,优先选用 implementation,因为这样可以减少一些编译时间

https://www.jianshu.com/p/8962d6ba936e

ListView 和 RecyclerView 都能实现需求的前提下,优先选用 RecyclerView,具体原因不解释,大家应该都懂。

ScrollView 和 NestedScrollView 都能实现需求的前提下,优先选用 NestedScrollView,是因为 NestedScrollView 和 RecyclerView 支持相互嵌套,而 ScrollView 是不支持嵌套滚动的。

不能在项目中创建副本文件,例如创建 HomeActivity2.java、home_activity_v2.xml 类似的副本文件,因为这样不仅会增加项目的维护难度,同时对编译速度也会造成一定的影响,正确的做法应该是在原有的文件基础上面修改,如果出现需求变更的情况,请直接使用 Git 或者 SVN 进行版本回退。

如果一个类不需要被继承,请直接用 final 进行修饰,如果一个字段在类初始化过程中已经赋值并且没有地方进行二次赋值,也应当用 final 修饰。

每个小组成员应当安装阿里巴巴代码约束插件,并及时地对插件所提示的代码警告进行处理或者抑制警告。

https://plugins.jetbrains.com/plugin/10046-alibaba-java-coding-guidelines

应用图标应该放在 mipmap 目录下,其他图片资源应当放到 drawable 目录下,具体原因可以看谷歌官方文档对这两个文件夹给出的介绍:

https://developer.android.google.cn/guide/topics/resources/providing-resources

目录资源类型
drawable

位图文件(.png、.9.png、.jpg、.gif)或编译为以下可绘制对象资源子类型的 XML 文件:位图文件九宫格(可调整大小的位图)状态列表形状动画可绘制对象其他可绘制对象请参阅 Drawable 资源

https://developer.android.google.cn/guide/topics/resources/drawable-resource

mipmap

适用于不同启动器图标密度的可绘制对象文件。如需了解有关使用 mipmap 文件夹管理启动器图标的详细信息,请参阅管理项目概览

https://developer.android.google.cn/studio/projects

后台接口规范

后台返回的 id 值,不要使用 int 或者 long 类型来接收,而应该用 string 类型来接收,因为我们不需要对这个 id 值进行运算,所以我们不需要关心它是什么类型的。

后台返回的金额数值应该使用 String 来接收,而不能用浮点数来接收,因为 float 或者 double 在数值比较大的时候会容易丢失精度,并且还需要自己手动转换出想要保留的小数位,最好的方式是后台返回什么前端就展示什么,而到了运算的时候,则应该用 BigDecimal 类来进行转换和计算,当然金额在前端一般展示居多,运算的情况还算是比较少的。

我们在定义后台返回的 Bean 类时,不应当将一些我们没有使用到的字段添加到代码中,因为这样会消耗性能,因为 Gson 是通过反射将后台字段赋值到 Java 字段中,所以我们应当避免一些不必要的字段解析,另外臃余的字段也会给我们排查问题造成一定的阻碍。

如果后台给定的字段名不符合代码命名的时候,例如当遇到 student_name 这种命名时,我们应当使用 Gson 框架中的 @SerializedName 注解进行重命名。

请求的接口参数和返回字段必须要写上注释,除此之外还应该备注对应的后台接口文档地址,以便我们后续能够更好地进行维护和迭代。

后台返回的 Bean 类字段不能直接访问,而应该通过生成 Get 方法,然后使用这个 Get 方法来访问字段。

接口请求成功的提示可以不显示,但请求失败的提示需要显示给到用户,否则会加大排查问题的难度,也极有可能会把问题掩盖掉,从而导致问题遗留到线上去。

变量命名规范

严禁使用中文或者中文拼音进行重命名

使用驼峰式命名风格(单词最好控制在三个以内)

局部变量应用作用来命名,举个栗子:

Stringname;

TextViewnameView;

FrameLayoutnameLayout;

// 命名规范附带技巧(当布局中同个类型的控件只有一个的时候,也可以这样命名)

TextView textView;

RecyclerView recyclerView;

成员变量必须以小 m 开头,举个栗子:

StringmName;

TextViewmNameView;

FrameLayoutmNameLayout;

// 命名规范附带技巧(当布局中同个类型的控件只有一个的时候,也可以这样命名)

TextView mTextView;

RecyclerView mRecyclerView;

布尔值命名规范,命名不应该以 is 开头,举个栗子:

// 错误写法示例

privatebooleanmIsDebug = false;

booleanisDebug = false;

// 正确写法示例

privatebooleanmDebug = false;

booleandebug = false;

静态变量则用小 s 开头,举个栗子:

static HandlersHandler;

常量则需要用大写,并且用下划线代替驼峰,举个栗子:

staticfinalStringREQUEST_INSTALL_PACKAGES;

有细心的同学可能会发现一个问题,为什么我们最常用的 Glide 和 OkHttp 的源码为什么没有用字母 m 来区分局部变量和成员变量?但是谷歌的 SDK 源码和 Support 库就有呢?那究竟是用还是不用呢?

这个问题其实很好回答,我们可以先从体量上分析,首先谷歌的开发人员和项目数量肯定是最多的,那么谷歌在这块的探索和研究肯定是多于 Glie 和 OkHttp 的,其次是 Glide 和 OkHttp 的源码都有一个特点,很多类都维持在 1k 行代码左右,而谷歌源码很多类都接近 10k 行代码,例如 Activity 的源码在 API 30 上面有 8.8k 行代码,所以谷歌在这块略胜一筹,如果非要二选一,我选择谷歌的代码风格,并不是说 Glide 和 OkHttp 命名风格不好,是因为或许在未来的某一天,可能会有新的图片请求框架和网络请求框架来代替 Glide 和 OkHttp,但是基本不可能会出现有代替 Android SDK 或者 Support 库的一天。

最后让我们静静地欣赏一下 Activity 成员变量的命名:

publicclassActivity{

publicstaticfinalintRESULT_CANCELED = 0;

publicstaticfinalintRESULT_OK = – 1;

privateInstrumentation mInstrumentation;

privateIBinder mToken;

privateIBinder mAssistToken;

privateApplication mApplication;

/*package*/Intent mIntent;

/*package*/String mReferrer;

privateComponentName mComponent;

/*package*/ActivityInfo mActivityInfo;

/*package*/ActivityThread mMainThread;

Activity mParent;

booleanmCalled;

/*package*/booleanmResumed;

/*package*/booleanmStopped;

booleanmFinished;

booleanmStartedActivity;

privatebooleanmDestroyed;

privatebooleanmDoReportFullyDrawn = true;

privatebooleanmRestoredFromBundle;

privatefinalArrayList<Application.ActivityLifecycleCallbacks> mActivityLifecycleCallbacks =

newArrayList<Application.ActivityLifecycleCallbacks>;

privateWindow mWindow;

privateWindowManager mWindowManager;

privateCharSequence mTitle;

privateintmTitleColor = 0;

finalHandler mHandler = newHandler;

finalFragmentController mFragments = FragmentController.createController( newHostCallbacks);

}

包名命名规范

不允许包名中携带英文大写

包名应该以简洁的方式命名

包名要按照模块或者作用来划分

请不要在某一包名下放置一些无关的类

方法命名规范

initXX:初始化相关方法,使用 init 为前缀标识,如初始化布局 initView

isXX:方法返回值为 boolean 型的请使用 is 或 check 为前缀标识

getXX:返回某个值的方法,使用 get 为前缀标识,例如 getName

setXX:设置某个属性值,使用 set 为前缀标识,例如 setName

handleXX/processXX:对数据进行处理的方法,例如 handleMessage

displayXX/showXX:弹出提示框和提示信息,例如 showDialog

updateXX:更新某个东西,例如 updateData

saveXX:保存某个东西,例如 saveData

resetXX:重置某个东西,例如 resetData

clearXX:清除某个东西,例如 clearData

removeXX:移除数据或者视图等,例如 removeView

drawXX:绘制数据或效果相关的,使用 draw 前缀标识,例如 drawText

类文件命名规范

业务模块请以 模块 + 类型 来命名,举个栗子

HomeActivity.java

SettingFragment.java

HomeAdapter.java

AddressDialog.java

技术模块请以类的 作用 来命名,举个栗子

CrashHandler.java

GridSpaceDecoration.java

PickerLayoutManager.java

接口文件命名规范

如果是监听事件可以参考 View 的写法及命名:

publicclassView{

privateView.OnClickListener mListener;

publicvoidsetOnClickListener( OnClickListener listener) {

mListener = listener;

}

publicinterfaceOnClickListener{

voidonClick( View v) ;

}

}

如果是回调事件可以参考 Handler 的写法及命名:

publicclassHandler{

publicinterfaceCallback{

booleanhandleMessage(Message msg);

}

}

至于接口写在内部还是外部,具体可以视实际情况而定,如果功能比较庞大,就可以考虑抽取成外部的,只作用在某个类上的,则就可以直接写成内部的。

布局文件命名规范

以 模块 + 类型 来命名,举个栗子:

home_activity.xml

setting_fragment.xml

menu_item.xml

address_dialog.xml

这样写的好处在于,由于 res 文件夹下是没有层级概念的

通过前缀的命名可以帮助我们更好定位到同一模块下的资源

例如分享对话框中,有对话框 Root 布局和 Item 布局

share_dialog.xmlRoot布局)

share_item.xmlItem布局)

资源文件命名规范

如果是业务模块下的资源,以 模块 + 类型 来命名,例如分享对话框的资源:

share_link_ic.png(复制链接)

share_moment_ic.png(分享到朋友圈)

share_qq_ic.png(分享到 QQ好友)

share_qzone_ic.png(分享到 QQ空间)

share_wechat_ic.png(分享到微信好友)

如果和业务模块不相干的资源,以 作用 + 类型 来命名,例如通用的控件样式资源:

button_rect_selector.xml(通用直角按钮样式)

button_round_selector.xml(通用圆角按钮样式)

这种资源有一个共同特点,它不属于哪个模块,但是在不同模块都有用到,所以不能用业务的模块名作为文件名前缀。

接口实现规范

一般情况下,我们会在类中这样实现接口,这样写的好处是,可以减少对象的创建,并且代码也比较美观。

publicfinalclassPasswordEditTextextendsEditText

implementsView. OnTouchListener,

View. OnFocusChangeListener, TextWatcher{

publicPasswordEditText(Context context, AttributeSet attrs, intdefStyleAttr) {

super(context, attrs, defStyleAttr);

setOnTouchListener( this);

setOnFocusChangeListener( this);

addTextChangedListener( this);

}

@Override

publicvoidonFocusChange(View view, booleanhasFocus) {

……

}

@Override

publicbooleanonTouch(View view, MotionEvent event){

……

}

@Override

publicvoidonTextChanged(CharSequence s, intstart, intbefore, intcount) {

……

}

@Override

publicvoidbeforeTextChanged(CharSequence s, intstart, intcount, intafter) {

……

}

@Override

publicvoidafterTextChanged(Editable s){

……

}

}

但是有一个美中不足的地方,就是在实现的接口过多时,我们很难分辨是哪个方法是哪个接口的,这个时候可以使用注释的方式来解决这个问题,加上 @link 还可以帮助我们快速定位接口类在项目中所在的位置。

publicfinalclassPasswordEditTextextendsEditText

implementsView. OnTouchListener,

View. OnFocusChangeListener, TextWatcher{

publicPasswordEditText(Context context, AttributeSet attrs, intdefStyleAttr) {

super(context, attrs, defStyleAttr);

setOnTouchListener( this);

setOnFocusChangeListener( this);

addTextChangedListener( this);

}

/**

* { @linkOnFocusChangeListener}

*/

@Override

publicvoidonFocusChange(View view, booleanhasFocus) {

……

}

/**

* { @linkOnTouchListener}

*/

@Override

publicbooleanonTouch(View view, MotionEvent event){

……

}

/**

* { @linkTextWatcher}

*/

@Override

publicvoidonTextChanged(CharSequence s, intstart, intbefore, intcount) {

……

}

@Override

publicvoidbeforeTextChanged(CharSequence s, intstart, intcount, intafter) {

……

}

@Override

publicvoidafterTextChanged(Editable s){

……

}

}

异常捕获规范

请不要使用此方式捕获异常,因为这种方式会把问题给隐藏掉,从而会加大后续排查问题的难度。

try{

Xxx.xxx;

} catch( Exceptione) {}

如需捕获异常,请用以下方式进行捕获,列出具体的异常类型,并在代码中输出对应的日志。

// 捕获这个异常,避免程序崩溃

try{

// 目前发现在 Android 7.1 主线程被阻塞之后弹吐司会导致崩溃,可使用 Thread.sleep(5000) 进行复现

// 查看源码得知 Google 已经在 Android 8.0 已经修复了此问题

// 主线程阻塞之后 Toast 也会被阻塞,Toast 因为显示超时导致 Window Token 失效

mHandler.handleMessage(msg);

} catch(WindowManager.BadTokenException | IllegalStateException e) {

// android.view.WindowManager$BadTokenException:Unable to add window — token android.os.BinderProxy is not valid; is your activity running?

// java.lang.IllegalStateException:View android.widget.TextView has already been added to the window manager.

e.printStackTrace;

}

如果这个异常不是通过方法 throws 关键字抛出,则需要在 try 块中说明崩溃的缘由,并注明抛出的异常信息。

有异常就一定要 try catch ?,这种想法其实是错的,例如我们项目用 Glide 加载图片会抛出以下异常:

Causedby: java.lang.IllegalArgumentException: Youcannotstartaloadforadestroyedactivity

atcom.bumptech.glide.manager.RequestManagerRetriever.assertNotDestroyed( RequestManagerRetriever.java:348)

atcom.bumptech.glide.manager.RequestManagerRetriever.get( RequestManagerRetriever.java:148)

atcom.bumptech.glide.Glide.with( Glide.java:826)

这是因为 Activity 的销毁了而去加载图片导致的(场景:异步执行图片加载),大多人的解决方式可能是:

try{

// Activity 销毁后执行加载图片会触发 crash

Glide.with( this)

.load(url)

. into(mImageView);

} catch(IllegalArgumentException e) {

// java.lang.IllegalArgumentException: You cannot start a load for a destroyed activity

e.printStackTrace;

}

虽然这种方式可以解决 crash 的问题,但是显得不够严谨,Glide 抛异常给外层,其实无非就想告诉调用者,调用的时机错了,正确的处理方式不是直接捕获这个异常,而是应该在外层做好逻辑判断,避免会进入出现 crash的代码,正确的处理示例如下:

if(isFinishing || isDestroyed) {

// Glide:You cannot start a load for a destroyed activity

return;

}

Glide.with( this)

.load(url)

.into(mImageView);

所以尽量不要通过 try catch 的方式来处理异常,除非外层真的判断不了,否则应该通过一些逻辑判断来避免进入一些会 crash 的代码。

Activity 跳转约定

应当将 Intent 中的 key 常量保存到一个管理类中,又或者定义在目标的 Activity 中

publicclassIntentKey{

/** id */

publicstaticfinalString ID = “id”;

/** token */

publicstaticfinalString TOKEN = “token”;

/** 订单 */

publicstaticfinalString ORDER = “order”;

/** 余额 */

publicstaticfinalString BALANCE = “balance”;

/** 时间 */

publicstaticfinalString TIME = “time”;

/** URL */

publicstaticfinalString URL = “url”;

/** 路径 */

publicstaticfinalString PATH = “path”;

/** 其他 */

publicstaticfinalString OTHER = “other”;

…..

}

如果跳转的 Activity 需要传递参数,应该在目标的 Activity 中定义静态的 start 又或者 newIntent 方法

publicfinalclassWebActivityextendsActivity{

publicstaticvoidstart(Context context, String url){

Intent intent = newIntent(context, WebActivity.class);

intent.putExtra(IntentKey.URL, url);

context.startActivity(intent);

}

}

publicfinalclassWebActivityextendsActivity{

publicstaticIntent newIntent(Context context, String url){

Intent intent = newIntent(context, WebActivity.class);

intent.putExtra(IntentKey.URL, url);

returnintent;

}

}

如果创建的 Fragment 需要传递参数,应该在目标的 Fragment 中定义静态的 newInstance 方法

publicfinalclassWebFragmentextendsFragment{

publicstaticWebFragment newInstance(String url){

WebFragment fragment = newWebFragment;

Bundle bundle = newBundle;

bundle.putString(IntentKey.URL, url);

fragment.setArguments(bundle);

returnfragment;

}

}

如果跳转的 Activity 或者创建的 Fragment 不需要传任何参数,可以不需要定义这些静态方法

第三方框架使用规范

集成一些第三方框架或者 SDK,必须注明作用和出处,以便出现问题时能够快速核查和反馈

/ / 权限请求框架:https://github.com/getActivity/XXPermissions

implementation ‘com.hjq:xxpermissions:9.8’

尽量不要选择功能两套相同的框架,应当引用最合适的一套框架进行开发

使用第三方库必须要依赖指定的版本号,而不能使用 + 号来指定依赖库最新的版本号

使用第三方开源库出现问题或者 Bug 时应及时通知到开源库的作者,如果没有及时回复就根据实际情况对问题进行修复

尽量避免 Copy 第三方库的技术代码到项目中,特别是在放置到项目业务模块中,因为这样会增加项目的复杂度,从而降低可维护性

如果出现问题不能找到开源库的作者,如果需要修改,应当将这些代码抽取到单独的 Module 中

能用框架就用成熟框架,尽量不要自己编写或者修改框架,如果有需要,要对这块进行严格测试

代码注释规范

类注释规范:author 是创建者(必填项)、time 是创建时间(必填项)、desc 是类的描述(必填项),doc是文档地址(非必填),github 是开源地址(如果项目是开源的则必填,否则不填)

/**

* author : Android 轮子哥

* github : https://github.com/getActivity/XXPermissions

* time : 2018/06/15

* desc : 权限请求实体类

* doc : https://developer.android.google.cn/reference/android/Manifest.permission?hl=zh_cn

* https://developer.android.google.cn/guide/topics/permissions/overview?hl=zh-cn#normal-dangerous

*/

publicfinalclassPermission{

….

}

方法注释规范:方法注释可根据实际情况而定

/**

* 设置请求的对象

*

* @param activity 当前 Activity,可以传入栈顶的 Activity

*/

publicstaticXXPermissions with(FragmentActivity activity){

return….;

}

字段注释规范:根据字段的作用而定

/** 请求的权限组 */

privatestaticfinalString REQUEST_PERMISSIONS = “request_permissions”;

/** 权限回调对象 */

privateOnPermissionCallback mCallBack;

变量注释规范(如果 API 是比较常见并且容易理解可以不用写,如果是复杂并且羞涩难懂则需要写上)

// 设置保留实例,不会因为屏幕方向或配置变化而重新创建

fragment.setRetainInstance( true);

注释什么情况下要写?什么情况下不用写?这个问题我很有感触,代码注释写多了不好,显得太啰嗦,也会增加工作量,写少了也不好,又怕别人看不懂,也害怕给自己后面留坑。

我个人的建议是尽量用规范的命名来减少不必要的注释,很多时候我们只需要换位思考一下,忘记这段代码是自己写的,再问一下自己能不能一下子读懂,如果可以的话,注释就可以不用写,否则注释还是要考虑写上。

String ID 命名规范

以 模块 + 功能 来命名,例如

<!– 主界面 –>

< stringname= “home_nav_index”> 首页 </ string>

< stringname= “home_nav_found”> 发现 </ string>

< stringname= “home_nav_message”> 消息 </ string>

< stringname= “home_nav_me”> 我的 </ string>

< stringname= “home_exit_hint”> 再按一次退出 </ string>

<!– 登录界面 –>

< stringname= “login_register”> 注册 </ string>

< stringname= “login_phone_hint”> 请输入手机号 </ string>

< stringname= “login_password_hint”> 请输入密码 </ string>

< stringname= “login_forget”> 忘记密码? </ string>

< stringname= “login_text”> 登录 </ string>

< stringname= “login_other”> 其他登录方式 </ string>

<!– 注册界面 –>

< stringname= “register_title”> 注册 </ string>

< stringname= “register_hint”> 手机号仅用于登录和保护账号安全 </ string>

< stringname= “register_login”> 登录 </ string>

< stringname= “register_password_hint1”> 设置密码 </ string>

< stringname= “register_password_hint2”> 再次输入密码 </ string>

< stringname= “register_password_input_error”> 两次密码输入不一致,请重新输入 </ string>

<!– 设置界面 –>

< stringname= “setting_title”> 设置 </ string>

< stringname= “setting_language_switchover”> 语言切换 </ string>

< stringname= “setting_language_simple”> 简体中文 </ string>

< stringname= “setting_language_complex”> 繁体中文 </ string>

Anim ID 命名规范

应用到某个模块 View,举个栗子

login_left_balloon_view.xml

login_right_balloon_view.xml

应用到全局 Activity,举个栗子

left_in_activity.xml

left_out_activity.xml

应用到全局 Dialog,举个栗子

bottom_in_dialog.xml

bottom_out_dialog.xml

View ID 命名规范

应该以 控件的缩写 + 模块名 + 作用 来命名,例如

@+ id/R.id.rg_login_type

@+ id/R.id.et_login_phone

@+ id/R.id.et_login_sms

@+ id/R.id.et_login_password

@+ id/R.id.btn_login_commit

View 和 Layout 控件缩写表,这里列举最常见的几个

名称缩写
TextViewtv
EditTextet
Buttonbtn
ImageViewiv
ImageButtonib
ListViewlv
RecyclerViewrv
RadioButtonrb
RadioGrouprg
ProgressBarpb
CheckBoxcb
TableLayouttl
ScrollViewsv
LinearLayoutll
RelativeLayoutrl
FrameLayoutfl

Style 样式命名规范

如果只是主题相关的样式,以 Theme 命名结尾,控件样式则以 Style 命名结尾,命名要求尽量简洁,并且需要有代码注释,示例如下:

<!– 应用主题样式 –>

< stylename= “AppTheme”parent= “Theme.AppCompat.DayNight.NoActionBar”>

…..

</ style>

<!– 全屏主题样式 –>

< stylename= “FullScreenTheme”parent= “AppTheme”>

…..

</ style>

<!– 闪屏页主题样式 –>

< stylename= “SplashTheme”parent= “FullScreenTheme”>

…..

</ style>

<!– 默认圆角按钮样式 –>

< stylename= “ButtonStyle”parent= “Widget.AppCompat.Button.Borderless”>

…..

</ style>

<!– 不带圆角按钮样式 –>

< stylename= “RectButtonStyle”parent= “ButtonStyle”>

…..

</ style>

<!– 默认文本框样式 –>

< stylename= “EditTextStyle”>

…..

</ style>

<!– 验证码按钮样式 –>

< stylename= “CountdownViewStyle”>

…..

</ style>

XML 编码规范

不推荐用 dp 作为字体单位,虽然在大部分手机上面 dp 和 sp 计算是差不多的,但是会有一部分老年用户群,例如咱们的长辈,他们通常会把手机显示的字体大小调大,这样他们才不需要带眼镜看手机,如果我们用 dp 作为字体单位,无论手机怎么调整字体大小,应用的字体大小都不会有任何的变化,所以这种操作显然是非常不人性化的。

<!– 错误写法示例 –>

< TextView

android:layout_width= “wrap_content”

android:layout_height= “wrap_content”

android:text= “老铁”

android:textSize= “18dp”/>

<!– 正确写法示例 –>

< TextView

android:layout_width= “wrap_content”

android:layout_height= “wrap_content”

android:text= “老铁”

android:textSize= “18sp”/>

不能根据设计图给定的宽高把 TextView 或者 Button 的宽高定死,而是通过 wrap_content 和 padding 的方式来调整 View 的宽高,因为在不同手机上面字体大小不一致,在字体显示比较小的手机上面会显示正常,但是在字体显示比较大的平板上面文字上半部分极有可能会出现被裁剪的情况,所以我们不能把宽高定死,而是通过 padding 来调整到控件的大小。

不过需要注意的是,TextView 有自带的文字间距,我们在拿设计图给定的 padding值时,需要拿设计图给定的值适当减去这一部分值(一般大概是在 2~3dp)。

<!– 错误写法示例 –>

< Button

android:layout_width= “180dp”

android:layout_height= “60dp”

android:gravity= “center”

android:text= “提交”/>

<!– 正确写法示例 –>

< Button

android:layout_width= “wrap_content”

android:layout_height= “wrap_content”

android:gravity= “center”

android:paddingStart= “80dp”

android:paddingTop= “20dp”

android:paddingEnd= “80dp”

android:paddingBottom= “20dp”

android:text= “提交”/>

ImageView 的宽高任一项定义成 match_parent 时,另外一项不能写死大小,而是应该使用 wrap_content,否则很可能会因为比例不对导致图片变形,另外还需要使用 android:adjustViewBounds=”true” 属性,否则 ImageView 无法根据图片的宽高来调整自己的宽高。

<!– 错误写法示例 –>

< ImageView

android:layout_width= “match_parent”

android:layout_height= “300dp”

android:src= “@drawable/example_bg”/>

<!– 正确写法示例 –>

< ImageView

android:layout_width= “match_parent”

android:layout_height= “wrap_content”

android:adjustViewBounds= “true”

android:src= “@drawable/example_bg”/>

XML 节点编写应该规范,在没有子节点的情况下,应当以 /> 节点结尾,如果有则以 </xxx.xxx.xxx> 节点结尾

<!– 错误写法示例 –>

< androidx.recyclerview.widget.RecyclerView

android:layout_width= “match_parent”

android:layout_height= “match_parent”>

</ androidx.recyclerview.widget.RecyclerView>

<!– 正确写法示例 –>

< androidx.recyclerview.widget.RecyclerView

android:layout_width= “match_parent”

android:layout_height= “match_parent”/>

<!– 错误写法示例 –>

< TextView

android:layout_width= “match_parent”

android:layout_height= “match_parent”>

</ TextView>

<!– 正确写法示例 –>

< TextView

android:layout_width= “match_parent”

android:layout_height= “match_parent”/>

预览属性约定

应该在布局文件根布局中定义 tools:context 属性,以便在布局文件中快速定位到对应的类

< FrameLayoutxmlns:android= “http://schemas.android.com/apk/res/android”

xmlns:app= “http://schemas.android.com/apk/res-auto”

xmlns:tools= “http://schemas.android.com/tools”

android:layout_width= “match_parent”

android:layout_height= “match_parent”

tools:context= “.ui.activity.HomeActivity”>

</ FrameLayout>

tools:context= “.ui.activity.HomeActivity”

tools:context= “.ui.fragment.SettingFragment”

tools:context= “.ui.adapter.HomeAdapter”

tools:context= “.ui.dialog.PersonDataDialog”

此外,tools 属性还有各种各样的用途,例如 RecyclerView 的 tools 属性

< androidx.recyclerview.widget.RecyclerView

android:id= “@+id/rv_pay_list”

android:layout_width= “match_parent”

android:layout_height= “wrap_content”

android:overScrollMode= “never”

tools:listitem= “@layout/item_dialog_pay_password”

tools:itemCount= “9”

tools:layoutManager= “androidx.recyclerview.widget.GridLayoutManager”

tools:spanCount= “3”/>

tools 这种命名不止可以应用于 RecyclerView,还可以应用于其他 View 的属性,比如常用的 TextView 和 ImageView

< TextView

android:layout_width= “wrap_content”

android:layout_height= “wrap_content”

tools:text= “学生姓名”/>

< ImageView

android:id= “@+id/iv_home_course_image”

android:layout_width= “wrap_content”

android:layout_height= “wrap_content”

tools:src= “@drawable/bg_home_placeholder”/>

如果某个 TextView 显示的字符串是一成不变的,那么可以直接定义在布局文件中,如果是动态变化的,那么应该使用 tools:text 预览属性,而不应该使用 android:text,其他布局属性也同理。

资源使用约定

String 硬编码规范:如果项目已经适配了多语种,则严禁写死在 Java 代码或者布局文件中,如果没有这块需求的话,也建议将 String 资源定义在 string.xml 文件,此项不强制要求,大家根据实际情况而定。

Color 硬编码规范:在没有使用夜间模式的情况下,允许大部分 Color 值直接定义在布局文件中,但是如果某个色值引用得比较多(例如主题强调色、默认背景色等),需要抽取到 color.xml 文件中。

Dimens 硬编码规范:允许写死在 Java 代码或者布局文件中,但是如果使用了通配符方案对屏幕进行适配,那么则不能直接写死。

Style 样式规范:对于一些常用并且样式比较统一的控件,例如 Button、EditText 等,我们对这些控件的样式进行抽取到 style.xml 文件中来,避免属性重复定义。

注意该项目持续更新,文章内容无法 一直同步,可以前往github 或码云地址持续关注:

https://github.com/getActivity/AndroidCodeStandard

https://gitee.com/getActivity/AndroidCodeStandard

原创文章版权所有,如需转载,请注明出处。http://www.qilurongmei.com/archives/7460

为您推荐

发表评论

邮箱地址不会被公开。 必填项已用*标注