Android代码规范

一、前言

代码规范是我们每个程序员要做的事,假设我们按照自己的喜好来写代码,那么很可能出现的问题就是我看不懂你的代码或者你看不懂我的代码,这样会给后续维护形成巨大的障碍。这个时候问题来了,如何让代码不分你我,或许只需要一套规则,你和我都认可并且遵守的代码规范守则。

那么你的疑问可能又来了,怎么样才能算好的代码规范,答案只有一个,真正好的代码规范就是别人的代码你一眼就能看懂,更不需要反复去看。之所以这样并不是因为看的人 Review 代码的能力有多强,而是写代码的人愿意遵守规则,他知道自己想这么写,但是知道你会看不懂,所以换了一种方式来写,这种方式就是代码规范。

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

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

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

二、常规规范

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

字符串比较,应该用"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,因为这样可以减少一些编译时间。

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

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

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

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

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

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

三、后台接口规范

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

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

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

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

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

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

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

四、变量命名规范

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

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

局部变量或者公开的成员变量应该以作用来命名,例如:

publicStringname;publicTextView nameView;publicFrameLayout nameLayout;

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

publicTextView textView;publicRecyclerView recyclerView;

非公开的成员变量必须以小m开头,例如:

privateStringmName;privateTextView mNameView;privateFrameLayout mNameLayout;

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

privateTextView mTextView;privateRecyclerView mRecyclerView;

布尔值命名规范,无论是局部变量还是成员变量,都不应该携带is,例如:

// 不规范写法示例privatebooleanmIsDebug;booleanisDebug;// 规范写法示例privatebooleanmDebug;booleandebug;

静态变量则用小s开头,例如:

staticHandlersHandler;

常量则需要用大写,并且用下划线代替驼峰,例如:

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;

privateInstrumentationmInstrumentation;

privateIBindermToken;

privateIBindermAssistToken;

privateApplicationmApplication;

/*package*/IntentmIntent;

/*package*/StringmReferrer;

privateComponentNamemComponent;

/*package*/ActivityInfomActivityInfo;

/*package*/ActivityThreadmMainThread;

ActivitymParent;

booleanmCalled;

/*package*/booleanmResumed;

/*package*/booleanmStopped;

booleanmFinished;

booleanmStartedActivity;

privatebooleanmDestroyed;

privatebooleanmDoReportFullyDrawn=true;

privatebooleanmRestoredFromBundle;

privatefinalArrayList

newArrayList();

privateWindowmWindow;

privateWindowManagermWindowManager;

privateCharSequencemTitle;

privateintmTitleColor=0;

finalHandlermHandler=newHandler();

finalFragmentControllermFragments=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.javaSettingFragment.javaHomeAdapter.javaAddressDialog.java

技术模块:请以类的作用来命名,例如:例如

CrashHandler.javaGridSpaceDecoration.javaPickerLayoutManager.java

八、接口文件命名规范

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

publicclassView{privateView.OnClickListener mListener;publicvoidsetOnClickListener(OnClickListener listener){        mListener = listener;    }publicinterfaceOnClickListener{voidonClick(View v);    }}

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

publicclassHandler{publicinterfaceCallback{booleanhandleMessage(Message msg);    }}

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

九、接口实现规范

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

publicfinalclassPasswordEditTextextendsEditTextimplementsView.OnTouchListener,View.OnFocusChangeListener,TextWatcher{

publicPasswordEditText(Contextcontext,AttributeSetattrs,intdefStyleAttr) {

super(context, attrs, defStyleAttr);

setOnTouchListener(this);

setOnFocusChangeListener(this);

addTextChangedListener(this);

}

@Override

publicvoidonFocusChange(Viewview,booleanhasFocus) {

......

}

@Override

publicbooleanonTouch(Viewview,MotionEventevent) {

......

}

@Override

publicvoidonTextChanged(CharSequences,intstart,intbefore,intcount) {

......

}

@Override

publicvoidbeforeTextChanged(CharSequences,intstart,intcount,intafter) {

......

}

@Override

publicvoidafterTextChanged(Editables) {

......

}

}

但是有两个美中不足的地方,就是在实现的接口过多时,我们很难分辨是哪个方法是哪个接口的,这个时候可以使用注释的方式来解决这个问题,加上@link还可以帮助我们快速定位接口类在项目中所在的位置;另外一个是implements修饰符换行的问题,合理的换行会使代码更加简单直观。

publicfinalclassPasswordEditTextextendsEditText

implementsView.OnTouchListener,

View.OnFocusChangeListener,TextWatcher{

publicPasswordEditText(Contextcontext,AttributeSetattrs,intdefStyleAttr) {

super(context, attrs, defStyleAttr);

setOnTouchListener(this);

setOnFocusChangeListener(this);

addTextChangedListener(this);

}

/**

* {@linkOnFocusChangeListener}

*/

@Override

publicvoidonFocusChange(Viewview,booleanhasFocus) {

......

}

/**

* {@linkOnTouchListener}

*/

@Override

publicbooleanonTouch(Viewview,MotionEventevent) {

......

}

/**

* {@linkTextWatcher}

*/

@Override

publicvoidonTextChanged(CharSequences,intstart,intbefore,intcount) {

......

}

@Override

publicvoidbeforeTextChanged(CharSequences,intstart,intcount,intafter) {

......

}

@Override

publicvoidafterTextChanged(Editables) {

......

}

}

十、异常捕获规范

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

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|IllegalStateExceptione) {

//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:Youcannot start a loadfora destroyed activity

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(IllegalArgumentExceptione) {

//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 activityreturn;}Glide.with(this)        .load(url)        .into(mImageView);

所以尽量不要通过捕获的方式来处理异常,除非外层真的判断不了,否则应该通过一些逻辑判断来避免进入一些会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{publicstaticIntentnewIntent(Context context, String url){Intent intent =newIntent(context, WebActivity.class);        intent.putExtra(IntentKey.URL, url);returnintent;    }}

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

publicfinalclassWebFragmentextendsFragment{publicstaticWebFragmentnewInstance(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 中。

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

十三、多模块规范

模块命名规范:应该以简单明了的方式来命名

appbasewidgetumengcoursesocketliveshop

模块混淆配置:请不要使用proguardFiles语句,而是应该使用consumerProguardFiles语句,因为consumerProguardFiles语句会将混淆规则和资源代码一同合并到aar包中,这样做的好处在于:在项目编译时会将 aar 包中的混淆规则合并到主模块中。

android{defaultConfig{//模块混淆配置consumerProguardFiles'proguard-xxx.pro'}}

资源前缀限制:我们应该在模块中加入此限制,这样我们在模块中添加资源时,编译器如果发现资源名称前缀不符合规范,则会出现代码警告。这样做的好处在于,以某一名称作为前缀,可以有效避免在编译时引发的一些资源合并冲突。

android {// 资源前缀限制resourcePrefix"xxx_"}

框架版本管理:我们应该统一抽取框架的版本到config.gradle文件中:

ext {

android=[compileSdkVersion:28,

minSdkVersion:19,

targetSdkVersion:28,

versionCode:40102,

versionName:"4.1.2",

]

dependencies=[

"appcompat":"androidx.appcompat:appcompat:1.2.0",

"material":"com.google.android.material:material:1.2.0",

]

}

然后在每个模块下这样定义,这样做的好处是可以做到版本号的统一管理。

applyfrom :'../config.gradle'android {compileSdkVersionrootProject.ext.android["compileSdkVersion"]    defaultConfig {minSdkVersionrootProject.ext.android["minSdkVersion"]targetSdkVersion rootProject.ext.android["targetSdkVersion"]    }}dependencies {implementationrootProject.ext.dependencies["appcompat"]implementation rootProject.ext.dependencies["material"]}

除此之外还有另外一种写法,我们可以把config.gradle修改成这样:

android{compileSdkVersion28defaultConfig{minSdkVersion19targetSdkVersion28versionName'4.1.2'versionCode40102}}dependencies{implementation'androidx.appcompat:appcompat:1.2.0'implementation'com.google.android.material:material:1.2.1'}

然后在每个模块上添加一句引用即可,相比上一种写方法,这种方式更强大,因为它不仅可以配置版本号,还支持统一其他的配置项。

applyfrom:'../config.gradle'

具体要用哪一种,可以根据实际情况而定,如果项目采用的是组件化,则可以考虑使用第一种方式,如果项目采用的是模块化,则可以考虑使用第二种方式。

十四、代码注释规范

类注释规范: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 */publicstaticXXPermissionswith(FragmentActivity activity){return....;}

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

/** 请求的权限组 */privatestaticfinalStringREQUEST_PERMISSIONS ="request_permissions";/** 权限回调对象 */privateOnPermissionCallback mCallBack;

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

// 设置保留实例,不会因为屏幕方向或配置变化而重新创建fragment.setRetainInstance(true);

注释什么情况下要写?什么情况下不用写?这个问题我很有感触,代码注释写多了不好,显得太啰嗦,也会增加工作量,写少了也不好,又怕别人看不懂,也害怕给自己后面留坑。我个人的建议是尽量用规范的命名来减少不必要的注释,很多时候我们只需要换位思考一下,忘记这段代码是自己写的,再问一下自己能不能一下子读懂,如果可以的话,注释就可以不用写,否则注释还是要考虑写上。

十五、代码硬编码规范

请尽量避免使用硬编码,例如系统的一些常量值,不能直接写死,而是应该通过代码引用,例如:

//不规范写法示例

if(view.getVisibility()!=0) {

return;

}

Intentintent=newIntent("android.settings.APPLICATION_DETAILS_SETTINGS");

startActivity(intent);

//规范写法示例

if(view.getVisibility() != View.VISIBLE) {return;}Intent intent =newIntent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);startActivity(intent);

在项目开发中,被多次使用到的数值或者字符串也应该提取成常量来供外部引用,例如:

publicfinalclassUserInfoManager{/** 学生 */publicstaticfinalintTYPE_STUDENT =0;/** 老师 */publicstaticfinalintTYPE_TEACHER =1;/** 家长 */publicstaticfinalintTYPE_PATRIARCH =2;}

但并不代表所有的数值都需要常量化,有一些数值常量化的意义并不大,例如:

ValueAnimatoranimator = ValueAnimator.ofInt(0, 100);animator.setDuration(500);animator.start();

所以衡量一个数值或者字符串是否进行常量化的标准有两点:

这个数值或者字符串是否会被多次使用

这个数值或者字符串是否具有一定的含义

十六、布局文件命名规范

以模块 + 类型来命名,例如:

home_activity.xmlsetting_fragment.xmlmenu_item.xmladdress_dialog.xml

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

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

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

share_dialog.xml(Root布局)share_item.xml(Item布局)

十七、资源文件命名规范

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

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(通用圆角按钮样式)

这种资源有一个共同特点,它不属于哪个模块,但是在不同模块都有用到,所以不能用业务的模块名作为文件名前缀,最后附上常见类型名称对应表:

十八、String ID 命名规范

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

<!--主界面-->

首页

发现

消息

我的

再按一次退出

<!--登录界面-->

注册

请输入手机号

请输入密码

忘记密码?

登录

其他登录方式

<!--注册界面-->

注册

手机号仅用于登录和保护账号安全

登录

设置密码

再次输入密码

两次密码输入不一致,请重新输入

<!--设置界面-->

设置

语言切换

简体中文

繁体中文

另外有一类 String 被多个模块所引用,需要以common + 作用来命名,例如:

加载中…</string>确定取消年月日时分秒

十九、Color ID 命名规范

请以模块 + 含义 + color来命名,例如:

#FFBBBBBB#FF33B5E5#FF99CC00#FFFFBB33#FFFF4444#FFFFFFFF

但是实际开发中,我们常常会遇到下面这种命名方式:

#color_FF35BF30</color>

但其实这种命名方式是不规范的,因为它对Color ID的名称定义比较模糊,会容易给别人造成误导;举个例子:假设项目中有200个地方引用了这个color_FF35BF30色值,其中有150地方是你自己引用的,另外50个地方是别人引用的,但是别人不知道你那个色值是干什么的,看到你有写就直接引用了,突然有一天产品经理心情不好要改这个色值,那么你要从200地方区分150个需要修改的地方和50个不需要修改的地方。

二十、Anim ID 命名规范

应用到某个模块View,例如:

login_left_balloon_view.xmllogin_right_balloon_view.xml

应用到全局Activity,例如:

left_in_activity.xmlleft_out_activity.xml

应用到全局Dialog,例如:

bottom_in_dialog.xmlbottom_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 控件缩写表,这里列举最常见的几个

二十二、Style 命名规范

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

<!-- 应用主题样式 -->    .....<!-- 全屏主题样式 -->    .....<!-- 闪屏页主题样式 -->    .....<!-- 默认圆角按钮样式 -->    .....<!-- 不带圆角按钮样式 -->    .....<!-- 默认文本框样式 -->    .....<!-- 验证码按钮样式 -->    .....

二十三、XML 编码规范

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

<!-- 不规范写法示例 --><!-- 规范写法示例 -->

不能根据设计图给定的宽高把TextView或者Button的宽高定死,而是通过wrap_content和padding的方式来调整 View 的宽高,因为在不同手机上面字体大小不一致,在字体显示比较小的手机上面会显示正常,但是在字体显示比较大的平板上面文字上半部分极有可能会出现被裁剪的情况,所以我们不能把宽高定死,而是通过padding来调整到控件的大小。不过需要注意的是,TextView 有自带的文字间距,我们在拿设计图给定的padding值时,需要拿设计图给定的值适当减去这一部分值(一般大概是在2~3dp)。

<!-- 不规范写法示例 --><!-- 规范写法示例 -->

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

<!-- 不规范写法示例 --><!-- 规范写法示例 -->

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

<!-- 不规范写法示例 --><!-- 不规范写法示例 --><!-- 规范写法示例 --><!-- 规范写法示例 -->

二十四、预览属性约定

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

tools:context=".ui.activity.HomeActivity"tools:context=".ui.fragment.SettingFragment"tools:context=".ui.adapter.HomeAdapter"tools:context=".ui.dialog.PersonDataDialog"

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

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

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

二十五、资源硬编码规范

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

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

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

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

版权申明:内容来源网络,版权归原创者所有。除非无法确认,都会标明作者及出处,如有侵权烦请告知,我们会立即删除并致歉。谢谢!

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,290评论 4 363
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,399评论 1 294
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,021评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,034评论 0 207
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,412评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,651评论 1 219
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,902评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,605评论 0 199
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,339评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,586评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,076评论 1 261
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,400评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,060评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,083评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,851评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,685评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,595评论 2 270