您的位置:首页 > 移动开发 > Android开发

Android支付宝输入密码界面

2017-04-24 23:29 302 查看
项目地址https://github.com/GitHubToLiao/CustomPassWordEditText.git

先说明一下,这里的功能是我按照HongChengDarren的博客写的,他的博客地址是http://blog.csdn.net/z240336124/article/details/53739790,但是这里的代码是我看完他讲解的视频自己写的。同时我会在这里说下我自己在实现该功能时踩的坑,同时也对这个功能做一下总结

首先先上一下效果图



这里我分析一下他的实现思路

- 这里我们可以吧他整体分为三个部分

第一部分是上面的密码显示框我们可以自定义EditText然后把背景和输入的密码绘制出来

第二部分就是我们自定义的键盘当然这里也可以使用系统自带的键盘,如果使用我们自定义的键盘就要将第一部分和第二部分通过接口回调关联起来

第三部分就是将第一部分和第二部分加载到Dialog中然后给Dialog设置全屏和底部显示同时也可以加入显示和退出的动画

1.第一部分的代码

public class PassWordEditText extends EditText {

private PWCommitListener mListener;
// 画笔
private Paint mPaint;
// 一个密码所占的宽度
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 = mDivisionLineColor;
// 密码圆点的半径大小
private int mPasswordRadius = 4;

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

public PassWordEditText(Context context, AttributeSet attrs) {
super(context, attrs);
//初始化画笔
initPaint();
//初始化自定义属性数据
initAttributeSet(context, attrs);
setMaxEms(mPasswordNumber);
setInputType(EditorInfo.TYPE_TEXT_VARIATION_PASSWORD);
// 不显示光标
setCursorVisible(false);
setEnabled(false);
}

/**
* 初始化画笔
*/
private void initPaint() {
mPaint = new Paint();
//设置坑锯齿
mPaint.setAntiAlias(true);
}

/**
* 初始化自
13cc9
定义属性
*/
private void initAttributeSet(Context context, AttributeSet attrs) {
TypedArray array = context.obtainStyledAttributes(attrs, R.styleable.PassWordEditText);
// 获取大小
mDivisionLineSize = (int) array.getDimension(R.styleable.PassWordEditText_divisionLinesSize, 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, mDivisionLineColor);
mPasswordNumber = array.getInt(R.styleable.PassWordEditText_passwordNumber, mPasswordNumber);
//回收
array.recycle();
}

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

@Override
protected void onDraw(Canvas canvas) {
int passwordWidth = getWidth() - (mPasswordNumber - 1) * mDivisionLineSize;
mPasswordItemWidth = passwordWidth / mPasswordNumber;
//绘制背景
drawBG(canvas);
//绘制分割线
drawDivisionLion(canvas);
//绘制密码
drawPassWord(canvas);
}

//添加密码将自定义键盘和密码框联系起来
public void addPW(String pw){
if(!TextUtils.isEmpty(pw)){
pw = getText().toString().trim()+pw;
if(pw.length() >=mPasswordNumber){
//密码输入完成回调
if(pw.length() ==mPasswordNumber){
setText(pw);
}
mListener.commit_PW(pw);
return;
}
setText(pw);
}
}

//删除最后一个密码
public void removePW(){
String pw = getText().toString().trim();
if(TextUtils.isEmpty(pw)){
return;
}
pw = pw.substring(0,pw.length()-1);
setText(pw);
}

//绘制密码
private void drawPassWord(Canvas canvas) {
//获取当前文本长度的原因是因为让用户输一个我们动态的展示一个密码
int passwordLength = getText().length();
//设置画笔颜色
mPaint.setColor(mPasswordColor);
//设密码为实心
mPaint.setStyle(Paint.Style.FILL);

for (int i = 0; i < passwordLength; i++) {
int cx = i * mDivisionLineSize + i * mPasswordItemWidth + mPasswordItemWidth / 2 + mBgSize;
canvas.drawCircle(cx, getHeight() / 2, mPasswordRadius, mPaint);
}
}

//绘制分割线
private void drawDivisionLion(Canvas canvas) {

//设置画笔颜色
mPaint.setColor(mDivisionLineColor);
//设置画笔大小
mPaint.setStrokeWidth(mDivisionLineSize);

for (int i = 0; i < mPasswordNumber - 1; i++) {
int startX = mPasswordItemWidth * (i + 1) + mDivisionLineSize * (i + 1);
canvas.drawLine(startX, mBgSize, startX, getHeight() - mBgSize, mPaint);
}
}

//绘制背景
private void drawBG(Canvas canvas) {
//设置背景颜色
mPaint.setColor(mBgColor);
//设置画笔为空心
mPaint.setStyle(Paint.Style.STROKE);
//设置空心线宽
mPaint.setStrokeWidth(mBgSize);
RectF rectF = new RectF(mBgSize, mBgSize, getWidth() - mBgSize, getHeight());
//进行绘制绘制过程中判断是否是圆角
if (mBgCorner != 0) {
//绘制圆角    rectf   圆角弧度    圆角弧度   画笔
canvas.drawRoundRect(rectF, mBgCorner, mBgCorner, mPaint);
} else {
canvas.drawRect(rectF, mPaint);
}
}
public void setOnPWCommitListener(PWCommitListener listener){
this.mListener =listener;
}

//密码完成回调
public interface PWCommitListener{
void commit_PW(String pw);
}
}


上面代码中已经写了注释但是要注意的是我这里在继承EditText的时候只重写了他的两个构造方法不知道你们发现了吗,为什么要重写两个构造方法原因是如果你们重写三个构造方法那么系统键盘我们就不能使用但是如果写两个构造方法就可有调用系统自带的键盘

在贴出自定义属性的代码文件路径res/values/attres.xml

<?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="divisionLinesSize" format="dimension"/>
<!--分割线颜色-->
<attr name="divisionLineColor" format="color"/>
<!--背景边框大小-->
<attr name="bgSize" format="dimension"/>
<!--背景边框颜色-->
<attr name="bgColor" format="color"/>
<!--背景边框弧度-->
<attr name="bgCorner" format="dimension"/>
</declare-styleable>
</resources>


2.接着是我们自定键盘的方法这里我的做法是继承自LinearLayout然后通过inflate()方法将我们已经写好的键盘布局引入这个非常简单

这里面主要功能不是布局而是怎么样把第一部分与第二部分关联起来,我这里的做法是将点击的按钮的数字通过接口回调回传给我们自定义的EditText也就是第一部分

好了下面就是自定义键盘的代码

public class CustomerKeyboard extends LinearLayout implements View.OnClickListener {

private CustomerKeyboardClickListener mListener;
public CustomerKeyboard(Context context) {
this(context, null);
}

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

public CustomerKeyboard(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
inflate(context,R.layout.kayboard,this);

setOnChildClick(this);
}

public void setOnChildClick(ViewGroup parant) {
//获取所有的子条目的个数
int childCount = parant.getChildCount();

//对所有子条目进行遍历
for(int i =0;i<childCount;i++){
View view =parant.getChildAt(i);
if(view instanceof ViewGroup){
//通过递归继续向下判断知道子条目不是VIewGroup
setOnChildClick((ViewGroup) parant.getChildAt(i));
//退出本次循环
continue;
}
//如果不是ViewGroup就给当前组件设置点击事件
view.setOnClickListener(this);
}
}

@Override
public void onClick(View v) {
//判断当前点击的按钮的类型我们的键盘只有TextView和ImageVIew
//如果当前View的类型是TextVIew那么他就是输入的密码
if(v instanceof TextView){
String pw =((TextView)v).getText().toString().trim();
//这里判空一个是因为代码的健壮性另外一个是因为我们TextView是空的如果不判断是会出问题的
if(!TextUtils.isEmpty(pw)){
if(mListener !=null) {
mListener.click(pw);
}
}
//如果当前View的类型是ImageVIew那么点击的就是删除键
}else if(v instanceof ImageView){
if(mListener !=null) {
mListener.remove();
}
}

}

/**
*
* @param listener CustomerKeyboardClickListener对象
* 点击回调监听
*/
public void setOnCustomerKeyboardClickListener(CustomerKeyboardClickListener listener){
this.mListener =listener;
}
/**
* 回调接口用来判断键盘的点击
* 如果点击数字调用click方法并将改密码返回回调出去
* 如果点击删除键则回调remove方法
*/
public interface  CustomerKeyboardClickListener{
void click(String pw);
void remove();
}
}


然后是键盘的布局代码

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#d1d5db"
android:orientation="vertical">

<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:layout_marginTop="1dp"
>

<TextView
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:background="@drawable/number_bg"
android:layout_marginRight="1dp"
android:gravity="center"
android:padding="15dp"
android:textColor="#000"
android:textStyle="bold"
android:text="1" />

<TextView
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:background="@drawable/number_bg"
android:gravity="center"
android:textColor="#000"
android:textStyle="bold"
android:padding="15dp"
android:layout_marginRight="1dp"
android:text="2" />

<TextView
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:gravity="center"
android:textStyle="bold"
android:padding="15dp"
android:textColor="#000"
android:background="@drawable/number_bg"
android:text="3" />

</LinearLayout>

<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:layout_marginTop="1dp"
android:orientation="horizontal">

<TextView
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:gravity="center"
android:textStyle="bold"
android:padding="15dp"
android:background="@drawable/number_bg"
android:textColor="#000"
android:layout_marginRight="1dp"
android:text="4" />

<TextView
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:gravity="center"
android:padding="15dp"
android:background="@drawable/number_bg"
android:textStyle="bold"
android:textColor="#000"
android:layout_marginRight="1dp"
android:text="5" />

<TextView
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:padding="15dp"
android:background="@drawable/number_bg"
android:textColor="#000"
android:textStyle="bold"
android:gravity="center"
android:text="6" />

</LinearLayout>

<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:layout_marginTop="1dp"
android:orientation="horizontal">

<TextView
android:layout_width="0dp"
android:layout_height="wrap_content"
android:padding="15dp"
android:background="@drawable/number_bg"
android:layout_marginRight="1dp"
android:layout_weight="1"
android:gravity="center"
android:textColor="#000"
android:textStyle="bold"
android:text="7" />

<TextView
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:padding="15dp"
android:background="@drawable/number_bg"
android:layout_marginRight="1dp"
android:gravity="center"
android:textColor="#000"
android:textStyle="bold"
android:text="8" />

<TextView
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:padding="15dp"
android:background="@drawable/number_bg"
android:gravity="center"
android:textColor="#000"
android:textStyle="bold"
android:text="9"
android:id="@+id/textView" />

</LinearLayout>

<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:layout_marginTop="1dp"
android:layout_marginBottom="1dp"
android:orientation="horizontal">

<TextView
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:gravity="center"
android:padding="15dp"
android:layout_marginRight="1dp"
android:text="" />

<TextView
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:gravity="center"
android:padding="15dp"
android:background="@drawable/number_bg"
android:layout_marginRight="1dp"
android:textColor="#000"
android:textStyle="bold"
android:text="0"
android:id="@+id/textView2" />

<ImageView
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:gravity="center"
android:src="@drawable/delete_end"/>

</LinearLayout>
</LinearLayout>


最后就是将上面我们已经关联好的EditText和键盘整合到Dialog中这里没什么好说的直接上代码

LayoutInflater inflater = LayoutInflater.from(this);
LinearLayout layout = (LinearLayout) inflater.inflate(R.layout.pw_layout, null);

Dialog dialog = new Dialog(this);
dialog.setContentView(layout);
//设置点击空白地区Dialog不退出
dialog.setCanceledOnTouchOutside(false);
Window window = dialog.getWindow();
//设置Dialog出现于退出的动画
window.setWindowAnimations(R.style.dial_style);
//设置底部显示
window.setGravity(Gravity.BOTTOM);
//设置水平方向全屏显示
int width =WindowManager.LayoutParams.MATCH_PARENT;
int height =WindowManager.LayoutParams.WRAP_CONTENT;
WindowManager.LayoutParams lp = window.getAttributes(); // 获取对话框当前的参数值
lp.width = ViewGroup.LayoutParams.MATCH_PARENT;
lp.height = ViewGroup.LayoutParams.WRAP_CONTENT;
window.setAttributes(lp);
dialog.show();


布局代码

R.layout.pw_layout

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:background="#fff"
android:orientation="vertical">

<com.example.as.passwordedittext.PassWordEditText
android:id="@+id/pw_EditText"
android:layout_width="match_parent"
android:layout_height="46dp"
android:layout_margin="20dp"
android:background="@null"
app:bgColor="#a5a5a5"
app:bgCorner="3dp"
app:divisionLineColor="#a5a5a5"
app:bgSize="1dp"
app:passwordColor="#000"
app:passwordRadius="5dp"
app:divisionLinesSize="1dp"
app:passwordNumber="6"
/>
<com.example.as.passwordedittext.CustomerKeyboard
android:id="@+id/keyboard_View"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="60dp"
/>
</LinearLayout>


Dialog的显示动画

R.style.dial_style

<style name="dial_style" parent="android:Animation">
<item name="android:windowEnterAnimation">@anim/dian_open</item>
<item name="android:windowExitAnimation">@anim/dial_exit</item>
</style>


dian_open.xml的代码

<?xml version="1.0" encoding="utf-8"?>
<!-- 上下滑入式 -->
<set xmlns:android="http://schemas.android.com/apk/res/android" >

<translate
android:duration="200"
android:fromYDelta="100%p"
android:toYDelta="0"
/>
</set>


dial_exit.xml的代码

<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android" >

<translate
android:duration="600"
android:toYDelta="100%p"
/>
</set>


这里在整合Dialog的过程中我自己有踩过一个坑分享一下这里创建Dialog的时候一定是Dialog而不能创建AlertDialog如果创建AlertDialog那么我们水平方向全屏显示就会不起作用·

项目地址https://github.com/GitHubToLiao/CustomPassWordEditText.git
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: