1. 程式人生 > >自定義View——仿支付寶支付彈窗介面

自定義View——仿支付寶支付彈窗介面

這裡寫圖片描述
上面這個是採用自定view方式實現的一個仿支付寶支付彈窗的效果;

1、自定義view並初始化自定義屬性

public class PasswordEditText extends EditText{
    //一個密碼所佔的寬度
    private int mPasswordItemWidth;
    //密碼的個數預設6個
    private int mPasswordNumder=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=mDivisionLineColor; //密碼圓點的半徑大小 private int mPasswordRadius=4; public PasswordEditText(Context context) { this
(context,null); } public PasswordEditText(Context context, AttributeSet attrs) { super(context, attrs); initAttributeSet(context, attrs); } /** * 初始化屬性 * @param context * @param attrs */ 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,dip2px(mBgCorner)); //獲取顏色 mBgColor=array.getColor(R.styleable.PasswordEditText_bgColor,mBgColor); mDivisionLineColor=array.getColor(R.styleable.PasswordEditText_divisionLineColor,mDivisionLineColor); mPasswordColor=array.getColor(R.styleable.PasswordEditText_passwordColor,mDivisionLineColor); array.recycle(); } }

繼承自EditText的話可以用使用EditText中的一些屬性和方法,在初始化完自定義屬性後要記得呼叫recycle()方法進行回收;

2、初始化畫筆

    /**
     * 初始化畫筆
     */
    private void initPaint() {
        mPaint=new Paint();
        mPaint.setAntiAlias(true);
        mPaint.setDither(true);
    }

在第三個建構函式中呼叫就可以了,

3、在onDraw()方法中進行繪製
在繪製的時候先要計算出一個密碼所佔的寬度

//一個密碼的寬度
private int mPasswordItemWidth=(getWidth()-2*mBgSize-(mPasswordNumder-1)*mDivisionLineSize)/mPasswordNumder;
一個密碼的寬度=(整個輸入框的寬度-(最左邊邊框的寬度+最右邊邊框的寬度)-(密碼個數-1)*密碼之間分割線的寬度)/密碼的個數

獲取到一個密碼的寬度後就可以進行繪製;
3.1、繪製密碼輸入框的背景

private void drawBg(Canvas canvas) {
        RectF rect=new RectF(mBgSize,mBgSize,getWidth()-mBgSize,getHeight()-mBgSize);
        //繪製背景  如果有圓角就繪製圓角矩形,沒有就繪製矩形
        //設定畫筆的大小
        mPaint.setStrokeWidth(mBgSize);
        mPaint.setColor(mBgColor);
        //繪製空心
        mPaint.setStyle(Paint.Style.STROKE);
        if(mBgCorner==0){
            canvas.drawRect(rect,mPaint);
        }else{
            canvas.drawRoundRect(rect,mBgCorner,mBgCorner,mPaint);
        }
    }

沒有圓角的話,就呼叫canvas.drawRect(rect,mPaint);繪製矩形,第一個引數是RectF物件,第二個引數是Paint(畫筆);

 /* @param left   The X coordinate of the left side of the rectangle
     * @param top    The Y coordinate of the top of the rectangle
     * @param right  The X coordinate of the right side of the rectangle
     * @param bottom The Y coordinate of the bottom of the rectangle
     */
    public RectF(float left, float top, float right, float bottom) {
        this.left = left;
        this.top = top;
        this.right = right;
        this.bottom = bottom;
    }

RectF原始碼對四個引數都有說明;

left=輸入框邊框的大小;
top=輸入框邊框的大小;
right=getWidth()-輸入框邊框的大小;
bottom=getHeight()-輸入框邊框的大小;

如果有圓角的話,就呼叫canvas.drawRoundRect(rect,mBgCorner,mBgCorner,mPaint);繪製圓角矩形,

  /**
     * @param rect  The rectangular bounds of the roundRect to be drawn
     * @param rx    The x-radius of the oval used to round the corners
     * @param ry    The y-radius of the oval used to round the corners
     * @param paint The paint used to draw the roundRect
     */
    public void drawRoundRect(@NonNull RectF rect, float rx, float ry, @NonNull Paint paint) {
        drawRoundRect(rect.left, rect.top, rect.right, rect.bottom, rx, ry, paint);
    }

drawRoundRect原始碼也對要傳入的引數做了說明;
3.2、繪製密碼輸入框之間的分割線

private void drawDivisionLine(Canvas canvas) {
        mPaint.setStrokeWidth(mDivisionLineSize);
        mPaint.setColor(mDivisionLineColor);
        for (int i=0;i<mPasswordNumder-1;i++){
            int startX=mBgSize+(i+1)*mPasswordItemWidth+i*mDivisionLineSize;
            int startY=mBgSize;
            int endX=startX;
            int endY=getHeight()-mBgSize;
            canvas.drawLine(startX,startY,endX,endY,mPaint);

        }
    }

在繪製分割線的時候需要注意,假設密碼的個數是6,只需要繪製5個分割線,呼叫canvas.drawLine(startX,startY,endX,endY,mPaint);方法進行繪製,

   /**
     * @param startX The x-coordinate of the start point of the line
     * @param startY The y-coordinate of the start point of the line
     * @param paint  The paint used to draw the line
     */
    public void drawLine(float startX, float startY, float stopX, float stopY,
            @NonNull Paint paint) {
        native_drawLine(mNativeCanvasWrapper, startX, startY, stopX, stopY, paint.getNativeInstance());
    }

上面是drawLine方法的原始碼,第一個引數是X的起始位置,第二個引數是Y的起始位置,第三個引數是X的終點位置,第四個引數是Y的終點位置;

startX=輸入框邊框的寬度+(每個密碼的寬度)*當前繪製的位置(當前繪製第幾個)+單個分割線的寬度*當前繪製的位置(當前繪製第幾個);
startY=輸入框邊框的寬度;
stopX=startX;
stopY=getHeight()-輸入框邊框的寬度;

3.3、繪製密碼

private void drawPassword(Canvas canvas) {
        //設定實心樣式
        mPaint.setStyle(Paint.Style.FILL);
        mPaint.setColor(mPasswordColor);
        String text=getText().toString().trim();
        int passwordLength=text.length();
        for(int i=0;i<passwordLength;i++){
            int cx=mBgSize+i*mPasswordItemWidth+i*mDivisionLineSize+mPasswordItemWidth/2;
            int cy=getHeight()/2;
            canvas.drawCircle(cx,cy,mPasswordRadius,mPaint);
        }
    }

在繪製密碼黑圓點之前先要獲取到輸入的字元的長度,根據長度遍歷該字串呼叫canvas.drawCircle(cx,cy,mPasswordRadius,mPaint);進行繪製,
drawCircle()原始碼:

  /**
     * @param cx     The x-coordinate of the center of the cirle to be drawn
     * @param cy     The y-coordinate of the center of the cirle to be drawn
     * @param radius The radius of the cirle to be drawn
     * @param paint  The paint used to draw the circle
     */
    public void drawCircle(float cx, float cy, float radius, @NonNull Paint paint) {
        native_drawCircle(mNativeCanvasWrapper, cx, cy, radius, paint.getNativeInstance());
    }

第一個引數是X的位置,第二個引數是Y的位置,第三個引數是圓的半徑,第四個引數是畫筆;

cx=輸入框邊框的寬度+單個分割線的寬度*當前繪製哪一個+密碼框的寬度*當前繪製哪一個+密碼框的寬度/2;
cy=getHeight()/2;

到這裡輸入框背景、分割線、密碼都已經繪製完畢了,在這裡面新增一個密碼新增和刪除的方法;

    /**
     * 設定密碼
     * @param numder
     */
    public void addPassword(String numder) {
        //把之前的密碼取出來
        String password=getText().toString().trim();
        //判斷密碼的長度,不能超過當前設定的密碼的個數
        if(password.length()>=mPasswordNumder){
            return;
        }
        //密碼疊加
        password+=numder;
        setText(password);
    }
    /**
     * 刪除最後一位密碼
     */
    public void deleteLastPassword() {
        //把之前的密碼取出來
        String password=getText().toString().trim();
        //判斷當前密碼是否為空
        if(TextUtils.isEmpty(password)){
            return;
        }
        password=password.substring(0,password.length()-1);
        setText(password);
    }

剩下還有鍵盤沒有弄好,鍵盤的實現是在xml佈局裡面寫好,新增到LinearLayout中;

4、輸入鍵盤的實現

public class CustomerKeyboard extends LinearLayout implements View.OnClickListener {
    public CustomerKeyboard(Context context) {
        this(context,null);
    }

    public CustomerKeyboard(Context context, AttributeSet attrs) {
        this(context, attrs,0);
    }

    public CustomerKeyboard(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        //直接載入佈局
        inflate(context,R.layout.ui_customer_keyboard,this);
    }
}

新增進來後,設定相應的點選事件;

    /**
     * 設定點選事件
     * @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 childView = viewGroup.getChildAt(i);
                setItemClickListener(childView);
            }
        }else{
            view.setOnClickListener(this);
        }
    }
   @Override
    public void onClick(View v) {
        if(v instanceof TextView){
            //點選的是文字
            String number=((TextView) v).getText().toString().trim();
            if(mListener!=null&&number.length()!=0){
                mListener.click(number);
            }
        }
        if(v instanceof ImageView){
            //點選的是刪除
            if(mListener!=null){
                mListener.delete();
            }
        }
    }

差不多效果就實現了,在點選的地方呼叫就可以了;

//彈出dialog輸入框
AlertDialog dialog=new AlertDialog.Builder(this).
     setContentView(R.layout.activity_main).
     fullWith().
     formBottom(true).
     setCancelable(true).
     show();
passwordEditText=dialog.getView(R.id.password);
        mCustomerKeyboard=dialog.getView(R.id.customer_keyboard);

        mCustomerKeyboard.setOnCustomerKeyboardClickListener(this);
        passwordEditText.setEnabled(false);
        passwordEditText.setOnPasswordFullListener(this);