Android-打造一個屬于自己的支付密碼輸入框

首先上張效果圖看看

device-2018-07-10-112505.gif

具體思路

1. 繪制外圍Rect
2. 繪制分割線
3. 繪制圓點密碼

大家看到邊框、分割線、圓點密碼的顏色、大小、都是通過自定義屬性而獲得。

ps.png

看這樣一張圖 我們首先需要繪制這樣一個View

自定義屬性

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <declare-styleable name="PasswordEditText">
        <!--密碼個數-->
        <attr name="passwordNumber" format="integer"/>
        <!--密碼原點的半徑-->
        <attr name="passwordRadius" format="dimension"/>
        <!--密碼原點的顏色-->
        <attr name="passwordColor" format="color"/>
        <!--分割線的顏色-->
        <attr name="divisionLineColor" format="color"/>
        <!--分割線的大小  指寬度-->
        <attr name="divisionLineSize" format="dimension"/>
        <!--背景邊框的顏色-->
        <attr name="bgColor" format="color"/>
        <!--背景邊框的大小-->
        <attr name="bgSize" format="dimension"/>
        <!--背景邊框的圓角大小-->
        <attr name="bgCorner" format="dimension"/>

    </declare-styleable>
</resources>

自定義密碼輸入框的View

public class PasswordEditText extends AppCompatEditText {
// 畫筆-->繪制背景框
private Paint mRectPaint;
// 畫筆--> 繪制密碼
private Paint mPasswordPaint;
// 一個密碼所占的寬度
private int mPasswordItemWidth;
// 密碼的個數默認為6位數
private int mPasswordNumber = 6;
// 背景邊框顏色
private int mBgColor = Color.parseColor("#d1d2d6");
// 背景邊框大小
private int mBgSize = 1;
// 背景邊框圓角大小
private int mBgCorner = 0;
// 分割線的顏色
private int mDivisionLineColor = mBgColor;
// 分割線的大小
private int mDivisionLineSize = 1;
// 密碼圓點的顏色
private int mPasswordColor = Color.parseColor("#000000");
// 密碼圓點的半徑大小
private int mPasswordRadius = 4;
//密碼輸入完畢需要一個接口回調出去
private PasswordFullListener mPasswordFullListener;

public PasswordEditText(Context context) {
    this(context, null);
}

public PasswordEditText(Context context, AttributeSet attrs) {
    super(context, attrs);
    initAttributeSet(context, attrs);
    //不顯示光標
    setCursorVisible(false);
    //不彈出系統軟鍵盤
    setInputType(InputType.TYPE_NULL);
    setBackground(null);
    initPaint();
}

/**
 * 初始化屬性
 */
private void initAttributeSet(Context context, AttributeSet attrs) {
    TypedArray array = context.obtainStyledAttributes(attrs, R.styleable.PasswordEditText);
    // 獲取大小
    mDivisionLineSize = ( int ) array.getDimension(R.styleable.PasswordEditText_divisionLineSize, dip2px(mDivisionLineSize));
    mPasswordRadius = ( int ) array.getDimension(R.styleable.PasswordEditText_passwordRadius, dip2px(mPasswordRadius));
    mBgSize = ( int ) array.getDimension(R.styleable.PasswordEditText_bgSize, dip2px(mBgSize));
    mBgCorner = ( int ) array.getDimension(R.styleable.PasswordEditText_bgCorner, 0);
    // 獲取顏色
    mBgColor = array.getColor(R.styleable.PasswordEditText_bgColor, mBgColor);
    mDivisionLineColor = array.getColor(R.styleable.PasswordEditText_divisionLineColor, mDivisionLineColor);
    mPasswordColor = array.getColor(R.styleable.PasswordEditText_passwordColor, mPasswordColor);
    array.recycle();
}

/**
 * 初始化畫筆
 */
private void initPaint() {
    //初始化繪制邊框的畫筆
    mRectPaint = new Paint();
    mRectPaint.setAntiAlias(true);
    mRectPaint.setDither(true);
    mRectPaint.setColor(mBgColor);
    //初始化密碼遠點的畫筆
    mPasswordPaint = new Paint();
    mPasswordPaint.setAntiAlias(true);
    mPasswordPaint.setDither(true);
    mPasswordPaint.setColor(mPasswordColor);

}


@Override
protected void onDraw(Canvas canvas) {
    //不需要調用super.onDraw(canvas); 為什么不需要呢?你去調用試試看,就明白為什么了
    //super.onDraw(canvas);
    //一個密碼的寬度
    mPasswordItemWidth = (getWidth() - mBgSize * 2 - (mPasswordNumber - 1) * mDivisionLineSize) / mPasswordNumber;
    drawRect(canvas);
    drawDivisionLine(canvas);
    drawPassword(canvas);
    if (mPasswordFullListener != null) {
        //獲取輸入的密碼
        String password = getText().toString().trim();
        if (password.length() == mPasswordNumber) {
            mPasswordFullListener.passwordFull(password);
        }
    }
}

/**
 * 繪制背景框
 *
 * @param canvas 畫布
 */
private void drawRect(Canvas canvas) {
    //矩形
    RectF rect = new RectF(mBgSize, mBgSize, getWidth() - mBgSize, getHeight() - mBgSize);
    mRectPaint.setStrokeWidth(mBgSize);
    //畫空心
    mRectPaint.setStyle(Paint.Style.STROKE);
    if (mBgCorner == 0) {
        canvas.drawRect(rect, mRectPaint);
    } else {
        canvas.drawRoundRect(rect, mBgCorner, mBgCorner, mRectPaint);
    }
}

/**
 * 繪制分割線
 *
 * @param canvas 畫布
 */
private void drawDivisionLine(Canvas canvas) {
    mRectPaint.setStrokeWidth(mDivisionLineSize);
    for (int i = 0; i < mPasswordNumber - 1; i++) {
        int startX = mBgSize + (i + 1) * mPasswordItemWidth + i * mDivisionLineSize;
        int startY = 0;
        int endX = startX;
        int endY = getHeight() - mBgSize;
        canvas.drawLine(startX, startY, endX, endY, mRectPaint);
    }
}

/**
 * 繪制圓點密碼
 *
 * @param canvas 畫布
 */
private void drawPassword(Canvas canvas) {
    //圓點密碼是實行的
    mPasswordPaint.setStyle(Paint.Style.FILL);
    int length = getText().toString().length();
    for (int i = 0; i < length; i++) {
        int cx = mBgSize + i * mDivisionLineSize + i * mPasswordItemWidth + mPasswordItemWidth / 2;
        int cy = getHeight() / 2;
        canvas.drawCircle(cx, cy, mPasswordRadius, mPasswordPaint);
    }
}

public void addPassword(String number) {
    if (TextUtils.isEmpty(number)) {
        return;
    }
    //把密碼取取出來
    String password = getText().toString().trim();
    if (password.length() <= mPasswordNumber) {
        //密碼疊加
        password += number;
        setText(password);
    }
}

/**
 * 刪除密碼
 */
public void deletePassword() {
    String password = getText().toString().trim();
    if (TextUtils.isEmpty(password)) {
        return;
    }
    password = password.substring(0, password.length() - 1);
    setText(password);
}

private int dip2px(int dip) {
    return ( int ) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
            dip, getResources().getDisplayMetrics());
}

/**
 * 設置一個密碼輸入完畢的監聽器
 *
 * @param passwordFullListener Listener
 */
public void setPasswordFullListener(PasswordFullListener passwordFullListener) {
    this.mPasswordFullListener = passwordFullListener;
}

public interface PasswordFullListener {
    void passwordFull(String password);
}
}

最主要的是去求出每個密碼的寬度、分割線的坐標位置(float startX, float startY, float stopX, float stopY)和原點密碼的(cx,cy);

key.png

在看這樣一個數字鍵盤,是寫一個這樣的布局,很簡單的。關鍵是怎么給每一個View設置一個點擊事件。每一個View 我們都去綁定一個Id,然后設置onClick事件嗎?這樣做,那是不可能的。具體這樣做,看代碼

 /**
 * 給每一個自定義數字鍵盤上的View 設置點擊事件
 *
 * @param view
 */
private void setItemClickListener(View view) {
    if (view instanceof ViewGroup) {
        ViewGroup viewGroup = ( ViewGroup ) view;
        int childCount = viewGroup.getChildCount();
        for (int i = 0; i < childCount; i++) {
            //不斷的給里面所有的View設置setOnClickListener
            View childView = (( ViewGroup ) view).getChildAt(i);
            setItemClickListener(childView);
        }
    } else {
        view.setOnClickListener(this);
    }
}

說下思路,鍵盤的整體布局是個LineaLayout,每一行布局是一個LineaLayout,然后每個LineaLayout會有三個子TextView。我們去遞歸下,這樣每次循環都去拿View,不是ViewGroup,那就是View。

源碼地址:https://github.com/StevenYan88/PayPasswordEditText

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容

  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 172,841評論 25 708
  • 1、通過CocoaPods安裝項目名稱項目信息 AFNetworking網絡請求組件 FMDB本地數據庫組件 SD...
    陽明AGI閱讀 16,003評論 3 119
  • 作者:creantan 小葡萄爸爸 1、hook sysctlbyname底層函數,此種方法比較通用、可以修改任意...
    crean閱讀 2,626評論 1 1
  • 煙雨迷茫 和你離開火車長長的車廂 拉著行李 來到你的家鄉 雨落村莊 路的兩旁 野花雨中含笑 隱約浮動縷縷暗香 樹上...
    雪戀心痕閱讀 322評論 1 3
  • 去某發超市買東西,送了兩張抵用券,以為能同時享用的,就買了好多東西,但結賬的時候,小哥拿著兩張券一掃,滿臉抱歉的說...
    666在簡書閱讀 192評論 0 0