13.2 编辑文本的控件(EditText)

EditText是除了TextView控件外的另一个非常重要的控件,EditText是TextView的子类,因此,EditText拥有TextView的一切XML属性及方法。EditText与TextView的区别是EditText可以输入文本,而TextView通常只用于显示文本。

在前面的章节中已经多次使用到了EditText控件,读者也已经了解了EditText控件的基本使用方法,现在再回顾一下EditText控件在布局文本中的使用方法,代码如下:

<EditText android:layout_width="wrap_content"

    android:layout_height="wrap_content" android:text="输入文本的组件"

    android:textColor="#000000" android:background="#FFFFFF"

    android:padding="20dp" android:layout_margin="10dp" />

从上面的代码可以看出,EditText和TextView的使用方法完全一样,只需要将<TextView>换成<EditText>即可,几乎不需要做任何修改。当然,EditText的功能还远不止输入文本这么简单,本节将对EditText的一些高级功能进行详细讲解。

13.2.1 像QQ一样输入表情图像

源代码目录:src/ch13/EditTextface

EditText和TextView一样,也可以进行图文混排。但不同的是,TextView只用于显示图文混排效果,而EditText不仅可显示,也可混合输入文字和图像,让我们先回顾一下13.1.2小节中图13-2所示的QQ聊天输入框,在输入框中可以同时输入文字和表情图像。实际上,这种效果在Android SDK中只需要几行代码就可以实现。为了使读者更有学习的冲动,先来欣赏一下即将实现的效果,如图13-7所示。

 

▲图13-7 在EditText 控件中输入文字和图像

为了实现这个程序,首先来准备一些图像文件,这些图像文件会在EditText控件中与文本混合输入。本例准备了9个png图像文件(face1.png至face9.png),都放在了res/drawable目录中。

接下来在屏幕上放一个只能显示3行(也可显示多行)的EditText和一个Button,布局文件的代码如下:

源代码文件:src/ch13/EditTextface/res/layout/main.xml

<?xml version="1.0" encoding="utf-8"?>

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"

  android:orientation="vertical" android:layout_width="fill_parent"

  android:layout_height="fill_parent">

  <EditText android:id="@+id/edittext" android:layout_width="fill_parent"

    android:layout_height="wrap_content" android:lines="3" android:gravity="left top"/>

  <Button android:layout_width="wrap_content"

    android:layout_height="wrap_content" android:text="随机插入表情"

    android:onClick="onClick_RandomFace" android:layout_marginTop="10dp" />

</LinearLayout>

上面代码的<EditText>标签中将android:gravity属性值设为“left top”,以使输入的文本从左上角开始显示。如果不设置该属性,则输入的文本会从左侧中心位置开始显示(由于设置了android:line=“3”,因此,EditText可同时显示三行的内容,所以会存在这个问题,如果只显示一行,则不存在这个问题)。

<Button>标签的android:onClick属性指定了单击事件方法(onClick_RandomFace),在该方法中随机获得了face1.png至face9.png中任意一个图像资源的ID。最常用的方法是将这9个图像资源ID放到数组中,然后随机产生一个数组索引来获取相应的图像资源ID。但本例未采用这种方法,而是采用了直接通过Java反射技术从R.drawable类中获得图像资源ID的方法。这种方法的好处是一旦图像资源非常多时,可以不需要在数组中逐个定义就可以获得任意的图像资源ID。

在13.1.2小节使用了<img>标签来插入图像,虽然在EditText控件中插入图像也可采用这种方法,但本例使用了另外一种更简单的方法,就是使用android.text.style.ImageSpan类来直接插入图像。下面来看看具体的实现代码。

源代码文件:src/ch13/EditTextface/src/mobile/android/edittext/face/Main.java

// “随机插入表情”按钮的单击事件方法

public void onClick_RandomFace(View view)

{

  // 随机产生1至9的整数

  int randomId = 1 + new Random().nextInt(9);

  try

  {

    // 根据随机产生的1至9的整数从R.drawable类中获得相应资源ID(静态变量)的Field对象

    Field field = R.drawable.class.getDeclaredField("face" + randomId);

    // 获得资源ID的值,也就是静态变量的值

    int resourceId = Integer.parseInt(field.get(null).toString());

    // 根据资源ID获得资源图像的Bitmap对象

    Bitmap bitmap = BitmapFactory.decodeResource(getResources(),resourceId);

    // 根据Bitmap对象创建ImageSpan对象

    ImageSpan imageSpan = new ImageSpan(this, bitmap);

    // 创建一个SpannableString对象,以便插入用ImageSpan对象封装的图像

    SpannableString spannableString = new SpannableString("face");

    // 用ImageSpan对象替换face

    spannableString.setSpan(imageSpan, 0, 4,Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);

    // 将随机获得的图像追加到EditText控件的最后

    edittext.append(spannableString);

  }

  catch (Exception e)

  {

  }

}

编写上面代码需要了解如下几点。

由于R.drawable中的资源ID都是public的静态变量,因此,可直接使用Field.get方法获得这些变量的值。如果是private或protected的变量,需要使用field.setAccessible(true)方法设置变量值的访问权限才可以读写这些变量的值。

使用Field.get方法获得变量值时,如果是静态变量,Field.get方法的参数值设为null即可。如果不是静态变量,需要为Field.get方法指定一个变量所在类的对象作为参数值。

由于EditText类不能直接插入Span对象,因此,需要先使用SpannableString对象来封装Span对象(如本例中的ImageSpan对象),再将SpannableString对象插入到EditText控件中。

13.2.2 在EditText中输入特定的字符

源代码目录:src/ch13/EditTextSpecialCharacter

EditText控件可以通过多种方式指定允许输入的字符,例如,如果只想输入数字(0~9),可以使用如下3种方法:

将<EditText>标签的android:digits属性值设为0123456789。

将<EditText>标签的android:numeric属性值设为integer。

将<EditText>标签的android:inputType属性值设为number。

本例将分别使用上面所述的3个属性来限制EditText控件的输入字符,XML布局文件的代码如下:

源代码文件:src/ch13/EdittextSpecialCharacter/res/layout/main.xml

<?xml version="1.0" encoding="utf-8"?>

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"

  android:orientation="vertical" android:layout_width="fill_parent"

  android:layout_height="fill_parent" android:gravity="center_horizontal">

  <TextView android:layout_width="wrap_content"

    android:layout_height="wrap_content" android:text="使用android:digits属性(输入

    数字)" />

  <EditText android:layout_width="200dp" android:layout_height="wrap_content"

    android:layout_margin="10dp" android:digits="0123456789" />

  <TextView android:layout_width="wrap_content"

    android:layout_height="wrap_content" android:text="使用android:digits属性(输入

    26个小写字母)" />

  <EditText android:layout_width="200dp" android:layout_height="wrap_content"

    android:layout_margin="10dp" android:digits="abcdefghijklmnopqrstuvwxyz" />

  <TextView android:layout_width="wrap_content"

    android:layout_height="wrap_content" android:text="使用android:inputType属性(输

    入数字)" />

  <EditText android:layout_width="200dp" android:layout_height="wrap_content"

    android:layout_margin="10dp" android:inputType="number textCapCharacters" />

  <TextView android:layout_width="wrap_content"

    android:layout_height="wrap_content" android:text="使用android:inputType属性(输

    入E-mail)" />

  <EditText android:layout_width="200dp" android:layout_height="wrap_content"

    android:layout_margin="10dp" android:inputType="textEmailAddress" />

  <TextView android:layout_width="wrap_content"

    android:layout_height="wrap_content" android:text="使用android:numeric属性(输入

    有符号的浮点数)" />

  <EditText android:layout_width="200dp" android:layout_height="wrap_content"

    android:layout_margin="10dp" android:numeric="decimal signed"/>

</LinearLayout>

如果使用android:inputType属性设置允许输入的字符,当焦点落在该EditText控件上,显示的虚拟键盘会随着inputType属性值的不同而不同,例如,图13-8是用于输入数字的虚拟键盘,图13-9是用于输入E-mail的虚拟键盘。要注意的是,用于输入E-mail的EditText控件并不会限制输入非E-mail的字符,只是在虚拟键盘上多了一个“@”键而已。关于android:inputType和android:numeric属性的其他可选值,读者可以参阅官方的文档。

 

▲图13-8 输入数字的虚拟键盘

 

▲图13-9 输入E-mail的虚拟键盘

多学一招:阻止界面上移

细心的读者会发现,在如图13-8所示界面的后面几个EditText控件中输入文本时会弹出系统软键盘,并且整个界面会向上移,以便显示出当前正处于焦点的EditText控件。如果出于某种需要,不想让界面向上移,可以使用getWindow().setSoftInputMode方法将输入法模式设为WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE,代码如下:

getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE);

13.2.3 AutoCompleteTextView(自动完成输入内容的控件)

源代码目录:src/ch13/AutoCompleteTextView

也许有的读者会想,有没有搞错,AutoCompleteTextView看样子像是TextView的“近亲”,怎么放到EditText部分了。要是这么想,大家可被AutoCompleteTextView的名字给唬住了。实际上,AutoCompleteTextView是对EditText的扩展(EditText是AutoCompleteTextView的直接父类),也属于文本编辑控件,只是该控件可以像Google搜索框一样在编辑框下方列出可供选择的列表。由此看来,AutoCompleteTextView改成AutoCompleteEditText更合适。

AutoCompleteTextView控件在布局文件中使用<AutoCompleteTextView>标签定义,该标签的使用方法与<EditText>标签相同。如果要让AutoCompleteTextView控件显示辅助输入列表,需要使用AutoCompleteTextView.setAdapter方法指定一个Adapter对象Adapter 对象是类名以Adapter 开头的类创建的对象,在后面的列表控件和数据库部分会详细介绍Adapter 对象的详细使用方法。

除了AutoCompleteTextView控件外,还可以使用MultiAutoCompleteTextView控件来完成连续输入的功能。也就是说,当输入完一个字符串后,在该字符串后面输入一个逗号(,),在逗号前后可以有任意多个空格,然后再输入一个字符串(例如“goo”),仍然会显示辅助输入的列表,但要使用MultiAutoCompleteTextView类的setTokenizer方法指定MultiAutoCompleteTextView. CommaTokenizer对象(该对象表示输入多个字符串时的分隔符为逗号)。

下面的代码是一个使用AutoCompleteTextView和MultiAutoCompleteTextView控件的完整示例。

源代码文件:src/ch13/AutoCompleteTextView/src/mobile/android/autotext/Main.java

public class Main extends Activity

{  

  @Override

  public void onCreate(Bundle savedInstanceState)

  {

    super.onCreate(savedInstanceState);

    setContentView(R.layout.main);

    String[] autoString = new String[]

    { "联合国", "联合国安理会", "联合国五个常任理事国", "bb", "bcd", "bcdf", "Google", "Google Map", "Google Android" };

    // 创建与数组关联的Adapter对象,其中ArrayAdapter类的第2个参数需要指定列表中每一个

    // 列表项使用的视图资源ID,第3个参数指定一个数组对象

    ArrayAdapter<String> adapter = new ArrayAdapter<String>(this,

        android.R.layout.simple_dropdown_item_1line, autoString);

    // AutoCompleteTextView

    AutoCompleteTextView autoCompleteTextView = (AutoCompleteTextView) findViewById (R.id.autoCompleteTextView);

    autoCompleteTextView.setAdapter(adapter);

    // MultiAutoCompleteTextView

    MultiAutoCompleteTextView multiAutoCompleteTextView = (MultiAutoCompleteTextView) findViewById(R.id.multiAutoCompleteTextView);

    multiAutoCompleteTextView.setAdapter(adapter);

    multiAutoCompleteTextView

        .setTokenizer(new MultiAutoCompleteTextView.CommaTokenizer());

  }

}

运行本例后,在屏幕的第1个编辑框中输入“联合”后,就会显示如图13-10所示的选择列表,在第2个编辑框中输入“联合国,”后,再输入“goo”,会显示如图13-11所示的选择列表。

 

▲图13-10 输入“联合”后显示的辅助输入列表

 

▲图13-11 输入“联合国,goo”后显示的辅助输入列表