您的位置:首页 > 其它

组合控件--实现Ip地址的输入与校验

2017-03-29 13:05 756 查看

自定义组合控件实现Ip地址的输入与校验

额,为什么要写这个东西呢,正常市面上的软件真想不到会让用户去输入Ip地址,但是为了准备比赛,看到有Ip地址校验的一道题目,于是说干就干试着模仿着写一个组合控件,实现上述的功能。

题目:



上面的题目上的效果看上去还是可以的吧,下面看一下我的版本的运行效果:



偷个懒,没有美化……

下面进入主题,介绍一下实现了那些功能,以及在书写代码的过程中所遇到的问题:

实现的功能:

Ip地址的合法性校验 每一个区段 0-255

焦点的自动获取,当每一个区段中的三个数输入完毕后自动跳转到下一个Ip区段

当输入的数据不合法时,会播放左右晃动动画进行提示用户

对于组合控件也算是涉及到自定义View的相关内容了,这里面用于存放EditText的ViewGroup我们继承自 已有的LinearLayout,这样就不需要处理ViewGroup的测量和布局了。

haha,适当的偷懒^_^

实现代码:

/**
* 组合控件Ip地址输入框
* @author wangke
*/

public class IpEditText extends LinearLayout {

private EditText mEditText1;
private EditText mEditText2;
private EditText mEditText3;
private EditText mEditText4;

4000
//EditText在父View中对应的下标的位置
private int[] edtIndex = new int[]{0, 2, 4, 6};

public IpEditText(Context context) {
super(context);
InitUI();
//检查Ip地址的输入是否正确
checkInput();

}

public IpEditText(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
InitUI();
//检查Ip地址的输入是否正确
checkInput();
}

public IpEditText(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
InitUI();
//检查Ip地址的输入是否正确
checkInput();
}

/**
* 初始化控件布局
*/
private void InitUI() {

mEditText1 = new EditText(getContext());
mEditText1.setTag(0);
mEditText2 = new EditText(getContext());
mEditText2.setTag(1);
mEditText3 = new EditText(getContext());
mEditText3.setTag(2);
mEditText4 = new EditText(getContext());
mEditText4.setTag(3);

TextView tvPoint1 = new TextView(getContext());
tvPoint1.setText(".");
tvPoint1.setTextSize(30);

TextView tvPoint2 = new TextView(getContext());
tvPoint2.setText(".");
tvPoint2.setTextSize(30);

TextView tvPoint3 = new TextView(getContext());
tvPoint3.setText(".");
tvPoint3.setTextSize(30);

addView(mEditText1); //0
addView(tvPoint1);
addView(mEditText2); //2
addView(tvPoint2);
addView(mEditText3); //4
addView(tvPoint3);
addView(mEditText4); //6

//遍历子View设置布局样式
for (int i = 0; i < getChildCount(); i++) {

if (getChildAt(i) instanceof EditText) {

((EditText) getChildAt(i)).setGravity(Gravity.CENTER);
//设置EditText最大的输入字符数为3
((EditText) getChildAt(i)).setFilters(new InputFilter[]{new InputFilter.LengthFilter(3)});
LinearLayout.LayoutParams params = (LayoutParams) getChildAt(i).getLayoutParams();

((EditText) getChildAt(i)).setInputType(InputType.TYPE_CLASS_NUMBER);
params.weight = 1;
params.width = 0;

}
}
}

private int currentFoucsIndex = 0;

/**
* 检查Ip地址的输入是否正确
*/
private void checkInput() {

for (int i = 0; i < getChildCount(); i++) {

if (getChildAt(i) instanceof EditText) {

final EditText edtText = (EditText) getChildAt(i);

edtText.setOnFocusChangeListener(new OnFocusChangeListener() {
@Override
public void onFocusChange(View v, boolean hasFocus) {

if (hasFocus == true) {

int tag = (int) v.getTag();
//记录当前获取焦点的下标
currentFoucsIndex = tag;
}

}
});

/**
* EdtText内容发生改变的监听
*/
edtText.addTextChangedListener(new TextWatcher() {
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {

}

@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {

Log.i("wk", this.toString() + "改变的:" + s + "count:" + count);

if (s.length() > 0) {

int ipValue = Integer.valueOf(s.toString());

if (ipValue > 255 || ipValue < 0) {

Toast.makeText(getContext(), "输入的Ip不合法!", Toast.LENGTH_SHORT).show();

//添加警告动画
addWarnAnim(edtText);

} else {

//如果输入的长度为3表示当前Ip的区段已经输入完成,将焦点递给下一个EditText
if (s.toString().length() == 3) {
currentFoucsIndex += 1;

if (currentFoucsIndex <= edtIndex.length - 1) {

//申请当前正在输入的下一个EditText获取焦点
getChildAt(edtIndex[currentFoucsIndex]).requestFocus();

}

}
}

} else {

Toast.makeText(getContext(), "不能为空!", Toast.LENGTH_SHORT).show();
}

}

@Override
public void afterTextChanged(Editable s) {

}
});

}

}

}

/**
* 创建一个用于提醒输入出错的动画效果
*
* @param editText
*/
private void addWarnAnim(EditText editText) {
AnimationSet animationSet = new AnimationSet(true);
animationSet.setRepeatCount(8);
TranslateAnimation rightAnim = new TranslateAnimation(0, 30, 0, 0);
rightAnim.setDuration(60);
TranslateAnimation leftAnim = new TranslateAnimation(30, 0, 0, 0);
rightAnim.setDuration(60);
animationSet.addAnimation(rightAnim);
animationSet.addAnimation(leftAnim);
editText.startAnimation(animationSet);
}

/**
* 判断当前Ip地址的每个区段是否都不为空
*
* @return 为空返回true/不为空返回false
*/
public Boolean isHaveEmpty() {

Boolean isHaveEmpty = false;

for (int i = 0; i < getChildCount(); i++) {

if (getChildAt(i) instanceof EditText) {

EditText edt = (EditText) getChildAt(i);

if (edt.getText().toString().equals("")) {

isHaveEmpty = true;

break;

} else {
isHaveEmpty = false;
}
}

}
return isHaveEmpty;
}

/**
* 获取用户当前输入的Ip地址
*
* @return
*/
public String getIpAddress() {

StringBuffer sb = new StringBuffer();
for (int i = 0; i < getChildCount(); i++) {

if (getChildAt(i) instanceof EditText) {

EditText edtText = (EditText) getChildAt(i);

sb.append(edtText.getText());
if (i != getChildCount() - 1) {
sb.append(".");
}
}
}
return sb.toString();
}
}


感觉上面的代码没有什么好解释的,注释写的也比较明确。下面记录一下

书写代码时遇到的问题:

AddView时出现的问题,当添加显示“.”的TextView的时候,由于考虑到既然都是一样的那么就new一个好了然后直接添加这一个,想法太天真,于是报出了下面的错误:

Caused by: java.lang.IllegalStateException: The specified child already has a parent. You must call removeView() on the child's parent first.


在网上查了下资料,发现: 当一个View已经作为一个子View添加到父View中了,再次添加到当前的View就会出现上面的错误。

将charsequence直接强转成String,于是就报错了%>_<%

java.lang.ClassCastException: android.text.SpannableStringBuilder cannot be cast to java.lang.String


当返回值为父类,而我需要的值为子类的时候,才能够进行强制转换不出现错误,也就是所说的向下转型,查看了下String类的源码发现String是实现了CharSequence接口。也就是说只要按照向下转型的原则就不会出现上面的错误(╮(╯▽╰)╭,有必要花时间把Java基础再复习一遍了)。

使用editText.requestFocus()来进行焦点的获取

EditText中setFilters的使用方法:

对于限制EditText输入长度,对于在Xml中直接加上
android:maxLength="3"
属性即可,在代码中进行设置需要通过EditText的setFilters方法。

InputFilter的作用是对输入的字符进行过滤处理,可以实现InputFilter接口进行指定任意的匹配规则。

使用方法:

editText.setFilters(new InputFilter[]{new InputFilter.LengthFilter(3)});


setFilters( )的参数是一个InputFilter类型的数组,如果直接添加限制输入字符长度的规则,直接new InputFilter.LengthFilter(3),使用系统提供好的方法。

下面让我们定义一个过滤A-Z之外字符的InputFilter,运行效果:



代码 :

/**
* 过滤A-Z以外的字符
*/
class MyFilter implements InputFilter{

@Override
public CharSequence filter(CharSe
bc4a
quence source, int start, int end, Spanned dest, int dstart, int dend) {

//A-Z
ArrayList<Character> filterChar = new ArrayList<>();

for(int i=65;i<=90;i++){

filterChar.add((char) i);
}

//source 一次从键盘上录入的字符的串的长度
Log.i("wk","输入字符长度:"+source.toString().length());

if(source.length()>1) {
//只允许单个字符的输入
return "";
}

if(source.length()>0) {

if (filterChar.contains(source.charAt(0))) {
//如果匹配成功返回null
return null;
}
else{
//匹配失败返回false
return "";
}
}

return null;

}
}


filter(CharSequence source, int start, int end, Spanned dest, int dstart, int dend) 方法中的参数含义:

source :当前新输入的字符串

start :当前输入字符串的起始下标,通常为0

end : 当前输入字符串的终点下标,长度为source.length-1

dest : 下一次输前的文本框中的字符内容

dstart : 原内容的起始下标,通常为0

dend : 原内容的终点下标,通常为 dest.length()-1

这样一个简单的组合控件就实现了,还有很多不足,比如Ip地址的区段只有输入三位数才能自动跳转到下一个EditText,当然这个只能用来作为提升自己的小例子,真正在项目中使用的话要考虑到方方面面。写博客的习惯不能丢,虽然目前的文章质量很差,也就当做是用来作为学习笔记来翻看,如果大家看到什么错误的地方,欢迎指出,感激不尽^_^。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: