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

安卓/Android 模仿支付宝/微信 支付密码输入框的自定义View

2017-07-12 10:26 881 查看
类似这个密码输入框相信在博客上已经不少了,但是我稍微看了一下,大都是通过XML布局,然后组合成的一个自定义View。但是我想只用一个java文件,拿来就用该怎么办呢?

那就需要自己画出来了。水平有限,这里提供我自己自定义的一个过程,哪里不好,大家提提意见。

先上个效果图预览吧,我这里只是实现了那个框,具体输入布局大家可以在xml随意定义。



使用起来还是比较方便的,主要开放有四个方法和一个输入完成的回调接口。

这几个方法下面会介绍。

实现过程

这个密码输入框整体是一个输入框,每个密码之间有一根竖线间隔,每输入一个密码,就绘制一个屏蔽图案(一般是圆形小点)。

那么我们需要:

1.绘制外部的输入框

2.根据密码个数绘制间隔的竖线

3.根据用户输入的密码个数绘制屏蔽图

大概流程就这样,具体每一部分代码就不贴出来了,我直接贴这个文件的代码吧

package cn.small_qi.transitiontest.diyview;

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.RectF;
import android.support.annotation.Nullable;
import android.util.AttributeSet;
import android.view.View;

/**
* Created by small_qi on 2017/7/12.
*/

public class PayInputView extends View {

private Paint mPaint;
private InputFinishListener inputFinishListener;//输入完成监听

private int inputNum=0;//当前输入的密码个数
private int passwordNum=6;//密码个数

private
4000
int boundWidth=2;//外层框线条粗细
private int boundColor= Color.BLACK;//外层框线条颜色
private int boundRadius=0;//外框圆角半径

private int deliverWidth=1;//分割线粗细
private int deliverColor=Color.GRAY;//分割线条颜色
private int deliverPadding=5;//分割线距离框的大小

private int circleRadius =15;//密码圆点半径大小
private int circleColor= Color.BLACK;//密码圆点颜色

private StringBuilder currentPassword;//用户输入的密码

public PayInputView(Context context) {
super(context);
init();
}

public PayInputView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
init();
}

private void init() {
currentPassword=new StringBuilder();
mPaint=new Paint(Paint.ANTI_ALIAS_FLAG);
}

public PayInputView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}

@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
//TODO 此处应处理控件的测量逻辑
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
int width =getWidth();
int height =getHeight();
float deliverSize=(width-getPaddingLeft()-getPaddingRight())/passwordNum;
//1.画外框
drawBound(canvas, width, height);
//2.画分割线
drawDeliver(canvas, height, deliverSize);
//3.输入密码之后显示的图案
drawCircle(canvas, height, deliverSize);
}

private void drawBound(Canvas canvas, int width, int height) {
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setStrokeWidth(boundWidth);
mPaint.setColor(boundColor);
RectF rectF = new RectF(getPaddingLeft(),getPaddingTop(),width-getPaddingRight(),height-getPaddingBottom());
canvas.drawRoundRect(rectF,boundRadius,boundRadius,mPaint);
}

private void drawDeliver(Canvas canvas, int height, float deliverSize) {
mPaint.setStrokeWidth(deliverWidth);
mPaint.setColor(deliverColor);
Path path = new Path();
for (int i = 1; i < passwordNum; i++) {
path.reset();
path.moveTo(deliverSize*i+getPaddingLeft(),0+deliverPadding+getPaddingTop());
path.lineTo(deliverSize*i+getPaddingLeft(),height-deliverPadding-getPaddingBottom());
canvas.drawPath(path,mPaint);
}
}

private void drawCircle(Canvas canvas, int height, float deliverSize) {
mPaint.setStyle(Paint.Style.FILL);
mPaint.setColor(circleColor);
for (int i = 0; i < inputNum; i++) {
canvas.drawCircle(deliverSize*i+getPaddingLeft()+deliverSize/2,(height-getPaddingTop()-getPaddingBottom())/2+getPaddingTop(), circleRadius,mPaint);
}
}

//输入一个密码
public void inputPassword(Object pwd){
if (inputNum<passwordNum) {
currentPassword.append(pwd);
inputNum++;
}
invalidate();
if (inputNum==passwordNum){
if (inputFinishListener!=null){
inputFinishListener.onFinish(getPassword());
}
}
}
//删除一个密码
public void deletePassword(){
if (currentPassword.length()>0) {
currentPassword.deleteCharAt(currentPassword.length() - 1);
inputNum--;
}
invalidate();
}
//清空输入的所有密码
public void cleanInput(){
inputNum=0;
currentPassword.delete(0,currentPassword.length());
invalidate();
}
//获取输入的所有密码
public String getPassword(){
return currentPassword.toString();
}

public void setInputFinishListener(InputFinishListener inputFinishListener) {
this.inputFinishListener = inputFinishListener;
}

public interface InputFinishListener{
void onFinish(String pwd);
}
}


之前说的开放四个方法分别就是:输入一个密码、删除一个密码、清空输入、获取当前输入的所有密码这四个。监听就是用户输入所有密码后回调。

总体来说,能够自定义的属性还是很多的,但是我没有开放出方法,也没有定义xml属性,如果需要,大家稍作修改即可。

大家看我的全局变量的定义就知道能够自动义那些内容,包括最大输入的密码个数;外框线条大小、颜色、圆角,分割线大小、颜色、距离外框的大小、屏蔽图案的大小颜色等。

如果有需要,大家还可以修改分割线是实线还是虚线、屏蔽图案任意图片、各种阴影效果,输入光标等都可以的。

使用起来也比较方便,只需要在xml中定义:

<your.package.PayInputView
android:id="@+id/inputview"
android:layout_width="256dp"
android:padding="1dp"
android:layout_height="48dp" />


这里有两点需要注意的是,

1.为什么我加了一个padding,因为我绘制并没有留出空隙,这个view是多大就画多大,如果大家觉得边框过于紧凑,加个padding就行了(建议加上,否则如果设置外框线条太粗可能显示没有设置的粗,或者圆角不明显等)。

2.我直接定义了宽和高,这显然不太好,其实这只是为了方便我测试而已,大家把onMeasure方法测量逻辑改成下面这样,使用wrap_content就行咯。

使用wrap_content:

/**
* 根据手机的分辨率从 dp 的单位 转成为 px(像素)
*/
private int dip2px(float dpValue) {
final float scale = getContext().getResources().getDisplayMetrics().density;
return (int) (dpValue * scale + 0.5f);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
// super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int widthMode=MeasureSpec.getMode(widthMeasureSpec);
int widthSize=MeasureSpec.getSize(widthMeasureSpec);
int heightMode=MeasureSpec.getMode(heightMeasureSpec);
int heightSize=MeasureSpec.getSize(heightMeasureSpec);
int width,height;
if (widthMode==MeasureSpec.AT_MOST){
width=dip2px(36)*passwordNum;
}else {
width=widthSize;
}
if (heightMode==MeasureSpec.AT_MOST){
height=dip2px(36);
}else{
height=heightSize;
}
setMeasuredDimension(width,height);
}


java代码中的使用:

private PayInputView payView;
private void initPayView() {
payView = (PayInputView) findViewById(R.id.inputview);
//输入完成监听
payView.setInputFinishListener(new PayInputView.InputFinishListener() {
@Override
public void onFinish(String pwd) {
Toast.makeText(MainActivity.this, "你输入的密码:"+pwd, Toast.LENGTH_SHORT).show();
}
});
}
public void onClick(View v){
switch (v.getId()){
case R.id.input://输入按钮
//这里只是模拟用户输入密码,具体使用时,用户按一个数字就调用这个方法把密码传过去即可。
payView.inputPassword(2);
break;
case R.id.delete://删除按钮
payView.deletePassword();
break;
case R.id.clear://清除按钮
payView.cleanInput();
break;
}
}


这大概只能算是半成品,但是如果要求不高,拿来直接用也是可以的。

如果有特殊要求,例如我不想自己写个密码输入布局,我想使用自带输入法,点击密码框就弹出输入法输入。这就要大家自己实现了监听输入法的输入了。

----------------修改继承自edittext 可以调用自带输入法进行密码输入,同时edittext的大部分属性都可以使用。同时加了输入指示,输入完成变色等---------

效果图:



JAVA源码:

package cn.small_qi.transitiontest.diyview;

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.Rect;
import android.graphics.RectF;
import android.os.Build;
import android.support.annotation.Nullable;
import android.support.annotation.RequiresApi;
import android.support.v7.widget.AppCompatEditText;
import android.util.AttributeSet;
import android.util.Log;
import android.view.ActionMode;
import android.view.Menu;
import android.view.MenuItem;
import android.view.MotionEvent;

/**
* Created by small_qi on 2017/7/12.
*/

public class PayInputView extends AppCompatEditText {

private Paint mPaint;
private InputFinishListener inputFinishListener;//输入完成监听

private int inputNum = 0;//当前输入的密码个数
private int passwordNum = 6;//密码个数

private int boundWidth = 2;//外层框线条粗细
private int boundColor = Color.BLACK;//外层框线条颜色
private int boundRadius = 0;//外框圆角半径

private int deliverWidth = 1;//分割线粗细
private int deliverColor = Color.GRAY;//分割线条颜色
private int deliverPadding = 5;//分割线距离框的大小

private int circleRadius = 15;//密码圆点半径大小
private int circleColor = Color.BLACK;//密码圆点颜色

private StringBuilder currentPassword;//用户输入的密码

public PayInputView(Context context) {
super(context);
init();
}

public PayInputView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
init();
}

private void init() {
currentPassword = new StringBuilder();
mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
setBackgroundDrawable(null);
setMaxLines(1);
//禁止复制等操作
disableCopy();

}

private void disableCopy() {
setLongClickable(false);
setTextIsSelectable(false);
setSelected(false);
setCustomSelectionActionModeCallback(new ActionMode.Callback() {
@Override
public boolean onCreateActionMode(ActionMode mode, Menu menu) {
return false;
}

@Override
public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
return false;
}

@Override
public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
return false;
}

@Override
public void onDestroyActionMode(ActionMode mode) {

}
});
}

public PayInputView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}

/**
* 根据手机的分辨率从 dp 的单位 转成为 px(像素)
*/
private int dip2px(float dpValue) {
final float scale = getContext().getResources().getDisplayMetrics().density;
return (int) (dpValue * scale + 0.5f);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
// super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int widthMode = MeasureSpec.getMode(widthMeasureSpec);
int widthSize = MeasureSpec.getSize(widthMeasureSpec);
int heightMode = MeasureSpec.getMode(heightMeasureSpec);
int heightSize = MeasureSpec.getSize(heightMeasureSpec);
int width, height;
if (widthMode == MeasureSpec.AT_MOST) {
width = dip2px(36) * passwordNum;
} else {
width = widthSize;
}
if (heightMode == MeasureSpec.AT_MOST) {
height = dip2px(36);
} else {
height = heightSize;
}
setMeasuredDimension(width, height);
}
@Override
protected void onDraw(Canvas canvas) {
if (inputNum==passwordNum){
//如果输入完毕 整个框变色 如无此需求可删除这段代码
if (hasFocus()&&hasFocusable()){
boundColor=Color.BLUE;
deliverColor =Color.BLUE;
}else{
boundColor=Color.BLACK;
deliverColor =Color.BLACK;
}
}else{
boundColor=Color.BLACK;
deliverColor =Color.BLACK;
}

//
int width = getWidth();
int height = getHeight();
float deliverSize = (width - getPaddingLeft() - getPaddingRight()) / passwordNum;
//1.画外框
drawBound(canvas, width, height);
//2.画分割线
drawDeliver(canvas, height, deliverSize);
//2.1.输入指示
drawIndicator(canvas, height, deliverSize);
//3.输入密码之后显示的图案
drawCircle(canvas, height, deliverSize);
}
private void drawIndicator(Canvas canvas, int height, float deliverSize) {
if (inputNum < 0 || inputNum == passwordNum ||!(hasFocus()&&hasFocusable())) return;
mPaint.setColor(Color.BLUE);
//mPaint.setShadowLayer(5,3,3,0xFFFFAAFF);//阴影效果
mPaint.setStrokeWidth(boundWidth + 2);
RectF rectF = new RectF(deliverSize * inputNum + getPaddingLeft(), getPaddingTop(), deliverSize * (inputNum + 1) + getPaddingLeft(), height - getPaddingBottom());
canvas.drawRoundRect(rectF, boundRadius, boundRadius, mPaint);
}
private void drawBound(Canvas canvas, int width, int height) {
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setStrokeWidth(boundWidth);
mPaint.setColor(boundColor);
RectF rectF = new RectF(getPaddingLeft(), getPaddingTop(), width - getPaddingRight(), height - getPaddingBottom());
canvas.drawRoundRect(rectF, boundRadius, boundRadius, mPaint);
}
private void drawDeliver(Canvas canvas, int height, float deliverSize) {
mPaint.setStrokeWidth(deliverWidth);
mPaint.setColor(deliverColor);
Path path = new Path();
for (int i = 1; i < passwordNum; i++) {
path.reset();
path.moveTo(deliverSize * i + getPaddingLeft(), 0 + deliverPadding + getPaddingTop());
path.lineTo(deliverSize * i + getPaddingLeft(), height - deliverPadding - getPaddingBottom());
canvas.drawPath(path, mPaint);
}
}
private void drawCircle(Canvas canvas, int height, float deliverSize) {
mPaint.setStyle(Paint.Style.FILL);
mPaint.setColor(circleColor);
for (int i = 0; i < inputNum; i++) {
acf5

canvas.drawCircle(deliverSize * i + getPaddingLeft() + deliverSize / 2, (height - getPaddingTop() - getPaddingBottom()) / 2 + getPaddingTop(), circleRadius, mPaint);
}
}
//监听文字输入长度
@Override
protected void onTextChanged(CharSequence text, int start, int lengthBefore, int lengthAfter) {
Log.i("TAG", "onTextChanged: " + text + " " + start + " " + lengthBefore + " " + lengthAfter);
//改变之后长度大于之前 证明是输入操作
if (Math.abs(lengthAfter - lengthBefore) == 1) {
if (lengthAfter < lengthBefore) {
deletePassword();
} else if (inputNum<passwordNum){
if (start == 0) {
notifyPasswordChange(text.charAt(lengthAfter - 1));
} else {
notifyPasswordChange(text.charAt(start));
}
}
} else{
for (int i = 0; i < lengthAfter-lengthBefore; i++) {
if (inputNum<passwordNum) {
notifyPasswordChange(text.charAt(start + i));
}
}
}
//缓存字符大于20时清空文字缓存
if (text.length()>20){
setText(getPassword());
}
}

@Override
public boolean onTouchEvent(MotionEvent event) {
return super.onTouchEvent(event);
}

//输入一个密码
public void inputPassword(Object pwd) {
append(String.valueOf(pwd));
}

private void notifyPasswordChange(Object pwd) {
if (inputNum < passwordNum) {
currentPassword.append(pwd);
inputNum++;
invalidate();
}
if (inputNum == passwordNum) {
if (inputFinishListener != null) {
inputFinishListener.onFinish(getPassword());
}
}
}

//删除一个密码
public void deletePassword() {
if (currentPassword.length() > 0) {
currentPassword.deleteCharAt(currentPassword.length() - 1);
inputNum--;
invalidate();
}
}
//清空输入的所有密码
public void cleanInput() {
inputNum = 0;
currentPassword.delete(0, currentPassword.length());
invalidate();
}
//获取输入的所有密码
public String getPassword() {
return currentPassword.toString();
}
public void setInputFinishListener(InputFinishListener inputFinishListener) {
this.inputFinishListener = inputFinishListener;
}
public interface InputFinishListener {
void onFinish(String pwd);
}
}


--有意见别忘了在评论中提出哦--
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: