开源项目GridPasswordView使用和源码分析

来源:互联网 发布:视频打马赛克软件 编辑:程序博客网 时间:2024/05/01 21:59

GridPasswordView是一个密码输入视图,类似于微信或支付宝的支付密码视图。可以设置文字颜色和大小、分割线颜色、密码的长度。

项目地址:
https://github.com/Jungerr/GridPasswordView
其中包含项目源码和示例代码。

运行效果图:



一、项目使用
(1).在工程的build.gradle文件中添加项目引用。

dependencies {    compile 'com.jungly:gridPasswordView:0.3'}
(2).在布局中添加GridPasswordView。
<com.jungly.gridpasswordview.GridPasswordView    android:id="@+id/gridpassword"    android:layout_width="match_parent"    android:layout_height="50dp" />
(3).添加Java代码。
GridPasswordView gridPasswordView = findViewById(R.id.gridpassword);gridPasswordView.setOnPasswordChangedListener(new GridPasswordView.OnPasswordChangedListener() {    @Override    public void onTextChanged(String psw) {        // add your code here    }    @Override    public void onInputFinish(String psw) {        // add your code here    }});

二、源码分析
实现原理:
整个网格密码输入框最外层是一个水平方向的LinearLayout,内部包括显示密码的View和垂直分隔线View。而多个显示密码的View中,第一个位置放置的是EditText,其余均为TextView。如图所示。


当LinearLayout被点击时,让EditText获取焦点并弹出软键盘。定义一个String类型数组保存已输入的密码,当监听到EditText有新的字符输入时,截取下来保存到数组中,并保持EditText中始终只存在一个字符。每当监听到EditText的退格键时,删除String数组的最后一条数据。最后遍历String数组得到的就是用户输入的密码。

代码核心部分包括视图的添加、字符输入的监听和退格键的监听。

(1).初始化视图
在构造方法中,完成View的初始化。包括强制设置方向为水平,添加一个EditText和多个TextView以及之间的分割线。由于EditText和多个TextView的layout_width=0且layout_weight=1,所以会均分宽度。
public class GridPasswordView extends LinearLayout {    private String[] mPasswordArr;    private TextView[] mViewArr;    public GridPasswordView(Context context) {        super(context);mPasswordArr = new String[mPasswordLength];        mViewArr = new TextView[mPasswordLength];        initViews(context);    }    private void initViews(Context context) {        super.setBackgroundDrawable(mOuterLineDrawable);        setShowDividers(SHOW_DIVIDER_NONE);        // 设置方向为水平        setOrientation(HORIZONTAL);        mTransformationMethod = new CustomPasswordTransformationMethod(mPasswordTransformation);        inflaterViews(context);    }    // 添加视图操作    private void inflaterViews(Context context) {        // 布局gridpasswordview中只有一个EditText,添加到当前视图        LayoutInflater inflater = LayoutInflater.from(context);        inflater.inflate(R.layout.gridpasswordview, this);        mInputView = (ImeDelBugFixedEditText) findViewById(R.id.inputView);        mInputView.setMaxEms(mPasswordLength);// 添加监听        mInputView.addTextChangedListener(textWatcher);// 添加监听        mInputView.setDelKeyEventListener(onDelKeyEventListener);        setCustomAttr(mInputView);        // 索引0是EditText        mViewArr[0] = mInputView;        // 索引1~mPasswordLength是TextView        int index = 1;        while (index < mPasswordLength) {            // 调用addView()添加分割线            View dividerView = inflater.inflate(R.layout.divider, null);            LayoutParams dividerParams = new LayoutParams(mLineWidth, LayoutParams.MATCH_PARENT);            dividerView.setBackgroundDrawable(mLineDrawable);            addView(dividerView, dividerParams);            // 调用addView()添加文本            TextView textView = (TextView) inflater.inflate(R.layout.textview, null);            setCustomAttr(textView);            LayoutParams textViewParams = new LayoutParams(0, LayoutParams.MATCH_PARENT, 1f);            addView(textView, textViewParams);            mViewArr[index] = textView;            index++;        }        // 设置点击事件        setOnClickListener(mOnClickListener);    }}

(2).弹出软键盘
当LinearLayout被点击时,触发mOnClickListener。使EditText强制获取焦点,弹出键盘。
private OnClickListener mOnClickListener = new OnClickListener() {    @Override    public void onClick(View v) {        forceInputViewGetFocus();    }};// EditText强制获取焦点,弹出键盘private void forceInputViewGetFocus() {    mInputView.setFocusable(true);    mInputView.setFocusableInTouchMode(true);    mInputView.requestFocus();    InputMethodManager imm = (InputMethodManager) getContext().getSystemService(Context.INPUT_METHOD_SERVICE);    imm.showSoftInput(mInputView, InputMethodManager.SHOW_IMPLICIT);}

(3).监听字符输入
使用TextWatcher监听EditText的字符输入。关键点在于使用数组保存每次输入的密码,和维持EditText内始终只显示首个密码字符。

private TextWatcher textWatcher = 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) {        if (s == null) {            return;        }        String newStr = s.toString();        if (newStr.length() == 1) {// 初次输入字符,赋值给索引0            mPasswordArr[0] = newStr;            notifyTextChanged();        } else if (newStr.length() == 2) {// 非首次输入字符            // 截取第2个字符            String newNum = newStr.substring(1);            for (int i = 0; i < mPasswordArr.length; i++) {                if (mPasswordArr[i] == null) {                    // 赋值给数组mPasswordArr,mPasswordArr中存储的就是最终的密码                    mPasswordArr[i] = newNum;                    // 给TextView设值                    mViewArr[i].setText(newNum);                    // 通知密码发生改变                    notifyTextChanged();                    break;                }            }            // 上述逻辑执行完毕后,把索引0的字符设置给mInputView            // 这样,之后再输入内容时,newStr.length()始终等于2。mInputView的内容长度始终等于1            mInputView.removeTextChangedListener(this);            mInputView.setText(mPasswordArr[0]);            if (mInputView.getText().length() >= 1) {                mInputView.setSelection(1);            }            mInputView.addTextChangedListener(this);        }    }    @Override    public void afterTextChanged(Editable s) {    }};

(4).监听退格键
当监听到EditText内触发退格键时,删除mPasswordArr数组内最后一个字符。

private ImeDelBugFixedEditText.OnDelKeyEventListener onDelKeyEventListener = new ImeDelBugFixedEditText        .OnDelKeyEventListener() {    @Override    public void onDeleteClick() {        for (int i = mPasswordArr.length - 1; i >= 0; i--) {            // 找到已输入的最后一个字符,将其置为null            if (mPasswordArr[i] != null) {                mPasswordArr[i] = null;                mViewArr[i].setText(null);                notifyTextChanged();                break;            } else {                mViewArr[i].setText(null);            }        }    }};

关键在于EditText对退格键的监听。在有的软键盘中,OnKeyListener无法监听到键盘的退格键KeyEvent.KEYCODE_DEL事件,在stackoverflow中已经有人给出了解决方法:
http://stackoverflow.com/questions/4886858/android-edittext-deletebackspace-key-event
EditText被重写后的核心代码:

public class ImeDelBugFixedEditText extends EditText {    private OnDelKeyEventListener delKeyEventListener;    // ......    // 键盘弹出时被调用    @Override    public InputConnection onCreateInputConnection(EditorInfo outAttrs) {        return new ZanyInputConnection(super.onCreateInputConnection(outAttrs), true);    }    private class ZanyInputConnection extends InputConnectionWrapper {        public ZanyInputConnection(InputConnection target, boolean mutable) {            super(target, mutable);        }        // 将KeyEvent.KEYCODE_DEL事件回调出去        @Override        public boolean sendKeyEvent(KeyEvent event) {            if (event.getAction() == KeyEvent.ACTION_DOWN && event.getKeyCode() == KeyEvent.KEYCODE_DEL) {                if (delKeyEventListener != null) {                    delKeyEventListener.onDeleteClick();                    return true;                }            }            return super.sendKeyEvent(event);        }        // deleteSurroundingText():删除字符,区间:当前光标前beforeLength个长度,当前光标后afterLength个长度        // beforeLength == 1 && afterLength == 0表示向前删除一个字符,可视为KeyEvent.KEYCODE_DEL        @Override        public boolean deleteSurroundingText(int beforeLength, int afterLength) {            if (beforeLength == 1 && afterLength == 0) {                return sendKeyEvent(new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_DEL)) && sendKeyEvent(new KeyEvent                        (KeyEvent.ACTION_UP, KeyEvent.KEYCODE_DEL));            }            return super.deleteSurroundingText(beforeLength, afterLength);        }    }    public void setDelKeyEventListener(OnDelKeyEventListener delKeyEventListener) {        this.delKeyEventListener = delKeyEventListener;    }    public interface OnDelKeyEventListener {        void onDeleteClick();    }}

0 0
原创粉丝点击