自定义密码 输入界面
知识点:selector,自定义View,RecyclerView
最终界面如下,按下一个数字按钮,则上面就会显示中间一个小灰点:

思路:
- 写一个自定义View,然后背景色是灰色,用一个画笔画的.然后前景色是更灰一点儿的颜色,用画笔画的.然后前景色可以设置为没有,这样看起来就是只有背景色了.
- 然后下面的是RecyclerView,点一下一个按钮,上面的最后自定义View就显示出中间的小灰点,就像是输入了一个密码一样.
- 当密码位数等于4的时候,判断是否输入正确.
1. 首先需要一个自定义View,用来显示密码的输入框的.CirclePsdView.java
public class CirclePsdView extends View {
private static final String TAG = "CirclePsdView";
private Paint mBGPaint; //背景画笔
private Paint mFGPaint; //前景画笔
private int mRadius; //View的半径
private static final int SMALL_CIRCLE_RADIUS = 10; //内圆半径
public CirclePsdView(Context context) {
super(context);
init();
}
public CirclePsdView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
init();
}
public CirclePsdView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}
/**
* 初始化设置
*/
private void init() {
mBGPaint = new Paint();
mFGPaint = new Paint();
mBGPaint.setARGB(255, 239, 239, 239);//设置背景画笔颜色
mFGPaint.setColor(Color.TRANSPARENT); //设置前景画笔颜色 默认是没有密码,是透明的
}
//测量自己的宽高
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
//因为是圆形,所以应该让宽高保持一致
int width = measureWidth(widthMeasureSpec);
int height = measureHeight(heightMeasureSpec);
//该方法必须由onMeasure(int,int)调用来存储测量的宽度和测量高度。 如果没有这样做,将在测量时间触发异常。
setMeasuredDimension(width, height);
}
@Override
protected void onDraw(Canvas canvas) {
//1, 获取当前控件的宽高,并设置半径的大小
int w = this.getMeasuredWidth();
int h = this.getMeasuredHeight();
mRadius = w < h ? w / 2 : h / 2;
//2, 首先画背景 #EFEFEF
canvas.drawCircle(mRadius, mRadius, mRadius, mBGPaint);
//3, 其次画中间的颜色深一点的圆圈 #B6B6B6
//画圆形,指定好中心点坐标、半径、画笔
canvas.drawCircle(mRadius, mRadius, SMALL_CIRCLE_RADIUS, mFGPaint);
}
//设置是否存在密码
public void setHadPass(boolean exist) {
LogUtil.d(TAG, "密码输入框切换状态");
//1, 判断是否存在密码 设置前景色
if (exist) {
mFGPaint.setARGB(255, 182, 182, 182);//设置画笔颜色
} else {
mFGPaint.setColor(Color.TRANSPARENT); //设置为透明
}
//2, 重绘界面
invalidate();
}
/**
* 测量View的长度
*/
private int measureHeight(int measureSpec) {
int result = 0;
//获取具体的测量模式和大小
int specMode = MeasureSpec.getMode(measureSpec);
int specSize = MeasureSpec.getSize(measureSpec);
if (specMode == MeasureSpec.EXACTLY) { //默认的大小(指定了具体的值 比如android:layout_width=100dp)
result = measureSpec;
} else {
result = 45;
if (specMode == MeasureSpec.AT_MOST) { //wrap_content
result = Math.min(result, specSize);
}
}
return result;
}
/**
* 测量View的宽度
*/
private int measureWidth(int measureSpec) {
int result = 0;
int specMode = MeasureSpec.getMode(measureSpec); //获取具体的测量模式(EXACTLY,AT_MOST,UNSPECIFIED)
int specSize = MeasureSpec.getSize(measureSpec); //获取具体的测量大小
if (specMode == MeasureSpec.EXACTLY) { //默认模式
result = measureSpec;
} else {
result = 45; //给一个默认的大小
if (specMode == MeasureSpec.AT_MOST) { //如果是wrap_content
result = Math.min(result, specSize); //取最小的 适合控件大小的
}
}
return result;
}
}
2. 然后需要建Activity的布局文件activity_enter_pas.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.xfhy.mobilesafe.activity.EnterPasActivity">
<!--程序锁拦截界面布局-->
<!--返回按钮-->
<ImageButton
android:id="@+id/ib_back"
android:layout_width="64dp"
android:layout_height="64dp"
android:background="@android:color/transparent"
android:padding="5dp"
android:src="@drawable/back"/>
<TextView
android:layout_width="match_parent"
android:layout_height="64dp"
android:layout_toRightOf="@id/ib_back"
android:gravity="center_vertical"
android:text="应用锁"
android:textColor="@color/colorBlack"
android:textSize="20sp"/>
<View
android:layout_width="match_parent"
android:layout_height="2dp"
android:layout_below="@id/ib_back"
android:background="@color/colorGray"/>
<!--下面的数字 用来输入密码的-->
<android.support.v7.widget.RecyclerView
android:id="@+id/rv_keyboard"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_centerHorizontal="true"/>
<LinearLayout
android:id="@+id/ll_pass_input"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_above="@id/rv_keyboard"
android:gravity="center_horizontal"
android:orientation="horizontal">
<!--密码输入框-->
<com.xfhy.mobilesafe.view.CirclePsdView
android:id="@+id/cpv_pass1"
android:layout_width="50dp"
android:layout_height="50dp"
android:layout_centerHorizontal="true"
android:layout_margin="5dp"
android:layout_marginBottom="64dp"/>
<!--密码输入框-->
<com.xfhy.mobilesafe.view.CirclePsdView
android:id="@+id/cpv_pass2"
android:layout_width="50dp"
android:layout_height="50dp"
android:layout_centerHorizontal="true"
android:layout_margin="5dp"
android:layout_marginBottom="64dp"/>
<!--密码输入框-->
<com.xfhy.mobilesafe.view.CirclePsdView
android:id="@+id/cpv_pass3"
android:layout_width="50dp"
android:layout_height="50dp"
android:layout_centerHorizontal="true"
android:layout_margin="5dp"
android:layout_marginBottom="64dp"/>
<!--密码输入框-->
<com.xfhy.mobilesafe.view.CirclePsdView
android:id="@+id/cpv_pass4"
android:layout_width="50dp"
android:layout_height="50dp"
android:layout_centerHorizontal="true"
android:layout_margin="5dp"
android:layout_marginBottom="64dp"/>
</LinearLayout>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_above="@id/ll_pass_input"
android:layout_centerHorizontal="true"
android:layout_marginBottom="10dp"
android:text="输入密码"
android:textColor="@color/colorBlack"
android:textSize="20sp"/>
</RelativeLayout>
3. 再新建一个RecyclerView的子项布局文件layout_pass_keyborad_item.xml
<?xml version="1.0" encoding="utf-8"?>
<Button xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/bt_keyboard"
android:layout_width="80dp"
android:layout_height="80dp"
android:background="@drawable/selector_keyborad_bg"
android:gravity="center"
android:text="1"
android:textColor="@color/colorBlack"
android:textSize="20sp"
android:textStyle="bold"/>
4. 再新建一个selector,RecyclerView子项的圆形selector,在res/drawable下面新建 selector_keybroad_bg.xml
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<!--九宫格界面的子项点击时的selector-->
<item android:state_focused="true"
android:state_enabled="true"
android:state_pressed="false">
<shape
xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="oval">
<solid android:color="@color/colorGray"/>
<size
android:width="80dp"
android:height="80dp"/>
</shape>
</item>
<item android:state_enabled="true"
android:state_pressed="true" >
<shape
xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="oval">
<solid android:color="@color/colorGray"/>
<size
android:width="80dp"
android:height="80dp"/>
</shape>
</item>
<item android:state_enabled="true"
android:state_checked="true">
<shape
xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="oval">
<solid android:color="@color/colorGray"/>
<size
android:width="80dp"
android:height="80dp"/>
</shape>
</item>
<item android:state_focused="false"
android:state_enabled="true"
android:state_pressed="true" >
<shape
xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="oval">
<solid android:color="@color/colorGray"/>
<size
android:width="80dp"
android:height="80dp"/>
</shape>
</item>
<item android:state_enabled="true" android:state_focused="true"
android:state_pressed="false">
<shape
xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="oval">
<solid android:color="@color/colorGray"/>
<size
android:width="80dp"
android:height="80dp"/>
</shape>
</item>
<item android:color="@color/colorTransparent" >
<shape
xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="oval">
<solid android:color="@color/colorTransparent"/>
<size
android:width="80dp"
android:height="80dp"/>
</shape>
</item>
</selector>
为了在API 21以上有更好的体验,需要在res下新建drawable-v21文件夹,再新建selector_keybroad_item.xml
<?xml version="1.0" encoding="utf-8"?>
<ripple xmlns:android="http://schemas.android.com/apk/res/android"
android:color="@color/colorGray">
<item android:drawable="@color/colorWhite"/>
</ripple>
5. 最后,界面的Activity,EnterPasActivity.java
public class EnterPasActivity extends BaseActivity {
private static final String TAG = "EnterPasActivity";
@BindView(R.id.ib_back)
ImageButton ibBack;
@BindView(R.id.rv_keyboard)
RecyclerView rvKeyboard;
@BindView(R.id.cpv_pass1)
CirclePsdView cpvPass1;
@BindView(R.id.cpv_pass2)
CirclePsdView cpvPass2;
@BindView(R.id.cpv_pass3)
CirclePsdView cpvPass3;
@BindView(R.id.cpv_pass4)
CirclePsdView cpvPass4;
/**
* 按钮上需要显示的内容
*/
private String[] mKeyTitleStrs;
private Context mContext;
private MyKeyBoardAdapter mAdapter;
/**
* 当前已经输入的密码的个数
*/
private int passInputCount = 0;
/**
* 用来存放密码的数组
*/
private int passArray[] = {-1, -1, -1, -1};
private static final int WAIT_DRAW = 1000;
/**
* 存放密码输入框
*/
private CirclePsdView[] psdViewArray;
private Handler mHandler = new Handler(){
@Override
public void handleMessage(Message msg) {
switch (msg.what){
case WAIT_DRAW:
//2, 清空数组
for (int i = 0; i < passArray.length; i++) {
passArray[i] = -1;
psdViewArray[i].setHadPass(false);
}
//3, 设置输入的密码位数为0
passInputCount = 0;
break;
}
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_enter_pas);
mContext = this;
ButterKnife.bind(this);
initData();
}
/**
* 初始化数据
*/
private void initData() {
//1, 准备数据 自定义键盘
mKeyTitleStrs = new String[]{
"1", "2", "3", "4", "5", "6", "7", "8", "9", "", "0", "×"
};
//2, 创建适配器
mAdapter = new MyKeyBoardAdapter();
//设置adapter
rvKeyboard.setAdapter(mAdapter);
//设置LayoutManager
GridLayoutManager gridLayoutManager = new GridLayoutManager(mContext, 3);
rvKeyboard.setLayoutManager(gridLayoutManager);
//存放密码输入框
psdViewArray = new CirclePsdView[]{cpvPass1, cpvPass2, cpvPass3, cpvPass4};
}
/**
* 九宫格的适配器RecyclerView
*/
private class MyKeyBoardAdapter extends RecyclerView.Adapter<MyKeyBoardAdapter
.ViewHolder> {
class ViewHolder extends RecyclerView.ViewHolder {
TextView btKeyboard;
public ViewHolder(View itemView) {
super(itemView);
btKeyboard = (TextView) itemView.findViewById(R.id.bt_keyboard);
}
}
@Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = LayoutInflater.from(mContext).inflate(R.layout.layout_pass_keyboard_item,
parent, false);
return new ViewHolder(view);
}
@Override
public void onBindViewHolder(ViewHolder holder, final int position) {
//1, 设置按钮上的内容
if (mKeyTitleStrs != null && position < mKeyTitleStrs.length) {
holder.btKeyboard.setText(mKeyTitleStrs[position]);
}
//2, 设置第10个按钮不可用
if (position == 9) {
holder.btKeyboard.setVisibility(View.GONE);
}
//3, 设置按钮点击事件
holder.btKeyboard.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
LogUtil.d(TAG, "position--------" + position);
//输入的密码个数不对 下标越界
if (passInputCount < 0 || passInputCount > 3) {
return;
}
//点击了一下键盘,则密码位数+1
passInputCount++;
//判断到底点了哪个按钮
switch (position) {
case 0:
passArray[passInputCount - 1] = 1;
psdViewArray[passInputCount - 1].setHadPass(true);
break;
case 1:
passArray[passInputCount - 1] = 2;
psdViewArray[passInputCount - 1].setHadPass(true);
break;
case 2:
passArray[passInputCount - 1] = 3;
psdViewArray[passInputCount - 1].setHadPass(true);
break;
case 3:
passArray[passInputCount - 1] = 4;
psdViewArray[passInputCount - 1].setHadPass(true);
break;
case 4:
passArray[passInputCount - 1] = 5;
psdViewArray[passInputCount - 1].setHadPass(true);
break;
case 5:
passArray[passInputCount - 1] = 6;
psdViewArray[passInputCount - 1].setHadPass(true);
break;
case 6:
passArray[passInputCount - 1] = 7;
psdViewArray[passInputCount - 1].setHadPass(true);
break;
case 7:
passArray[passInputCount - 1] = 8;
psdViewArray[passInputCount - 1].setHadPass(true);
break;
case 8:
passArray[passInputCount - 1] = 9;
psdViewArray[passInputCount - 1].setHadPass(true);
break;
case 10:
passArray[passInputCount - 1] = 0;
psdViewArray[passInputCount - 1].setHadPass(true);
break;
case 11:
LogUtil.d(TAG, "干掉: passInputCount" + passInputCount);
if (passInputCount == 1) {
passInputCount = 0;
return;
}
passArray[passInputCount - 2] = -1;
psdViewArray[passInputCount - 2].setHadPass(false);
passInputCount -= 2;
break;
}
//输入的密码位数达到4位
if (passInputCount == 4) {
//1, 判断密码是否正确
if (passArray[0] == 1 && passArray[1] == 1 && passArray[2] == 1 &&
passArray[3] == 1) {
ToastUtil.showSuccess("密码验证成功");
} else {
ToastUtil.showError("密码验证失败");
}
//开一个子线程,等待500毫秒再更新UI,不然最后一个控件还没来得及画出来,就被设置为false了
new Thread(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
//发送 等待完成 信号 可以刷新UI啦
mHandler.sendEmptyMessage(WAIT_DRAW);
}
}).start();
}
}
});
}
@Override
public int getItemCount() {
return mKeyTitleStrs == null ? 0 : mKeyTitleStrs.length;
}
}
}

浙公网安备 33010602011771号