数字键盘已经有了,剩下的主要是密码输入的布局。这里想到了一个简单的思路,利用 6 个 ImageView 来显示小黑点就可以了,每次按下数字键显示一个小黑点,按下删除键则隐藏一个小黑点。
布局文件非常简单,6 个横向排列的 FrameLayout 分别放入一个 ImageView,之后会给出源码。代码部分的实现:
/**
* 密码输入布局(6位密码)
* Created by ayuhani on 2017/6/29.
*/
public class PasswordView extends RelativeLayout {
private String[] numbers; // 用来保存输入的密码
private ImageView[] points; // 用来保存每个小黑点
private FrameLayout[] frameLayouts;
public PasswordView(Context context) {
this(context, null);
}
public PasswordView(Context context, AttributeSet attrs) {
super(context, attrs);
init(context);
}
private void init(Context context) {
numbers = new String[6];
points = new ImageView[6];
frameLayouts = new FrameLayout[6];
LayoutInflater.from(context).inflate(R.layout.layout_password, this);
points[0] = findViewById(R.id.iv_0);
points[1] = findViewById(R.id.iv_1);
points[2] = findViewById(R.id.iv_2);
points[3] = findViewById(R.id.iv_3);
points[4] = findViewById(R.id.iv_4);
points[5] = findViewById(R.id.iv_5);
// 这里获取外层的FrameLayout,是因为之后要给它们添加点击事件
frameLayouts[0] = findViewById(R.id.fl_0);
frameLayouts[1] = findViewById(R.id.fl_1);
frameLayouts[2] = findViewById(R.id.fl_2);
frameLayouts[3] = findViewById(R.id.fl_3);
frameLayouts[4] = findViewById(R.id.fl_4);
frameLayouts[5] = findViewById(R.id.fl_5);
}
// 获取保存6位密码的数组
public String[] getNumbers() {
return numbers;
}
// 获取保存小黑点的数组
public ImageView[] getPoints() {
return points;
}
// 获取小黑点密码父布局的数组(用来添加点击事件)
public FrameLayout[] getFrameLayouts() {
return frameLayouts;
}
// 获取6位支付密码
public String getPassword() {
StringBuilder builder = new StringBuilder();
for (int i = 0; i < numbers.length; i++) {
if (numbers[i] != null) {
builder.append(numbers[i]);
}
}
return builder.toString();
}
// 清空密码
public void clear() {
for (int i = 0; i < numbers.length; i++) {
numbers[i] = null;
}
for (int i = 0; i < points.length; i++) {
points[i].setVisibility(GONE);
}
}
}
xml 也不贴出来了,效果如图
自定义类继承自 PopupWindow,关联上面的布局文件
/**
* 仿微信支付布局
* Created by ayuhani on 2017/6/29.
*/
public class WeChatPayWindow extends PopupWindow implements KeyboardAdapter.OnKeyboardClickListener {
private ImageView ivClose; // 关闭按钮
private ImageView ivIcon; // 头像
private TextView tvTitle; // 标题
private TextView tvMessage; // 消费详情
private TextView tvPrice; // 价格
private PasswordView passwordView;
private KeyboardView keyboardView;
private List datas;
private String[] numbers;
private ImageView[] points;
private int currentIndex; // 当前即将要输入密码的格子的索引
public OnPasswordFinishedListener listener;
public WeChatPayWindow(Context context) {
super(context);
init(context);
}
private void init(Context context) {
View contentView = LayoutInflater.from(context).inflate(R.layout.layout_wechat_pay, null);
setContentView(contentView);
setHeight(ViewGroup.LayoutParams.MATCH_PARENT);
setWidth(ViewGroup.LayoutParams.MATCH_PARENT);
setFocusable(true);
setClippingEnabled(false); // 让PopupWindow同样覆盖状态栏
setBackgroundDrawable(new ColorDrawable(0xAA000000)); // 加上一层黑色透明背景
initView(contentView);
}
private void initView(View contentView) {
// 获取布局中的各个控件
ivClose = contentView.findViewById(R.id.iv_close);
ivIcon = contentView.findViewById(R.id.iv_icon);
tvTitle = contentView.findViewById(R.id.tv_title);
tvMessage = contentView.findViewById(R.id.tv_message);
tvPrice = contentView.findViewById(R.id.tv_price);
passwordView = contentView.findViewById(R.id.password_view);
keyboardView = contentView.findViewById(R.id.keyboard_view);
ivClose.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
dismiss();
}
});
keyboardView.setOnKeyBoardClickListener(this);
datas = keyboardView.getDatas();
numbers = passwordView.getNumbers();
points = passwordView.getPoints();
// 这里给每个FrameLayout添加点击事件,当键盘被收起时点击空白输入框,再次弹出键盘
// 微信也是这样的,但我觉得并没有什么意义
for (int i = 0; i < passwordView.getFrameLayouts().length; i++) {
final int finalI = i;
passwordView.getFrameLayouts()[i].setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
if (points[finalI].getVisibility() != View.VISIBLE && !keyboardView.isVisible()){
keyboardView.show();
}
}
});
}
}
// 可以自定义一些方法
public WeChatPayWindow setIcon(String url) {
// 设置头像
return this;
}
public WeChatPayWindow setTitle(CharSequence title) {
tvTitle.setText(title);
return this;
}
public WeChatPayWindow setMessage(CharSequence message) {
tvMessage.setText(message);
return this;
}
public WeChatPayWindow setPrice(CharSequence price) {
tvPrice.setText(price);
return this;
}
// 弹出PopupWindow
public void show(View rootView) {
showAtLocation(rootView, Gravity.BOTTOM | Gravity.CENTER_HORIZONTAL, 0, 0);
}
@Override
public void onKeyClick(View view, RecyclerView.ViewHolder holder, int position) {
switch (position) {
case 9: // 点击小数点没有作用,最好是把小数点隐藏掉,我这里偷懒了
break;
default:
if (currentIndex >= 0 && currentIndex < numbers.length) {
numbers[currentIndex] = datas.get(position);
points[currentIndex].setVisibility(View.VISIBLE);
currentIndex++; // 当前位置的密码输入后,位置加一
if (currentIndex == numbers.length && listener != null) {
// 已经输入了六位数的密码了,回调方法
listener.onFinish(passwordView.getPassword());
}
}
}
}
@Override
public void onDeleteClick(View view, RecyclerView.ViewHolder holder, int position) {
// 点击删除按钮
if (currentIndex > 0 && currentIndex < numbers.length) {
currentIndex--;
numbers[currentIndex] = "";
points[currentIndex].setVisibility(View.GONE);
}
}
// 写一个回调接口,输入密码完成后调用
public interface OnPasswordFinishedListener {
void onFinish(String password);
}
public void setOnPasswordFinishedListener(OnPasswordFinishedListener listener) {
this.listener = listener;
}
}
在 Activity 里面显示就可以了。动态图:
支付宝支付时有选择支付方式、指纹支付等高级功能,我这里则只是简单的实现了密码输入,和上面的实现一样,只是布局不同,所以我也不贴代码了,直接看效果吧: