Android UI组件之EditText 实现网站注册效果的校验

  时间过得太快,还没有什么感觉就到周末了,人生五十载,如梦亦如幻。不过我还没那么老。前两天曾说过,如果需要实现输入IP的功能,那么我们可以整一个自定义控件,然后对他进行事件监听,巴拉巴拉一大堆,好不容易做完了。后来想想,这是为了符合我们平常在PC上的习惯而去自定义的。那么如果只是单纯的为了输入一个IP地址,然后得到结果,不想去这么折腾,有没有什么好办法呢?

  很显然是有的,那就今天就来看看Android最常用的组件之一的EditText,虽然之前用的也是EditText,但是侧重点其实是自定义控件,那么今天就来着重说说EditText。对于每个使用网络的人来说,每天接触的最多的就是可编辑的文本框,所以搞好这个文本框也是重中之重。

  EditText继承自TextView,所以大部分属性都是继承自TextView。从官方文档的话中能看出:A TextView is a complete text editor, however the basic class is configured to not allow editing; see EditText for a subclass that configures the text view for editing.即TextView设计的时候就是一个text editor,但是呢在这个类中又不允许编辑,通过又整了一个EditText来实现可编辑的TextView。因此EditText没有自己独特的属性,绝大部分常用属性均是TextView的。

  1.常用属性。(在这不提那些布局属性,只讲一讲功能属性)

  android:cursorVisible:获得焦点是是否让cursor显示,默认是显示。建议不修改。

  android:capitalize:大写。有三种情况:每个字母都大写,每个单词的第一个字母大写,每个句子的第一个字母大写。已被android:inputType代替。

  android:numeric:数值的类型。已被android:inputType代替。

  android:digits:用来限定可以接受的字符。例: android:digits="0123456789."

  android:enabled:见文知意,用来控制EditText是否可编辑。

  android:hint:当没有获得焦点的时候显示的提示性的文字。

  android:imeOptions:通过这个参数来控制软键盘的enter键的功能。下面是常用的值及简单的翻译。

    

Constant Value Description
normal 0x00000000 default,系统自己来选择
actionUnspecified 0x00000000 default,系统自来选择
actionNone 0x00000001 换行
actionGo 0x00000002 "GO",
actionSearch 0x00000003 "search","搜索"
actionSend 0x00000004 "send","发送"
actionNext 0x00000005 "next","下一个"
actionDone 0x00000006 "Done","完成"

  android:inputType:因为它,好多属性已经被抛弃,所以自然地它的取值就灰常多。这个属性是系统用来判断弹出什么类型的软键盘,是数字的,还是字母的。通过这个属性可以提供更好的输入的体验,减少用户的错误输入。也为程序对字段的校验减轻了压力。既然有系统的便利,何乐而不为。

  

ConstantValueDescription
none 0x00000000 There is no content type. The text is not editable.这句话把我整的有点懵,实际测试时发现并不是不可编辑,而是和下面的text一样。
text 0x00000001 Just plain old text. Corresponds to TYPE_CLASS_TEXT | TYPE_TEXT_VARIATION_NORMAL.普通类型的软键盘。
textCapCharacters 0x00001001 Can be combined with text and its variations to request capitalization of all characters. Corresponds to TYPE_TEXT_FLAG_CAP_CHARACTERS.上面提到过的,每个字母都大写,所以一直是大写字母键盘。
textCapWords 0x00002001 Can be combined with text and its variations to request capitalization of the first character of every word. Corresponds to TYPE_TEXT_FLAG_CAP_WORDS.单词的第一个字母大写键盘,之后自动切换到text模式。
textCapSentences 0x00004001 Can be combined with text and its variations to request capitalization of the first character of every sentence. Corresponds to TYPE_TEXT_FLAG_CAP_SENTENCES.句子的第一个字母,然后切换到text。
textAutoCorrect 0x00008001 Can be combined with text and its variations to request auto-correction of text being input. Corresponds to TYPE_TEXT_FLAG_AUTO_CORRECT.设置纠错。
textAutoComplete 0x00010001 Can be combined with text and its variations to specify that this field will be doing its own auto-completion and talking with the input method appropriately. Corresponds toTYPE_TEXT_FLAG_AUTO_COMPLETE.这个一般多用AutoCompleteTextView。
textMultiLine 0x00020001 Can be combined with text and its variations to allow multiple lines of text in the field. If this flag is not set, the text field will be constrained to a single line. Corresponds toTYPE_TEXT_FLAG_MULTI_LINE.多行。
textImeMultiLine 0x00040001 Can be combined with text and its variations to indicate that though the regular text view should not be multiple lines, the IME should provide multiple lines if it can. Corresponds toTYPE_TEXT_FLAG_IME_MULTI_LINE.
textEmailAddress 0x00000021 Text that will be used as an e-mail address. Corresponds to TYPE_CLASS_TEXT |TYPE_TEXT_VARIATION_EMAIL_ADDRESS.Email键盘,即会有@键辅助输入。
textShortMessage 0x00000041 Text that is the content of a short message. Corresponds to TYPE_CLASS_TEXT |TYPE_TEXT_VARIATION_SHORT_MESSAGE.
textLongMessage 0x00000051 Text that is the content of a long message. Corresponds to TYPE_CLASS_TEXT |TYPE_TEXT_VARIATION_LONG_MESSAGE.
textPersonName 0x00000061 Text that is the name of a person. Corresponds to TYPE_CLASS_TEXT | TYPE_TEXT_VARIATION_PERSON_NAME.
textPostalAddress 0x00000071 Text that is being supplied as a postal mailing address. Corresponds to TYPE_CLASS_TEXT |TYPE_TEXT_VARIATION_POSTAL_ADDRESS.
textPassword 0x00000081 Text that is a password. Corresponds to TYPE_CLASS_TEXT | TYPE_TEXT_VARIATION_PASSWORD.密码,输入时显示成"."
textVisiblePassword 0x00000091 Text that is a password that should be visible. Corresponds to TYPE_CLASS_TEXT |TYPE_TEXT_VARIATION_VISIBLE_PASSWORD.
textFilter 0x000000b1 Text that is filtering some other data. Corresponds to TYPE_CLASS_TEXT | TYPE_TEXT_VARIATION_FILTER.
textPhonetic 0x000000c1 Text that is for phonetic pronunciation, such as a phonetic name field in a contact entry. Corresponds to TYPE_CLASS_TEXT | TYPE_TEXT_VARIATION_PHONETIC.
textWebEmailAddress 0x000000d1 Text that will be used as an e-mail address on a web form. Corresponds to TYPE_CLASS_TEXT |TYPE_TEXT_VARIATION_WEB_EMAIL_ADDRESS.
textWebPassword 0x000000e1 Text that will be used as a password on a web form. Corresponds to TYPE_CLASS_TEXT |TYPE_TEXT_VARIATION_WEB_PASSWORD.
number 0x00000002 A numeric only field. Corresponds to TYPE_CLASS_NUMBER | TYPE_NUMBER_VARIATION_NORMAL.对应上面的android:numeric.把原本numeric的值拆成了三个属性。
numberSigned 0x00001002 Can be combined with number and its other options to allow a signed number. Corresponds toTYPE_CLASS_NUMBER | TYPE_NUMBER_FLAG_SIGNED.
numberDecimal 0x00002002 Can be combined with number and its other options to allow a decimal (fractional) number. Corresponds to TYPE_CLASS_NUMBER | TYPE_NUMBER_FLAG_DECIMAL.
numberPassword 0x00000012 A numeric password field. Corresponds to TYPE_CLASS_NUMBER | TYPE_NUMBER_VARIATION_PASSWORD.
phone 0x00000003 For entering a phone number. Corresponds to TYPE_CLASS_PHONE.
datetime 0x00000004 For entering a date and time. Corresponds to TYPE_CLASS_DATETIME | TYPE_DATETIME_VARIATION_NORMAL.
date 0x00000014 For entering a date. Corresponds to TYPE_CLASS_DATETIME | TYPE_DATETIME_VARIATION_DATE.
time 0x00000024 For entering a time. Corresponds to TYPE_CLASS_DATETIME | TYPE_DATETIME_VARIATION_TIME.

  android:singleLine:是否单行。

  android:maxLength:最大长度。

  常用的就列这么多了,更多的还是建议用到的时候去看SDK文档,全记住也不现实。接下来是常用方法。

  2.常用方法.

  public final void setText (CharSequence text)

    Sets the string value of the TextView. TextView does not accept HTML-like formatting, which you can do with text strings in XML resource   files. To style your strings, attach android.text.style.* objects to a SpannableString, or see the Available Resource Types documentation for an   example of setting formatted text in the XML resource file.

  

  public CharSequence getText ()

    Return the text the TextView is displaying. If setText() was called with an argument of BufferType.SPANNABLE or BufferType.EDITABLE,   you can cast the return value from this method to Spannable or Editable, respectively. Note: The content of the return value should not be   modified. If you want a modifiable one, you should make your own copy first.

  public static final void selectAll (Spannable text)()(详见Selection类)

    Select the entire text.

  public static final void setSelection (Spannable text, int index)(详见Selection类)

    Move the cursor to offset index.

  public static void setSelection (Spannable text, int start, int stop)(详见Selection类)

    Set the selection anchor to start and the selection edge to stop.

  介绍完基础的属性和方法,接下来就来实战。于是结合前面的IP地址,来做一个接受字符串的编辑框,并且在离开时会自动判断是否符合格式,如果不符合格式继续停留在该控件内。(有点像我们注册网站的时候填写信息的样子,对了就往下继续,错了就给出提示)。

  程序的功能和流程比较简单。直接上代码并讲解一下吧。重点还是我犯过的错误和解决的办法。

  首先是布局文件activity_main.xml和效果图:

  在布局文件中有两个EditText,第一个用来接收输入IP地址,第二个是辅助控件,没有实际作用,只是为了体现程序流程,正确则跳到下一个控件,错误继续留在原控件,在这里通过android:digits和android:inputype来限制用户输入的内容必须输数字和".",来帮相减少错误。

 1 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
 2     xmlns:tools="http://schemas.android.com/tools"
 3     android:layout_width="match_parent"
 4     android:layout_height="match_parent"
 5     android:orientation="vertical"
 6     android:focusable="true"
 7     android:focusableInTouchMode="true"
 8     tools:context=".MainActivity" >
 9 
10     <EditText 
11         android:id="@+id/ipcontrol"
12         android:layout_width="fill_parent"
13         android:layout_height="wrap_content"
14         android:inputType="number"
15         android:imeOptions="actionDone"
16         android:digits="0123456789."
17         android:hint="如:10.10.10.10"
18         />
19     <EditText
20         android:id="@+id/mytext" 
21         android:layout_width="fill_parent"
22         android:layout_height="wrap_content"
23         android:inputType="text"
24         android:imeOptions="actionDone"
25         />
26     
27     <Button
28         android:id="@+id/confirm"
29         android:layout_width="fill_parent"
30         android:layout_height="wrap_content"
31         android:text="校验"
32         />
33 
34 </LinearLayout>

  技术分享

  接下来是逻辑代码。最初我的逻辑是这样的,在失去焦点的时候进行判断,如果不满足IP地址格式,则继续请求焦点。表面上看好像简单,但是实际上出现了一个非常哭笑不得的效果。先贴上最初的代码:

  

 1 import java.util.ArrayList;
 2 import java.util.List;
 3 
 4 import android.app.Activity;
 5 import android.os.Bundle;
 6 import android.view.Menu;
 7 import android.view.View;
 8 import android.view.View.OnClickListener;
 9 import android.view.View.OnFocusChangeListener;
10 import android.widget.Button;
11 import android.widget.EditText;
12 import android.widget.Toast;
13 
14 public class MainActivity extends Activity {
15     private EditText editText;
16     private EditText myEditText;
17     private Button button;
18     //private List<EditText> errorList;
19     //private boolean done = false;
20     @Override
21     protected void onCreate(Bundle savedInstanceState) {
22         super.onCreate(savedInstanceState);
23         setContentView(R.layout.activity_main);
24         editText = (EditText) this.findViewById(R.id.ipcontrol);
25         button = (Button) this.findViewById(R.id.confirm);
26         myEditText = (EditText) this.findViewById(R.id.mytext);
27         //errorList = new ArrayList<EditText>();
28         editText.setOnFocusChangeListener(new OnFocusChangeListener() {
29             
30             @Override
31             public void onFocusChange(View v, boolean hasFocus) {
32                 // TODO Auto-generated method stub
33                 if(hasFocus){
34                     //得到焦点
35                 }else{
36                     //失去焦点
37                     String reg = "((2[0-4]\\d|25[0-5]|[01]?\\d\\d?)\\.){3}(2[0-4]\\d|25[0-5]|[01]?\\d\\d?)";
38                     String ipString;
39                     if(editText.getText() != null && 
40                             editText.getText().toString().equals("") !=true )
41                     {
42                         ipString = editText.getText().toString();
43                         if(ipString.matches(reg)){
44                             Toast.makeText(getBaseContext(), ipString, Toast.LENGTH_LONG).show();
45                         }
46                         else{
47                             editText.setError("请输入正确的IP地址.");
48                             //errorList.add(editText);
49                             editText.requestFocus();
50                         }
51                     }
52                 }
53             }
54         });
55         myEditText.setOnFocusChangeListener(new OnFocusChangeListener() {
56             
57             @Override
58             public void onFocusChange(View v, boolean hasFocus) {
59                 // TODO Auto-generated method stub
60                 if(hasFocus){
61 //                    if(errorList.size()>0){
62 //                        EditText temp = errorList.remove(errorList.size()-1);
63 //                        temp.setFocusable(true);
64 //                        temp.requestFocus();
65 //                    }
66                 }
67             }
68         });
69         button.setOnClickListener(new OnClickListener() {
70             
71             @Override
72             public void onClick(View v) {
73                 // TODO Auto-generated method stub
74                 if(myEditText.getText().toString().equals("") ||
75                     editText.getText().toString().equals("")){
76                     Toast.makeText(getApplicationContext(), "必须输入", Toast.LENGTH_LONG).show();
77                 }
78             }
79         });
80     }
81 
82     @Override
83     public boolean onCreateOptionsMenu(Menu menu) {
84         // Inflate the menu; this adds items to the action bar if it is present.
85         getMenuInflater().inflate(R.menu.main, menu);
86         return true;
87     }
88 
89 }

  效果如下:

  技术分享

  正常的人都能看出来,这不科学啊,怎么同时又连个都在闪呢,简直让我无语,事实证明我想的太简单了,但我点了第二个控件时,系统把焦点移到下一个控件,但是还要处理上一个控件的焦点变化事件,而我又在上一个事件处理中请求焦点,于是系统就傻掉了,你到底要让焦点去哪儿?那么我们看看系统怎么决定的,实际上焦点在第二个控件中。第一个只有光标在闪,但并没有获得焦点。所以无法弹出键盘去修改内容。刚开始思维一直僵持在事件处理这儿,试了各种函数都没作用。后来想到如果一个东西一直错,那么解决思路的问题肯定有问题,思路走错方向了,所以一直对不了。于是只有转换思路,既然焦掉已经进入了写一个控件,我们不妨在下一个控件中来处理焦点,于是程序编程这样,定义一个EditText变量或者List,用来存储出错的控件,在下一个EditText控件获得焦点时判断,如果这个变量不为空,或者这个list的size大于0,说明有控件的值不对,需要修改,就将焦点还回去。于是修改买吗如下:

  

 1 package com.example.regtest;
 2 
 3 import java.util.ArrayList;
 4 import java.util.List;
 5 
 6 import android.app.Activity;
 7 import android.os.Bundle;
 8 import android.view.Menu;
 9 import android.view.View;
10 import android.view.View.OnClickListener;
11 import android.view.View.OnFocusChangeListener;
12 import android.widget.Button;
13 import android.widget.EditText;
14 import android.widget.Toast;
15 
16 public class MainActivity extends Activity {
17     private EditText editText;
18     private EditText myEditText;
19     private Button button;
20     private List<EditText> errorList;
21     //private boolean done = false;
22     @Override
23     protected void onCreate(Bundle savedInstanceState) {
24         super.onCreate(savedInstanceState);
25         setContentView(R.layout.activity_main);
26         editText = (EditText) this.findViewById(R.id.ipcontrol);
27         button = (Button) this.findViewById(R.id.confirm);
28         myEditText = (EditText) this.findViewById(R.id.mytext);
29         errorList = new ArrayList<EditText>();
30         editText.setOnFocusChangeListener(new OnFocusChangeListener() {
31             
32             @Override
33             public void onFocusChange(View v, boolean hasFocus) {
34                 // TODO Auto-generated method stub
35                 if(hasFocus){
36                     //得到焦点
37                 }else{
38                     //失去焦点
39                     String reg = "((2[0-4]\\d|25[0-5]|[01]?\\d\\d?)\\.){3}(2[0-4]\\d|25[0-5]|[01]?\\d\\d?)";
40                     String ipString;
41                     if(editText.getText() != null && 
42                             editText.getText().toString().equals("") !=true )
43                     {
44                         ipString = editText.getText().toString();
45                         if(ipString.matches(reg)){
46                             Toast.makeText(getBaseContext(), ipString, Toast.LENGTH_LONG).show();
47                         }
48                         else{
49                             editText.setError("请输入正确的IP地址.");
50                             errorList.add(editText);
51                             //editText.requestFocus();
52                         }
53                     }
54                 }
55             }
56         });
57         myEditText.setOnFocusChangeListener(new OnFocusChangeListener() {
58             
59             @Override
60             public void onFocusChange(View v, boolean hasFocus) {
61                 // TODO Auto-generated method stub
62                 if(hasFocus){
63                     if(errorList.size()>0){
64                         EditText temp = errorList.remove(errorList.size()-1);
65                         temp.setFocusable(true);
66                         temp.requestFocus();
67                     }
68                 }
69             }
70         });
71         button.setOnClickListener(new OnClickListener() {
72             
73             @Override
74             public void onClick(View v) {
75                 // TODO Auto-generated method stub
76                 if(myEditText.getText().toString().equals("") ||
77                     editText.getText().toString().equals("")){
78                     Toast.makeText(getApplicationContext(), "必须输入", Toast.LENGTH_LONG).show();
79                 }
80             }
81         });
82     }
83 
84     @Override
85     public boolean onCreateOptionsMenu(Menu menu) {
86         // Inflate the menu; this adds items to the action bar if it is present.
87         getMenuInflater().inflate(R.menu.main, menu);
88         return true;
89     }
90 
91 }

  这样修改后程序就能够正常运行了。焦点也不会出错。也提供了更多的选择,你可以选择每走一步就验证一个,对了才继续(采用变量),也可以用list记录所有值不对的控件(采用list列表),然后再button控件中去处理。

  写完代码我们发现代码居然这么少,比起之前那个裹脚布Android UI组件之自定义控件实现IP地址控件的代码少了不少。细心的话就能发现原因在于在如下代码中使用了正则表达式。String中的matches可以直接匹配正则表达式串。但是如果你喜欢用类的话,也可以用java.util.regex包中的Pattern和Matcher。具体实现可以Ikan文档。网上也有很多资料,使用正则表达式来进行字符串匹配和字符串校验,简直只能说爽。当然如果你还不了解的话推荐去学一下。这儿有一个三十分钟精通正则表达式。不过需要多少分钟看你自己了。当然还有一点要说的,学正则不要只看啊,最好的还是用自己拿笔实战几下,效率很高的。

  

 1 String reg = "((2[0-4]\\d|25[0-5]|[01]?\\d\\d?)\\.){3}(2[0-4]\\d|25[0-5]|[01]?\\d\\d?)";
 2                     String ipString;
 3                     if(editText.getText() != null && 
 4                             editText.getText().toString().equals("") !=true )
 5                     {
 6                         ipString = editText.getText().toString();
 7                         if(ipString.matches(reg)){
 8                             Toast.makeText(getBaseContext(), ipString, Toast.LENGTH_LONG).show();
 9                         }
10                         else{
11                             editText.setError("请输入正确的IP地址.");
12                             errorList.add(editText);
13                             //editText.requestFocus();
14                         }
15                     }

  小拓展

  在上面有个EditText错误提示,这个也是通过setError函数来设置(好吧,这是废话),但是有的人可能会问为什么有时候校验之后值错了,通过函数设置了报错提醒,但是没弹出那句话,只有一个图标,其实那是因为,设置了之后必须让该空间获得焦点,他才会自动让那句话出来,不然只能手动去点,才会有错误提示。而且你可以通过setError (CharSequence error, Drawable icon)自定义它的图标。

  EditText就写这么多了,一个下午就这样过去了。太阳也下山了。最后希望如果你有更好的方法来处理控件的焦点,望不吝赐教。

郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。