Android开发笔记之:用Enum(枚举类型)取代整数集的应用详解
2023-06-13 09:14:54 时间
在Android的API中可以发现有很多用整数集来作为参数的地方,先来看一下实例。
LinearLayout是大家所熟知的一个UI基本元素,它里面有一个方向的属性,可以通过以下方法来设置:
复制代码代码如下:
LinearLayout.setOrientation(int);
使用的时候,通常都是这样:
LinearLayout.setOrientation(LinearLayout.HORIZONTAL);
LinearLayout.setOrientation(LinearLayout.VERTICAL);
但也可以这样使用:
LinearLayout.setOrientation(0);//LinearLayout.HORIZONTAL=0
LinearLayout.setOrientation(1);//LinearLayout.VERTICAL=0x01
甚至可以这样:
LinearLayout.setOrientation(Integer.MAX_VALUE);
LinearLayout.setOrientation(Integer.MIN_VALUE);
LinearLayout.setOrientation(2012);
因为方法setOrientation接收的参数是一个整数,所以你可以传任意合法的整数---至少这在编译时不会有任何问题。它只会在运行时可能引发问题,但如你所知,开发者只关注程序能否编译成功,至于运行时,那是用户关心的事儿,因为开发者不一定使用他们所开发出的程序。
除了这个例子,在Android的API中到处可以看到这种API,比如设置View的可见性,设置Wifi状态等等。都是定义了整数集,然后用整数来做为参数,并寄希望开发者能传递整数集中定义的常量来作为参数。但如你所知,并不是每个人都那么的守规矩,如果每个人都能遵守规则,这个世界就真的和谐了,蛋扯远了。
因为开发者通常只能关注编译,所以如果能把这个规则应用在编译时,那么就会大大减少出错的可能。有兴趣的朋友可以去试试看,给这些接收整数参数的方法传一些“平常”的数值,比如2012,Integer.MAX_VALUE,Integer.MIN_VALUE等等,看会出现什么状况。
另外,如果开发者传递与常量定义一致的整数值,虽然编译运行都不会有错,但代码的可读性会大大的降低,比如:
LinearLayout.setOrientation(0);
LinearLayout.setOrientation(1);
这完全没有错,但是代码的阅读者和维护者通常都会蛋疼的。
当然,Android自身还是有保护措施的,如果对API传递不合法参数,不会造成其他影响,只是设置不能生效,但API会使用默认值,因为对于每个内置参数,都有相应的默认值。如LinearLayout的orientation,默认值就是LinearLayout.HORIZONTAL,所以如果对setOrientation()传入非法值,LinearLayout会保持水平排列,无其他影响。后面有个对Linearlayout的orientation做的试验。
另外,如果在LayoutXML文件中设置这些属性就不会有些问题,如:
<LinearLayout
android:orientation="vertical"
android:gravity="center">
因为XML布局会在编译时被处理,如果有非法的值,会有编译错误的。我想这也就是Android特别鼓励开发者用XML来制作所有的布局的一个原因吧。实例,三个没有设置指向的线性布局,默认是水平放置,在代码中设置了几个离谱的值,发现它们还是水平的,也就是说设置离谱的值不会出错,但也不起作用:运行结果如下:
代码如下:
<?xmlversion="1.0"encoding="utf-8"?>
<LinearLayoutxmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:gravity="center"
>
<LinearLayout
android:id="@+id/linearlayout_test_1"
android:layout_width="fill_parent"
android:layout_height="wrap_content">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="#ff00ff00"
android:background="#aa331155"
android:layout_weight="1"
android:textSize="18sp"
android:text="Microsoft"
/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="#ffff0000"
android:background="#aa117711"
android:layout_weight="1"
android:textSize="18sp"
android:text="Apple"
/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="#ff0000ff"
android:background="#aa774411"
android:layout_weight="1"
android:textSize="18sp"
android:text="Google"
/>
</LinearLayout>
<LinearLayout
android:id="@+id/linearlayout_test_2"
android:layout_width="fill_parent"
android:layout_height="wrap_content">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="#ff00ff00"
android:background="#aa331155"
android:layout_weight="1"
android:textSize="18sp"
android:text="Microsoft"
/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="#ffff0000"
android:background="#aa117711"
android:layout_weight="1"
android:textSize="18sp"
android:text="Apple"
/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="#ff0000ff"
android:background="#aa774411"
android:layout_weight="1"
android:textSize="18sp"
android:text="Google"
/>
</LinearLayout>
<LinearLayout
android:id="@+id/linearlayout_test_3"
android:layout_width="fill_parent"
android:layout_height="wrap_content">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="#ff00ff00"
android:background="#aa331155"
android:layout_weight="1"
android:textSize="18sp"
android:text="Microsoft"
/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="#ffff0000"
android:background="#aa117711"
android:layout_weight="1"
android:textSize="18sp"
android:text="Apple"
/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="#ff0000ff"
android:background="#aa774411"
android:layout_weight="1"
android:textSize="18sp"
android:text="Google"
/>
</LinearLayout>
</LinearLayout>
和:
packagecom.android.explorer;
importandroid.app.Activity;
importandroid.os.Bundle;
importandroid.widget.LinearLayout;
publicclassLinearLayoutTestextendsActivity{
@Override
publicvoidonCreate(BundlesavedInstanceState){
super.onCreate(savedInstanceState);
setContentView(R.layout.linearlayout_test);
LinearLayoutone=(LinearLayout)findViewById(R.id.linearlayout_test_1);
one.setOrientation(2012);
LinearLayouttwo=(LinearLayout)findViewById(R.id.linearlayout_test_2);
two.setOrientation(Integer.MAX_VALUE);
LinearLayoutthree=(LinearLayout)findViewById(R.id.linearlayout_test_3);
three.setOrientation(Integer.MIN_VALUE);
}
}
所以,如果LinearLayout使用Enum,就像这样定义:
publicclassLinearLayoutextendsViewGroup{
privateOrientationmOrientation;
publicenumOrientation{
HORIZONTAL,VERTICAL
};
publicvoidsetOrientation(Orientationdir){
mOrientation=dir;
}
}
然后这样使用:
importandroid.widget.LinearLayout;
LinearLayout.setOrientation(Orientation.HORIZONTAL);
LinearLayout.setOrientation(Orientation.VERTICAL);
那么,开发者就不会用错了,因为首先,它看到setOrientation所需要的参数是一个Orientation的枚举类型,就会自然的传送Orientation中定义的类型;另外,如果传其他的值,比如0或者1,编译器也不会答应的。
可悲的是Android中几乎所有的API都是以整数集的方式来定义的,所以就要时刻提醒自己和组里的人,一定要传所定义的整数集中的常量。
那么我们能做的,除了要传整数集中定义的常量,对于那些以整数集方式定义的API,以外。更重要的是当自己定义接口的时候,尽量用Enum而不要使用整数集。
还有一点需要注意的是,对于某些弱类型语言,也就是说在编译时不会对类型做特别细致的检查,比如C++,C等,那么即使使用了Enum,也不一定安全,因为对于C++和C来讲Enum中的常量与整数常量完全一样,连编译器都分不清。所以,对于这类语言,只能寄希望于开发者了。
Button.setEnabled(true);//enablethebutton
Button.setEnabled(false);//disablethebutton
但对于某些情况,当方法的名字不能体现Boolean参数的作用时,或是多于一个参数时,而方法的主要目的又不能体现Boolean参数的作用时,就很不清楚,比如:
//com/android/mms/data/ContactList.java
publicString[]getNumbers(boolean);
您能猜出来这个boolean变量是决定是否要为彩信对联系人做特殊的处理吗?您在使用这个API的时候能很快知道该传True还是该传False吗?当读到这些语句的时候:
String[]mms=getNumbers(true);
String[]sms=getNumbers(false);
您能知道True和False的含义与作用吗?至少我看到这样的代码时,如果不去跟踪它的实现,是猜不出来的。
但现实的问题是,API通常又需要从调用者那里得到做还是不做的决定。一个可行的途径是用方法来封装和隐藏,比如:
Button.setEnabled(true);//enablethebutton
Button.setEnabled(false);//disablethebutton
可以改成:
Button.enable();
Button.disable();
这是简单的情况,对于稍复杂的情况,比如后一个例子,可以添加另外的接口,而不是用重载方法,但内部的实现,可能还是需要重载,但是这就把问题缩小了,起码对使用者来说是隐藏的:
//com/android/mms/data/ContactList.java
publicString[]getNumbersForSms();
publicString[]getNumbersForMms();
这样一来,对外来讲就是良好的封装。内部实现可能还是需要一个类似这样的私有方法:
//com/android/mms/data/ContactList.java
publicString[]getNumbersForSms(){
returngetNumbers(false);
}
publicString[]getNumbersForMms(){
returngetNumbers(true);
}
privateString[]getNumbers(boolean){
//implementation
}
但至少把问题缩小化了,也可以加上注释来说明。就不必导致使用者来猜方法的用法和含义了。
相关文章
- xps 转 pdf android版,OakDoc XPS to PDF Converter(XPS文件转PDF格式工具)V2.2 正式版
- Android Studio 和 SDK 下载、安装和环境变量配置
- android退出app代码,Android应用退出代码各种方式
- mac 电脑android环境变量设置,mac上Android环境变量配置[通俗易懂]
- android短信验证码代码,Android短信验证码自动填写实现代码
- Android Q Beta 3 亮相 Google I/O'19
- android 杀进程 方法,android中杀死进程的方法
- android toast显示时间,Android Toast自定义显示时间「建议收藏」
- Android angle_android 界面悬停
- android scaleanimation动画,Android 动画之ScaleAnimation应用详解「建议收藏」
- Android Studio 教程:创建 Android 应用
- Android常见问题及开发经验总结(四)
- Android studio更新后出现警告:Warning:The `android.dexOptions.incremental` property is deprecated and it has
- 【Android 应用开发】Android 网络编程 API笔记 - java.net 包相关 接口 api
- 【Android 应用开发】Activity 任务亲和性 taskAffinity 设置 ( taskAffinity 属性 )
- 【Android 安装包优化】开启资源压缩 ( 资源压缩配置 | 启用严格模式的资源引用检查 | 自定义保留/移除资源配置 | 资源压缩效果 )
- 【Android 安装包优化】资源混淆 ( resources.arsc 资源映射表文件格式 | 头文件 数据格式 | 全局字符串池 数据格式 | 包数据 数据格式 | 包头 数据格式 )
- 【Android 插件化】Hook 插件化框架 ( 从 Hook 应用角度分析 Activity 启动流程 二 | AMS 进程相关源码 | 主进程相关源码 )
- 【Android 逆向】代码调试器开发 ( 代码调试器功能简介 | 设置断点 | 读写内存 | 读写寄存器 | 恢复运行 | Attach 进程 )
- Android 应用中拨打电话详解手机开发
- Android 应用安装过程分析详解手机开发
- Chrome OS现在能运行Android应用,无需移植
- Android系统基于Linux内核,实现移动设备突破极限。(android linux内核)
- android版本检测Android程序的版本检测与更新实现介绍
- Android笔记之:App应用之启动界面SplashActivity的使用
- Android笔记之:App应用之发布各广告平台版本的详解
- Android模拟器(JAVA)与C++socket通讯分享
- Android中应用界面主题Theme使用方法和页面定时跳转应用
- android使用urlconnection示例(get和post数据获取返回数据)