自定義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);