美文网首页
Android自定义密码输入框

Android自定义密码输入框

作者: shada | 来源:发表于2017-08-17 14:55 被阅读209次

    先看最终效果图
    可在布局文件中配置各种属性,如密码框个数,密码框圆角半径,颜色,中间圆半径和大小,padding值也有效。


    image.png

    完整代码如下(有注释):

    public class PwdInput extends View implements View.OnKeyListener {
        private static final String TAG = PwdInput.class.getSimpleName();
        private static final int DEFAULT_PWD_LENGTH = 6;
        private int mPwdLength;
        private int itemWidth;
        private int itemHeight;
        private Paint mPaint;
        private RectF mRectF;
        private InputMethodManager inputMethodManager;
        private ArrayList<String> mPasswords;
        private TextPaint mTextPaint;
        private ArrayList<String> mNeedHidden = new ArrayList<>();
        private OnInputFinished onInputFinished;
        private Paint circlePaint;
        private Handler mHandler = new Handler() {
            @Override
            public void dispatchMessage(Message msg) {
                super.dispatchMessage(msg);
                if (msg.what == 100) {
                    mNeedHidden.add((String) msg.obj);
                    invalidate();
                }
            }
        };
        private float textSize;
        private int itemBorderColor;
        private int itemCircleColor;
        private Rect textRect;
        private float mRadius;
        private float roundRectRaidus;
        private float mStrokeWidth;
    
        public void setOnInputFinished(OnInputFinished onInputFinished) {
            this.onInputFinished = onInputFinished;
        }
    
        public PwdInput(Context context) {
            this(context, null);
        }
    
        public PwdInput(Context context, @Nullable AttributeSet attrs) {
            this(context, attrs, 0);
        }
    
        public PwdInput(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
            super(context, attrs, defStyleAttr);
            TypedArray array = context.obtainStyledAttributes(attrs, R.styleable.PwdInput);
            //密码长度
            mPwdLength = array.getInteger(R.styleable.PwdInput_pwd_length, DEFAULT_PWD_LENGTH);
            //输入框文字大小
            textSize = array.getDimension(R.styleable.PwdInput_pwd_textsize, dp2px(15));
            //密码框颜色
            itemBorderColor = array.getColor(R.styleable.PwdInput_pwd_item_border_color, Color.BLACK);
            //圆点颜色
            itemCircleColor = array.getColor(R.styleable.PwdInput_pwd_circle_color, Color.MAGENTA);
            //圆点半径
            mRadius = array.getDimension(R.styleable.PwdInput_pwd_circle_radius, dp2px(10));
            //密码框圆角半径
            roundRectRaidus = array.getDimension(R.styleable.PwdInput_pwd_round_rect_radius, dp2px(4));
            mStrokeWidth=array.getDimension(R.styleable.PwdInput_pwd_round_rect_border_width,dp2px(0.5f));
            array.recycle();
            init();
        }
    
        private void init() {
            //键盘输入监听
            setOnKeyListener(this);
            //获取焦点
            setFocusable(true);
            setFocusableInTouchMode(true);
            requestFocus();
            //键盘管理
            inputMethodManager = (InputMethodManager) getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
            //密码框 paint
            mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
            mPaint.setColor(itemBorderColor);
            mPaint.setStyle(Paint.Style.STROKE);
            mPaint.setStrokeWidth(mStrokeWidth);
            //设置阴影效果
            mPaint.setShadowLayer(3, 3, 3, Color.GRAY);
            setLayerType(LAYER_TYPE_SOFTWARE, null);
            //密码框rect
            mRectF = new RectF();
            //存放密码集合
            mPasswords = new ArrayList<>();
            //密码明文paint
            mTextPaint = new TextPaint(TextPaint.ANTI_ALIAS_FLAG);
            mTextPaint.setTextSize(textSize);
            //密码暗文paint
            circlePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
            circlePaint.setColor(itemCircleColor);
            //测量文字所需rect
            textRect = new Rect();
            //密码框宽高 默认宽高一样
            itemWidth = getMaxItemWidth();
            itemHeight = getMaxItemWidth();
        }
    
    
        @Override
        protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
            Log.d(TAG, "onMeasure: " + getPaddingBottom());
            //整体宽度等于密码框宽度和密码框之间间隙总和再加上padding大小
            int widthMode = MeasureSpec.getMode(widthMeasureSpec);
            int widthSize = MeasureSpec.getSize(widthMeasureSpec);
            int heightMode = MeasureSpec.getMode(heightMeasureSpec);
            int heightSize = MeasureSpec.getSize(heightMeasureSpec);
            int width, height;
            if (widthMode != MeasureSpec.EXACTLY) {//宽度未知
                //宽度未知时
                //密码框之间的间隙等于密码框大小的1/5
                width = itemWidth * mPwdLength + (mPwdLength - 1) * itemWidth / 5 + getPaddingLeft() + getPaddingRight();
                if (heightMode == MeasureSpec.EXACTLY) {//高度已知
                    itemHeight = heightSize - getPaddingTop() - getPaddingBottom();
                    height = heightSize;
                } else {//高度未知
                    height = itemHeight + getPaddingTop() + getPaddingBottom();
                }
            } else {//宽度已知
                itemWidth = (widthSize - getPaddingLeft() - getPaddingRight()) * 5 / (mPwdLength * 6 - 1);
                width = widthSize;
                if (heightMode == MeasureSpec.EXACTLY) {//高度已知
                    itemHeight = heightSize - getPaddingTop() - getPaddingBottom();
                    height = heightSize;
                } else {//高度未知
                    height = itemHeight + getPaddingBottom() + getPaddingTop();
                }
            }
            setMeasuredDimension(width, height);
        }
    
        @Override
        protected void onDraw(Canvas canvas) {
            super.onDraw(canvas);
            Log.d(TAG, "onDraw: ");
            //绘制圆角矩形
            for (int i = 0; i < mPwdLength; i++) {
                mRectF.left = i * (itemWidth + itemWidth / 5) + getPaddingLeft() + mStrokeWidth / 2;
                mRectF.top = getPaddingTop() + mStrokeWidth / 2;
                mRectF.right = mRectF.left + itemWidth - mStrokeWidth;
                mRectF.bottom = mRectF.top + itemHeight - mStrokeWidth;
                canvas.drawRoundRect(mRectF, roundRectRaidus, roundRectRaidus, mPaint);
            }
            //绘制文字
            for (int i = 0; i < mPasswords.size(); i++) {
                String text = mPasswords.get(i);
                textRect.setEmpty();
                //测量文字宽高
                mTextPaint.getTextBounds(text, 0, text.length(), textRect);
                //绘制文字坐标
                float textX = i * itemWidth / 5.0f + (i * 2 + 1) * itemWidth / 2.0f + getPaddingLeft() - textRect.width() / 2.0f;
                float textY = getPaddingTop() + itemHeight / 2.0f + textRect.height() / 2.0f;
                canvas.drawText(text, textX, textY, mTextPaint);
            }
            //绘制圆点
            for (int i = 0; i < mNeedHidden.size(); i++) {
                //圆心坐标
                float cx = i * itemWidth / 5.0f + (i * 2 + 1) * itemWidth / 2.0f + getPaddingLeft();
                float cy = itemHeight / 2.0f + getPaddingTop() ;
                canvas.drawCircle(cx, cy, mRadius, circlePaint);
            }
        }
    
        //dp px 换算
        private float dp2px(float dp) {
            return TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp, getResources().getDisplayMetrics());
        }
        //获取密码框最大宽高
        private int getMaxItemWidth() {
            return ((getResources().getDisplayMetrics().widthPixels - getPaddingRight() - getPaddingLeft()) * 5) / (mPwdLength * 6 - 1);
        }
    
        @Override
        public boolean onTouchEvent(MotionEvent event) {
            if (event.getAction() == MotionEvent.ACTION_DOWN) {
                Log.d(TAG, "onTouchEvent: ");
                requestFocus();
                inputMethodManager.showSoftInput(this, InputMethodManager.SHOW_FORCED);
                return true;
            }
            return super.onTouchEvent(event);
        }
    
    
        @Override
        public InputConnection onCreateInputConnection(EditorInfo outAttrs) {
            outAttrs.inputType = InputType.TYPE_CLASS_NUMBER;
            outAttrs.imeOptions = EditorInfo.IME_ACTION_DONE;
            return new BaseInputConnection(this, false);
        }
    
        @Override
        public boolean onCheckIsTextEditor() {
            return true;
        }
    
        @Override
        public boolean onKey(View v, int keyCode, KeyEvent event) {
            if (KeyEvent.ACTION_DOWN != event.getAction()) {
                return false;
            }
            if (event.isShiftPressed()) {
                return false;
            }
            //只能输入数字
            if (keyCode >= KeyEvent.KEYCODE_0 && keyCode <= KeyEvent.KEYCODE_9) {
                if (mPasswords.size() < mPwdLength) {
                    Log.d(TAG, "onKey: add");
                    mPasswords.add(String.valueOf(keyCode - 7));
                    confirmFinshed();
                    Message message = Message.obtain();
                    message.what = 100;
                    message.obj = String.valueOf(keyCode - 7);
                    mHandler.sendMessageDelayed(message, 250);
                    invalidate();
    
                }
                return true;
            }
            //删除键
            if (keyCode == KeyEvent.KEYCODE_DEL) {
                if (mPasswords.size() > 0) {
                    Log.d(TAG, "onKey: delete");
                    mPasswords.remove(mPasswords.size() - 1);
                    if (!mNeedHidden.isEmpty()) {
                        mNeedHidden.remove(mNeedHidden.size() - 1);
                    }
                    invalidate();
                }
                return true;
            }
            //确认
            if (keyCode == KeyEvent.KEYCODE_ENTER) {
                confirmFinshed();
                return true;
            }
            return false;
        }
    
        //获取密码
        private String getPwd() {
            StringBuilder sb = new StringBuilder();
            for (String p : mPasswords) {
                sb.append(p);
            }
            return sb.toString();
        }
        //密码输入完成 隐藏键盘
        private void confirmFinshed() {
            if (mPasswords.size() == mPwdLength) {
                inputMethodManager.hideSoftInputFromWindow(getWindowToken(), 0);
                if (onInputFinished != null) {
                    onInputFinished.onFinished(getPwd());
                }
            }
        }
    
        @Override
        protected void onDetachedFromWindow() {
            super.onDetachedFromWindow();
            inputMethodManager.hideSoftInputFromWindow(getWindowToken(), 0);
        }
        //对外输出密码
        interface OnInputFinished {
            void onFinished(String pwd);
        }
    }
    

    2.使用的自定义属性如下:

    <declare-styleable name="PwdInput">
            <attr name="pwd_length" format="integer"/>
            <attr name="pwd_textsize" format="dimension"/>
            <attr name="pwd_item_border_color" format="color"/>
            <attr name="pwd_circle_color" format="color"/>
            <attr name="pwd_circle_radius" format="dimension"/>
            <attr name="pwd_round_rect_radius" format="dimension"/>
            <attr name="pwd_round_rect_border_width" format="dimension"/>
        </declare-styleable>
    

    3.截图对应的布局文件如下:

    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        xmlns:tools="http://schemas.android.com/tools"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical"
        tools:context="com.example.xxx.pwdedittextdemo.MainActivity">
    
        <com.example.xxx.pwdedittextdemo.PwdInput
            android:id="@+id/pwdedittext"
            android:layout_marginTop="10dp"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content" />
        <com.example.xxx.pwdedittextdemo.PwdInput
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginTop="20dp"
            app:pwd_circle_color="#ff8100"
            app:pwd_circle_radius="10dp"
            app:pwd_item_border_color="#666"
            app:pwd_length="8"
            app:pwd_round_rect_radius="8dp"
            app:pwd_textsize="17sp" />
        <com.example.xxx.pwdedittextdemo.PwdInput
            android:layout_marginTop="10dp"
            android:layout_width="250dp"
            android:layout_height="80dp"
            android:paddingLeft="20dp"
            android:paddingRight="20dp"
            android:paddingTop="5dp"
            android:background="#dedede"
            />
        <com.example.xxx.pwdedittextdemo.PwdInput
            android:layout_marginTop="10dp"
            android:layout_width="wrap_content"
            android:layout_height="80dp"
            android:paddingLeft="20dp"
            android:paddingRight="10dp" />
        <com.example.xxx.pwdedittextdemo.PwdInput
            android:layout_marginTop="10dp"
            android:layout_width="wrap_content"
            android:layout_height="80dp"
           android:paddingTop="20dp" />
    </LinearLayout>
    
    

    4.获取密码

    PwdInput pwdEditText= (PwdInput) findViewById(R.id.pwdedittext);
            pwdEditText.setOnInputFinished(new PwdInput.OnInputFinished() {
                @Override
                public void onFinished(String pwd) {
                    Log.d("xxxxxx", "onFinished: "+pwd);
                    Toast.makeText(MainActivity.this, "密码是:"+pwd, Toast.LENGTH_SHORT).show();
                }
            });
    

    相关文章

      网友评论

          本文标题:Android自定义密码输入框

          本文链接:https://www.haomeiwen.com/subject/cuairxtx.html