0x00 ArrayAdapter & ArrayList
ArrayAdapter:adapter即适配器,这个数组适配器一般用于列表中,如:ListView、Spinner。
ArrayList:List接口的可调整大小数组实现。实现所有可选的列表操作,并允许所有元素,包括null。除了实现List接口之外,该类还提供了一些方法来控制用于内部存储列表的数组大小。 (这个类大致相当于Vector,除了它是不同步的。)
ArrayList包含了两个重要的对象:elementData 和 size。
(01) elementData 是"Object[]类型的数组",它保存了添加到ArrayList中的元素。实际上,elementData是个动态数组,我们能通过构造函数 ArrayList(int initialCapacity)来执行它的初始容量为initialCapacity;如果通过不含参数的构造函数ArrayList()来创建ArrayList,则elementData的容量默认是10。elementData数组的大小会根据ArrayList容量的增长而动态的增长,具体的增长方式,请参考源码分析中的ensureCapacity()函数。
(02) size 则是动态数组的实际大小。
0x01 metadata
元数据是用来描述数据的数据(Data that describes other data)。
我再举一个例子。在电影数据库IMDB上可以查到每一部电影的信息。IMDB本身也定义了一套元数据,用来描述每一部电影。下面是它的一级元数据,每一级下面又列出了二级元数据,总共加起来,可以从100多个方面刻画一部电影:
Cast and Crew(演职人员)、Company Credits(相关公司)、Basic Data(基本情况)、Plot & Quotes(情节和引语)、Fun Stuff(趣味信息)、Links to Other Sites(外部链接)、Box Office and Business(票房和商业开发)、Technical Info(技术信息)、Literature(书面内容)、Other Data(其他信息)。
元数据最大的好处是,它使信息的描述和分类可以实现格式化,从而为机器处理创造了可能。
0x02 @param注释
注释格式:
1、单行(single-line)注释:“//……”
2、块(block)注释:“/……/”
3、文档注释:“/*……/”
4、javadoc 注释标签语法
@author 对类的说明 标明开发该类模块的作者
@version 对类的说明 标明该类模块的版本
@see 对类、属性、方法的说明 参考转向,也就是相关主题
@param 对方法的说明 对方法中某参数的说明
@return 对方法的说明 对方法返回值的说明
@exception 对方法的说明 对方法可能抛出的异常进行说明
0x03 intent
Intent是一个消息传递对象,您可以使用它从其他应用组件请求操作。尽管 Intent 可以通过多种方式促进组件之间的通信,但其基本用例主要包括以下三个:
-
启动 Activity:
Activity表示应用中的一个屏幕。通过将 Intent传递给startActivity(),您可以启动新的 Activity实例。Intent描述了要启动的 Activity,并携带了任何必要的数据。
如果您希望在 Activity 完成后收到结果,请调用startActivityForResult()。在 Activity 的 onActivityResult()回调中,您的 Activity 将结果作为单独的Intent对象接收。如需了解详细信息,请参阅 Activity 指南。 -
启动服务:
Service是一个不使用用户界面而在后台执行操作的组件。通过将Intent传递给startService(),您可以启动服务执行一次性操作(例如,下载文件)。Intent描述了要启动的服务,并携带了任何必要的数据。
如果服务旨在使用客户端-服务器接口,则通过将Intent传递给bindService(),您可以从其他组件绑定到此服务。如需了解详细信息,请参阅服务指南。
-
传递广播:
广播是任何应用均可接收的消息。系统将针对系统事件(例如:系统启动或设备开始充电时)传递各种广播。通过将Intent传递给sendBroadcast()、sendOrderedBroadcast()或 [sendStickyBroadcast()](https://developer.android.com/reference/android/co ntent/Context.html),您可以将广播传递给其他应用。
0x04 单例模式
单例模式的定义是它应该保证一个类仅有一个实例,同时这个类还必须提供一个访问该类的全局访问点。如下图是单例模式的结构图:
实现单例模式有以下几个关键点:
(1)其构造函数不对外开放,一般为private;
(2)通过一个静态方法或者枚举返回单例类对象;
(3)确保单例类的对象有且只有一个,尤其要注意多线程的场景;
(4)确保单例类对象在反序列化时不会重新创建对象;
通过将单例类的构造函数私有化,使得客户端不能通过new的形式手动构造单例类的对象。单例类会主动暴露一个公有的静态方法,客户端调用这个静态的方法获取到单例类的唯一实例。在获取这个单例类的时候需要确保这个过程是线程安全的。
引自:https://blog.csdn.net/happy_horse/article/details/51164262
0x05 Serializable
https://www.jianshu.com/p/fb903a1f8831
0x06 关键字 new
作用:https://blog.csdn.net/ljheee/article/details/52235915
声明一个变量来指向一个对象,即引用;
实例化一个类对象;
初始化一个类对象.
0x07 UUID
UUID是Universally Unique Identifier的缩写,它是在一定的范围内(从特定的名字空间到全球)唯一的机器生成的标识符。UUID具有以下涵义:来源
经由一定的算法机器生成:为了保证UUID的唯一性,规范定义了包括网卡MAC地址、时间戳、名字空间(Namespace)、随机或伪随机数、时序等元素,以及从这些元素生成UUID的算法。UUID的复杂特性在保证了其唯一性的同时,意味着只能由计算机生成。
非人工指定,非人工识别:UUID是不能人工指定的,除非你冒着UUID重复的风险。UUID的复杂性决定了“一般人“不能直接从一个UUID知道哪个对象和它关联。
在特定的范围内重复的可能性极小:UUID的生成规范定义的算法主要目的就是要保证其唯一性。但这个唯一性是有限的,只在特定的范围内才能得到保证,这和UUID的类型有关(参见UUID的版本)。
UUID是16字节128位长的数字,通常以36字节的字符串表示,示例如下:
3F2504E0-4F89-11D3-9A0C-0305E82C3301
其中的字母是16进制表示,大小写无关。
0x08 Fragment 生命周期
1 生命周期两张图
Fragment生命周期图:
imageFragment与Activity生命周期对比图:
image2 生命周期分析
1. 当一个fragment被创建的时候,它会经历以下状态.
- onAttach()
- onCreate()
- onCreateView()
- onActivityCreated()
2. 当这个fragment对用户可见的时候,它会经历以下状态。
- onStart()
- onResume()
3. 当这个fragment进入“后台模式”的时候,它会经历以下状态。
- onPause()
- onStop()
4. 当这个fragment被销毁了(或者持有它的activity被销毁了),它会经历以下状态。
- onPause()
- onStop()
- onDestroyView()
- onDestroy() // 本来漏掉类这个回调,感谢xiangxue336提出。
- onDetach()
5. 就像activitie一样,在以下的状态中,可以使用Bundle对象保存一个fragment的对象。
- onCreate()
- onCreateView()
- onActivityCreated()
6. fragments的大部分状态都和activitie很相似,但fragment有一些新的状态。
- onAttached() —— 当fragment被加入到activity时调用(在这个方法中可以获得所在的activity)。
- onCreateView() —— 当activity要得到fragment的layout时,调用此方法,fragment在其中创建自己的layout(界面)。
- onActivityCreated() —— 当activity的onCreated()方法返回后调用此方法
- onDestroyView() —— 当fragment中的视图被移除的时候,调用这个方法。
- onDetach() —— 当fragment和activity分离的时候,调用这个方法。
一旦activity进入resumed状态(也就是running状态),你就可以自由地添加和删除fragment了。因此,只有当activity在resumed状态时,fragment的生命周期才能独立的运转,其它时候是依赖于activity的生命周期变化的。
0x09 inflater.inflate
很多人在网上问LayoutInflater类的用法,以及inflate()方法参数的含义,现解释如下:
inflate()的作用就是将一个用xml定义的布局文件查找出来,注意与findViewById()的区别,inflate是加载一个布局文件,而findViewById则是从布局文件中查找一个控件。
- 获取LayoutInflater对象有三种方法
LayoutInflater inflater=LayoutInflater.from(this);
LayoutInflater inflater=getLayoutInflater();
LayoutInflater inflater=(LayoutInflater)this.getSystemService(LAYOUT_INFLATER_SERVICE);
- 关于LayoutInflater类inflate(int resource, ViewGroup root, boolean attachToRoot)方法三个参数的含义
resource:需要加载布局文件的id,意思是需要将这个布局文件中加载到Activity中来操作。
root:需要附加到resource资源文件的根控件,什么意思呢,就是inflate()会返回一个View对象,如果第三个参数attachToRoot为true,就将这个root作为根对象返回,否则仅仅将这个root对象的LayoutParams属性附加到resource对象的根布局对象上,也就是布局文件resource的最外层的View上,比如是一个LinearLayout或者其它的Layout对象。
attachToRoot:是否将root附加到布局文件的根视图上
0x0A Builder模式
一、 概述
1.1 定义
将一个复杂对象的构建与表示分离开来,使得同样的构建过程可以创建不同的表示。
1.2 使用场景
-
相同的方法,不同的执行顺序,产生不同的结果
-
多中属性都可以装配到一个对象中,但是产生的运行结果不同
-
复杂的产品类,或者产品类结果会随调用顺序发生改变
-
初始化一个对象特别复杂,如参数特别多,且很多参数都具有默认值时
1.3 分析
Builder模式将部件和组装过程分离,使得构建过程和部件都可以自由扩展,从而降低两者耦合度。
优点:
- 良好的封装性,使用Builder模式可以使客户端不必知道产品内部的组成细节
- Builder独立,易于扩展
缺点:
- 会产生冗余的Builder对象以及组装对象,消耗内存
二、 实现
2.1 示例
Builder模式非常易于上手,我们通过分析一个简单的demo来感受Builder模式的魅力。
EventBus
框架中的 EventBusBuilder
就采用 Builder 模式实现,我们来分析Builder模式的实现,EventBus 3.0 源码解析见 EventBus3.源码解析。
EventBusBuilder
是 EventBus
框架中的个性化配置类,从类名就可以看出这是一个 Builder 模式,通过Builder 对象来组装个性化设置 EventBus
的各项参数配置,包括 是否通过Log输出异常信息logSubscriberExceptions 等。下面看看 EventBusBuilder
的相关源码。
public class EventBusBuilder {
private final static ExecutorService DEFAULT_EXECUTOR_SERVICE = Executors.newCachedThreadPool();
boolean logSubscriberExceptions = true;//是否通过Log输出异常信息
boolean logNoSubscriberMessages = true;//是否通过Log输出发送事件无订阅者信息
boolean sendSubscriberExceptionEvent = true;//是否将内部异常信息通过SubscriberExceptionEvent发送出去
boolean sendNoSubscriberEvent = true;//是否将无订阅者的时间通过NoSubscriberEvent发送出去
boolean throwSubscriberException;//是否将内部异常信息通过EventBusException抛出
boolean eventInheritance = true;//发送事件是否支持调用所有父类及实现的接口
boolean ignoreGeneratedIndex;//是否忽略生成被观察者订阅的方法(通过反射)
boolean strictMethodVerification;//是否开启严格的方法验证,(public,只有一个参数,不为static及abstract),非法则均抛出异常
ExecutorService executorService = DEFAULT_EXECUTOR_SERVICE;//发送事件的线程池
List<Class<?>> skipMethodVerificationForClasses;//对类忽略方法校验,目前未实现
List<SubscriberInfoIndex> subscriberInfoIndexes;//通过annotation preprocessor生成的订阅者方法list
EventBusBuilder() {
}
/**
* 设置 logSubscriberExceptions,默认为true
* @param logSubscriberExceptions
* @return
*/
public EventBusBuilder logSubscriberExceptions(boolean logSubscriberExceptions) {
this.logSubscriberExceptions = logSubscriberExceptions;
return this;
}
/**
* 设置 logNoSubscriberMessages,默认为 true
* @param logNoSubscriberMessages
* @return
*/
public EventBusBuilder logNoSubscriberMessages(boolean logNoSubscriberMessages) {
this.logNoSubscriberMessages = logNoSubscriberMessages;
return this;
}
/**
* 设置 sendSubscriberExceptionEvent,默认为 true
* @param sendSubscriberExceptionEvent
* @return
*/
public EventBusBuilder sendSubscriberExceptionEvent(boolean sendSubscriberExceptionEvent) {
this.sendSubscriberExceptionEvent = sendSubscriberExceptionEvent;
return this;
}
/**
* 设置 sendNoSubscriberEvent,默认为 true
* @param sendNoSubscriberEvent
* @return
*/
public EventBusBuilder sendNoSubscriberEvent(boolean sendNoSubscriberEvent) {
this.sendNoSubscriberEvent = sendNoSubscriberEvent;
return this;
}
/**
* 设置 throwSubscriberException,默认为 false
* @param throwSubscriberException
* @return
*/
public EventBusBuilder throwSubscriberException(boolean throwSubscriberException) {
this.throwSubscriberException = throwSubscriberException;
return this;
}
/**
* 设置 eventInheritance,默认为 true
* @param eventInheritance
* @return
*/
public EventBusBuilder eventInheritance(boolean eventInheritance) {
this.eventInheritance = eventInheritance;
return this;
}
/**
* 设置 executorService
* @param executorService
* @return
*/
public EventBusBuilder executorService(ExecutorService executorService) {
this.executorService = executorService;
return this;
}
/**
* 设置 skipMethodVerificationForClasses
* @param clazz
* @return
*/
public EventBusBuilder skipMethodVerificationFor(Class<?> clazz) {
if (skipMethodVerificationForClasses == null) {
skipMethodVerificationForClasses = new ArrayList<>();
}
skipMethodVerificationForClasses.add(clazz);
return this;
}
/**
* 设置 ignoreGeneratedIndex
* @param ignoreGeneratedIndex
* @return
*/
public EventBusBuilder ignoreGeneratedIndex(boolean ignoreGeneratedIndex) {
this.ignoreGeneratedIndex = ignoreGeneratedIndex;
return this;
}
/**
* 设置 strictMethodVerification
* @param strictMethodVerification
* @return
*/
public EventBusBuilder strictMethodVerification(boolean strictMethodVerification) {
this.strictMethodVerification = strictMethodVerification;
return this;
}
/**
* 设置 subscriberInfoIndexes
* @param index
* @return
*/
public EventBusBuilder addIndex(SubscriberInfoIndex index) {
if(subscriberInfoIndexes == null) {
subscriberInfoIndexes = new ArrayList<>();
}
subscriberInfoIndexes.add(index);
return this;
}
/**
* 实现修改 EventBus 默认 EventBusBuilder(EventBus.defaultInstance)
* @return
*/
public EventBus installDefaultEventBus() {
synchronized (EventBus.class) {
if (EventBus.defaultInstance != null) {
throw new EventBusException("Default instance already exists." +
" It may be only set once before it's used the first time to ensure consistent behavior.");
}
EventBus.defaultInstance = build();
return EventBus.defaultInstance;
}
}
/**
* 通过 build 方式实现 EventBus 初始化
* @return
*/
public EventBus build() {
return new EventBus(this);
}
}
上述代码中,EventBusBuilder
类可以设置 EventBus
中的 * logSubscriberExceptions、logNoSubscriberMessages、sendSubscriberExceptionEvent* 等参数,这些参数统一存储在 EventBusBuilder
中。在调用 EventBusBuilder
类的 build()
函数时会创建 EventBus
,并且将各项配置用于到 EventBus
中去。
EventBusBuilder
采用 Builder 模式主要是初始化 EventBus
配置项参数复杂,并且很多参数具有默认值,在使用时可能只需要对部分参数进行修改,故采用 Builder 模式方便修改初始化。
2.2 小结
通过 2.1节 可以看出 Builder 模式使用非常方便,并且对于多参数属性初始化来说,极大的简化了工作。
在项目中合理的加入 Builder 模式吧~
三、小结
Builder 模式在 Android 开发中较为常用,通常作为配置类的构造器将配置的构建和表示分离开来,同时也实现了将配置类从目标类中抽离出来,避免了过多的 setter
方法。
Builder 模式比较常见的实现方式是通过调用链实现,这样使得代码简单易懂。
0x0B 单例模式
一、 概述
1.1 定义
确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例。
1.2 使用场景
确保某个类有且只有一个对象的场景,避免产生多个对象消耗过多的资源;或者某种类型的对象应该有且只有一个。
eg:创建一个对象需要消耗的资源过多,如访问IO和数据库资源。
1.3 关键点
构造函数不对外开放,一般为 private ;
通过一个静态方法或者枚举返回单例类对象;
确保单例类的对象有且只有一个,尤其是在 多线程 环境下;
确保单例类对象在反序列化时不会重新构建对象。
二、实现方式
2.1 懒汉模式
声明一个静态对象,并且在用户第一次调用 getInstance 时进行初始化。
2.1.1 分析
synchronized 关键字用于在多线程情况下保证单例对象唯一性
优点:单例只有在使用时才会被实例化,在一定程度上节约了资源
缺点:
每一次加载时需要及时进行实例化,响应速度稍慢
每次调用 getInstance() 都进行同步,造成不必要的同步开销
一般不建议使用
2.1.2 源码
public class Singleton {
private static Singleton instance;
private Singleton() {
}
public static synchronized Singleton getInstance() {
if (null == instance) {
instance = new Singleton();//加载时进行实例化
}
return instance;
}
}
2.2 饿汉模式
声明静态对象时就已经初始化。
2.2.1 分析
静态对象在声明的时候就已经初始化,从而保证了单例对象唯一性
优点: 每次调用 getInstance() 直接取出静态对象,不需要同步锁,响应速度快
缺点:初始化声明对象造成了一定资源的闲置浪费
2.2.2 源码
public class Singleton {
private static Singleton instance = new Singleton();
private Singleton() {
}
public static Singleton getInstance() {
return instance;
}
}
2.3 Double Check Lock (DCL) 模式
2.3.1 分析
优点:
资源利用率高
既能够在需要时才初始化单例,又能够保证线程安全,且单例对象初始化后调用 getInstance() 不进行同步锁
缺点:
第一次加载时响应稍慢
由于Java内存模型的原因偶尔会失败
instance = new Singleton(); 这句代码并不是一个原子操作,由于 Java 编译器允许处理器乱序执行汇编指令以及 JDK1.5 之前的 JVM (Java Memory Model, Java 内存模型) 中Cache、寄存器到主内存回写顺序的规定,该语句转换的汇编指令无法确保顺序执行
在 JDK1.5 之后,具体化了 volatile 关键字,因此可以直接定义成 private volatile static Singleton instance = null; ,就可以保证 instance 对象每次都是从主内存中读取
2.3.2 源码
public class Singleton {
private volatile static Singleton instance = null;
private Singleton() {
}
public static Singleton getInstance() {
if (null == instance) {
synchronized (Singleton.class) {
if (null == instance) {
instance = new Singleton();
}
}
}
return instance;
}
}
2.4 静态内部类单例模式
2.4.1 分析
强烈推荐使用
优点:
第一次加载 Singleton 类时并不会初始化 instance ,只有在第一次调用 getInstance() 时才会初始化
既能保证线程安全,也能保证单例对象的唯一性,同时也延迟了单例的实例化
2.4.2 源码
public class Singleton {
private Singleton() {
}
public static Singleton getInstance() {
return SingletonHolder.instance;
}
/**
* 静态内部类
*/
private static class SingletonHolder {
private static final Singleton instance = new Singleton();
}
}
2.5 枚举单例
2.5.1 分析
枚举单例模式最大的优点是写法简单,枚举在 Java 中与普通类是一样的,不仅能够有字段,还能够有自己的方法。最重要的是默认枚举实例的创建时线程安全的,并且在任何情况下它都是一个单例。
在上述的几种单例模式中,反序列化 的时候会出现重新创建对象的情况。**
上述示例中如果要杜绝单利对象在被反序列化时重新生成对象,则必须加入如下方法:
private Object readResolve() throws ObjectStreamException {
return instance;
}
2.5.2 源码
public enum Singleton {
INSTANCE;
public void doSomething() {
// ... do something
}
}
2.6 使用容器实现单例模式
2.6.1 分析
在程序初始化的时候,将多种单例类型注入到一个统一的管理类中,在使用时根据 key 获取对象对应类型的对象。
这种方式使得我们可以管理多种类型的单例,并且在使用时候可以通过统一的接口进行获取操作,降低了用户的使用成本,也对用户隐藏了具体实现,降低了耦合度。
2.6.2 源码
public class SingletonManager {
private static Map<String, Object> data = new HashMap<>();
public SingletonManager() {
}
public static void register(String key, Object instance) {
if (!data.containsKey(key)) {
data.put(key, instance);
}
}
public static Object get(String key) {
return data.get(key);
}
}
三、小结
所有的单例模式核心原理都是将构造函数私有化,并且通过静态方法获取一个唯一的实例。
需要注意的是在获取实例的过程中保证线程安全、防止反序列化导致重新生成实例对象等问题。
具体选择哪种方式实现单例模式还需要结合项目业务逻辑。
0X0C AndroidManifest之activity
<activity android:allowEmbedded=["true" | "false"]
android:allowTaskReparenting=["true" | "false"]
android:alwaysRetainTaskState=["true" | "false"]
android:autoRemoveFromRecents=["true" | "false"]
android:banner="drawable resource"
android:clearTaskOnLaunch=["true" | "false"]
android:configChanges=["mcc", "mnc", "locale",
"touchscreen", "keyboard", "keyboardHidden",
"navigation", "screenLayout", "fontScale",
"uiMode", "orientation", "screenSize",
"smallestScreenSize"]
android:documentLaunchMode=["intoExisting" | "always" |
"none" | "never"]
android:enabled=["true" | "false"]
android:excludeFromRecents=["true" | "false"]
android:exported=["true" | "false"]
android:finishOnTaskLaunch=["true" | "false"]
android:hardwareAccelerated=["true" | "false"]
android:icon="drawable resource"
android:label="string resource"
android:launchMode=["multiple" | "singleTop" |
"singleTask" | "singleInstance"]
android:maxRecents="integer"
android:multiprocess=["true" | "false"]
android:name="string"
android:noHistory=["true" | "false"]
android:parentActivityName="string"
android:permission="string"
android:process="string"
android:relinquishTaskIdentity=["true" | "false"]
android:screenOrientation=["unspecified" | "behind" |
"landscape" | "portrait" |
"reverseLandscape" | "reversePortrait" |
"sensorLandscape" | "sensorPortrait" |
"userLandscape" | "userPortrait" |
"sensor" | "fullSensor" | "nosensor" |
"user" | "fullUser" | "locked"]
android:stateNotNeeded=["true" | "false"]
android:taskAffinity="string"
android:theme="resource or theme"
android:uiOptions=["none" | "splitActionBarWhenNarrow"]
android:windowSoftInputMode=["stateUnspecified",
"stateUnchanged", "stateHidden",
"stateAlwaysHidden", "stateVisible",
"stateAlwaysVisible", "adjustUnspecified",
"adjustResize", "adjustPan"] >
. . .
</activity>
0x0D 使TextView中的文本滑动
首先在textView
组件中设置scrollbars
属性为vertical
:
<TextView
android:id="@+id/textView1"
android:layout_width="match_parent"
android:layout_height="200dp"
android:layout_weight="2.89"
android:scrollbars="vertical"
android:textColor="#ffffffff"
android:text=" "/>
然后在java里实例化TextView
的时候:
// obj表示实例化的TextView组件
obj.setMovementMethod(ScrollingMovementMethod.getInstance());
0x0E 圆角按钮
在res/drawable/
目录下添加一个这样的xml
:
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools">
<item android:state_pressed="true">
<shape>
<solid android:color="@color/light_goldenrod_yellow"/>
<corners android:radius="8dp"/>
</shape>
</item>
<!--<item android:state_pressed="false">-->
<!--<shape>-->
<!--<solid android:color="@android:color/holo_blue_dark"/>-->
<!--<corners android:radius="5dp"/>-->
<!--</shape>-->
<!--</item>-->
<item>
<shape android:shape="rectangle">
<corners android:radius="8dp"/>
<solid android:color="@android:color/transparent"/>
<stroke android:color="@color/dark_slate_gray"
android:width="2dp"/>
</shape>
</item>
</selector>
一些Tips
- layout_gravity 和 gravity的区别
android:gravity
sets the gravity of the contents (i.e. its subviews) of the View it's used on.
android:layout_gravity
sets the gravity of the View or Layout relative to its parent.
- scrollview
放在根视图, 可以使整个activity滚动.
-
TimePicker DatePicker可以弹出时间/日期选择器.
-
edittext添加输入字数限制:
xml中:
<EditText android:layout_width = "fill_parent"
android:layout_height = "wrap_content"
android:id = "@+id/mEdit"
android:maxLength = "10"/>
代码中:
view plain
EditText mEdit = (EditText)findViewById(R.id.mEdit);
InputFilter[] filters = {new LengthFilter(10)};
mEdit.setFilters(filters);
网友评论