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

自定义组合控件实现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;
    //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(CharSequence 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,当然这个只能用来作为提升自己的小例子,真正在项目中使用的话要考虑到方方面面。写博客的习惯不能丢,虽然目前的文章质量很差,也就当做是用来作为学习笔记来翻看,如果大家看到什么错误的地方,欢迎指出,感激不尽^_^。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值