欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  移动技术

Android实现支付宝6位密码输入界面

程序员文章站 2024-02-22 11:54:46
 我们先来照图分析一下: (1)限制输入6位,每一位都有自己的框格,每个格显示一位; (2)有回退/取消支付按钮; (3)有忘记密码链接; (4)自定义的...

 我们先来照图分析一下:
(1)限制输入6位,每一位都有自己的框格,每个格显示一位;
(2)有回退/取消支付按钮;
(3)有忘记密码链接;
(4)自定义的只能输入数字的键盘输入区;
(5)在6位输完后自动进行密码校验和支付交易。如上图左边是ios支付宝支付密码输入控件,右边是我模仿实现的效果。

首先,我们需要一个页面来完成以上的静态布局,.xml代码如下:

<?xml version="1.0" encoding="utf-8"?> 
<relativelayout xmlns:android="http://schemas.android.com/apk/res/android" 
 android:layout_width="match_parent" 
 android:layout_height="match_parent" 
 android:background="#eeeeee" 
 android:gravity="bottom"> 
 
 <linearlayout 
  android:id="@+id/linear_pass" 
  android:layout_width="match_parent" 
  android:layout_height="wrap_content" 
  android:orientation="vertical"> 
 
  <relativelayout 
   android:layout_width="match_parent" 
   android:layout_height="wrap_content" 
   android:layout_margin="5dp"> 
 
   <!-- 取消按钮 --> 
   <imageview 
    android:id="@+id/img_cancel" 
    android:layout_width="wrap_content" 
    android:layout_height="wrap_content" 
    android:background="@drawable/icon_clean" /> 
 
   <textview 
    android:layout_width="wrap_content" 
    android:layout_height="wrap_content" 
    android:layout_centerinparent="true" 
    android:text="输入密码" 
    android:textcolor="#898181" 
    android:textsize="20sp" /> 
  </relativelayout> 
 
  <view 
   android:layout_width="match_parent" 
   android:layout_height="0.5dp" 
   android:background="#555555" /> 
 
  <!-- 6位密码框布局,需要一个圆角边框的shape作为layout的背景 --> 
  <linearlayout 
   android:layout_width="match_parent" 
   android:layout_height="wrap_content" 
   android:layout_marginleft="40dp" 
   android:layout_marginright="40dp" 
   android:layout_margintop="20dp" 
   android:background="@drawable/shape_input_area" 
   android:orientation="horizontal"> 
 
   <!-- inputtype设置隐藏密码明文 
     textsize设置大一点,否则“点”太小了,不美观 --> 
   <textview 
    android:id="@+id/tv_pass1" 
    android:layout_width="0dp" 
    android:layout_height="wrap_content" 
    android:layout_weight="1" 
    android:gravity="center" 
    android:inputtype="numberpassword" 
    android:textsize="32sp" /> 
 
   <view 
    android:layout_width="1dp" 
    android:layout_height="match_parent" 
    android:background="#999999" /> 
 
   <textview 
    android:id="@+id/tv_pass2" 
    android:layout_width="0dp" 
    android:layout_height="wrap_content" 
    android:layout_weight="1" 
    android:gravity="center" 
    android:inputtype="numberpassword" 
    android:textsize="32sp" /> 
 
   <view 
    android:layout_width="1dp" 
    android:layout_height="match_parent" 
    android:background="#999999" /> 
 
   <textview 
    android:id="@+id/tv_pass3" 
    android:layout_width="0dp" 
    android:layout_height="wrap_content" 
    android:layout_weight="1" 
    android:gravity="center" 
    android:inputtype="numberpassword" 
    android:textsize="32sp" /> 
 
   <view 
    android:layout_width="1dp" 
    android:layout_height="match_parent" 
    android:background="#999999" /> 
 
   <textview 
    android:id="@+id/tv_pass4" 
    android:layout_width="0dp" 
    android:layout_height="wrap_content" 
    android:layout_weight="1" 
    android:gravity="center" 
    android:inputtype="numberpassword" 
    android:textsize="32sp" /> 
 
   <view 
    android:layout_width="1dp" 
    android:layout_height="match_parent" 
    android:background="#999999" /> 
 
   <textview 
    android:id="@+id/tv_pass5" 
    android:layout_width="0dp" 
    android:layout_height="wrap_content" 
    android:layout_weight="1" 
    android:gravity="center" 
    android:inputtype="numberpassword" 
    android:textsize="32sp" /> 
 
   <view 
    android:layout_width="1dp" 
    android:layout_height="match_parent" 
    android:background="#999999" /> 
 
   <textview 
    android:id="@+id/tv_pass6" 
    android:layout_width="0dp" 
    android:layout_height="wrap_content" 
    android:layout_weight="1" 
    android:gravity="center" 
    android:inputtype="numberpassword" 
    android:textsize="32sp" /> 
  </linearlayout> 
 
  <!-- 忘记密码链接 --> 
  <textview 
   android:id="@+id/tv_forgetpwd" 
   android:layout_width="wrap_content" 
   android:layout_height="wrap_content" 
   android:layout_gravity="right" 
   android:layout_margin="15dp" 
   android:text="忘记密码?" 
   android:textcolor="#354eef" /> 
 </linearlayout> 
 
 <!-- 输入键盘 --> 
 <gridview 
  android:id="@+id/gv_keybord" 
  android:layout_width="match_parent" 
  android:layout_height="wrap_content" 
  android:layout_below="@id/linear_pass" 
  android:layout_margintop="40dp" 
  android:background="@android:color/black" 
  android:horizontalspacing="0.5dp" 
  android:numcolumns="3" 
  android:verticalspacing="0.5dp" /> 
</relativelayout> 

其中需要圆角背景shape_input_area.xml:

<?xml version="1.0" encoding="utf-8"?> 
<shape xmlns:android="http://schemas.android.com/apk/res/android"> 
 <corners android:radius="5dp"/> 
 <stroke android:color="@android:color/darker_gray" 
  android:width="1dp"/> 
 <solid android:color="@android:color/white"/> 
</shape> 

需要数字按钮的背景selector_gride.xml:

<?xml version="1.0" encoding="utf-8"?> 
<selector xmlns:android="http://schemas.android.com/apk/res/android"> 
 <item android:state_enabled="false"> 
  <shape> 
   <solid android:color="#c0c4c7" /> 
  </shape> 
 </item> 
 <item android:state_enabled="true" android:state_pressed="false"> 
  <shape> 
   <solid android:color="@android:color/white" /> 
  </shape> 
 </item> 
 <item android:state_enabled="true" android:state_pressed="true"> 
  <shape> 
   <solid android:color="#c0c4c7" /> 
  </shape> 
 </item> 
</selector> 

需要回退键背景selector_key_del.xml:

<?xml version="1.0" encoding="utf-8"?> 
<selector xmlns:android="http://schemas.android.com/apk/res/android"> 
 <item android:state_enabled="false"> 
  <shape> 
   <solid android:color="#c0c4c7" /> 
  </shape> 
 </item> 
 <item android:state_enabled="true" android:state_pressed="false"> 
  <shape> 
   <solid android:color="#c0c4c7" /> 
  </shape> 
 </item> 
 <item android:state_enabled="true" android:state_pressed="true"> 
  <shape> 
   <solid android:color="@android:color/white" /> 
  </shape> 
 </item> 
</selector> 

下面来完成我们的自定义控件passwordview.java:

public class passwordview extends relativelayout implements view.onclicklistener { 
 context context; 
 
 private string strpassword;  //输入的密码 
 private textview[] tvlist;  //用数组保存6个textview,为什么用数组? 
         //因为就6个输入框不会变了,用数组内存申请固定空间,比list省空间(自己认为) 
 private gridview gridview; //用grideview布局键盘,其实并不是真正的键盘,只是模拟键盘的功能 
 private arraylist<map<string, string>> valuelist; //有人可能有疑问,为何这里不用数组了? 
              //因为要用adapter中适配,用数组不能往adapter中填充 
 
 private imageview imgcancel; 
 private textview tvforget; 
 private int currentindex = -1; //用于记录当前输入密码格位置 
 
 public passwordview(context context) { 
  this(context, null); 
 } 
 
 public passwordview(context context, attributeset attrs) { 
  super(context, attrs); 
  this.context = context; 
  view view = view.inflate(context, r.layout.layout_popup_bottom, null); 
   
  valuelist = new arraylist<map<string, string>>(); 
  tvlist = new textview[6]; 
   
  imgcancel = (imageview) view.findviewbyid(r.id.img_cancel); 
  imgcancel.setonclicklistener(this); 
 
  tvforget = (textview) findviewbyid(r.id.tv_forgetpwd); 
  tvforget.setonclicklistener(this); 
   
  tvlist[0] = (textview) view.findviewbyid(r.id.tv_pass1); 
  tvlist[1] = (textview) view.findviewbyid(r.id.tv_pass2); 
  tvlist[2] = (textview) view.findviewbyid(r.id.tv_pass3); 
  tvlist[3] = (textview) view.findviewbyid(r.id.tv_pass4); 
  tvlist[4] = (textview) view.findviewbyid(r.id.tv_pass5); 
  tvlist[5] = (textview) view.findviewbyid(r.id.tv_pass6); 
 
  gridview = (gridview) view.findviewbyid(r.id.gv_keybord); 
 
  setview(); 
   
  addview(view);  //必须要,不然不显示控件 
 } 
 
 @override 
 public void onclick(view v) { 
  switch (v.getid()) { 
   case r.id.img_cancel: 
    toast.maketext(context, "cancel", toast.length_short).show(); 
    break; 
   case r.id.tv_forgetpwd: 
    toast.maketext(context, "forget", toast.length_short).show(); 
    break; 
  } 
 } 
 
 private void setview() { 
  /* 初始化按钮上应该显示的数字 */ 
  for (int i = 1; i < 13; i++) { 
   map<string, string> map = new hashmap<string, string>(); 
   if (i < 10) { 
    map.put("name", string.valueof(i)); 
   } else if (i == 10) { 
    map.put("name", ""); 
   } else if (i == 12) { 
    map.put("name", "<<-"); 
   } else if (i == 11) { 
    map.put("name", string.valueof(0)); 
   } 
   valuelist.add(map); 
  } 
 
  gridview.setadapter(adapter); 
  gridview.setonitemclicklistener(new adapterview.onitemclicklistener() { 
   @override 
   public void onitemclick(adapterview<?> parent, view view, int position, long id) { 
    if (position < 11 && position != 9) { //点击0~9按钮 
     if (currentindex >= -1 && currentindex < 5) {  //判断输入位置————要小心数组越界 
      tvlist[++currentindex].settext(valuelist.get(position).get("name")); 
     } 
    } else { 
     if (position == 11) {  //点击退格键 
      if (currentindex - 1 >= -1) {  //判断是否删除完毕————要小心数组越界 
       tvlist[currentindex--].settext(""); 
      } 
     } 
    } 
   } 
  }); 
 } 
 
 //设置监听方法,在第6位输入完成后触发 
 public void setonfinishinput(final onpasswordinputfinish pass) { 
  tvlist[5].addtextchangedlistener(new textwatcher() { 
   @override 
   public void beforetextchanged(charsequence s, int start, int count, int after) { 
 
   } 
 
   @override 
   public void ontextchanged(charsequence s, int start, int before, int count) { 
 
   } 
 
   @override 
   public void aftertextchanged(editable s) { 
    if (s.tostring().length() == 1) { 
     strpassword = "";  //每次触发都要先将strpassword置空,再重新获取,避免由于输入删除再输入造成混乱 
     for (int i = 0; i < 6; i++) { 
      strpassword += tvlist[i].gettext().tostring().trim(); 
     } 
     pass.inputfinish(); //接口中要实现的方法,完成密码输入完成后的响应逻辑 
    } 
   } 
  }); 
 } 
 
 /* 获取输入的密码 */ 
 public string getstrpassword() { 
  return strpassword; 
 } 
 
 /* 暴露取消支付的按钮,可以灵活改变响应 */ 
 public imageview getcancelimageview() { 
  return imgcancel; 
 } 
 
 /* 暴露忘记密码的按钮,可以灵活改变响应 */ 
 public textview getforgettextview() { 
  return tvforget; 
 } 
 
 //grideview的适配器 
 baseadapter adapter = new baseadapter() { 
  @override 
  public int getcount() { 
   return valuelist.size(); 
  } 
 
  @override 
  public object getitem(int position) { 
   return valuelist.get(position); 
  } 
 
  @override 
  public long getitemid(int position) { 
   return position; 
  } 
 
  @override 
  public view getview(int position, view convertview, viewgroup parent) { 
   viewholder viewholder; 
   if (convertview == null) { 
    convertview = view.inflate(context, r.layout.item_gride, null); 
    viewholder = new viewholder(); 
    viewholder.btnkey = (textview) convertview.findviewbyid(r.id.btn_keys); 
    convertview.settag(viewholder); 
   } else { 
    viewholder = (viewholder) convertview.gettag(); 
   } 
   viewholder.btnkey.settext(valuelist.get(position).get("name")); 
   if(position == 9){ 
    viewholder.btnkey.setbackgroundresource(r.drawable.selector_key_del); 
    viewholder.btnkey.setenabled(false); 
   } 
   if(position == 11){ 
    viewholder.btnkey.setbackgroundresource(r.drawable.selector_key_del); 
   } 
 
   return convertview; 
  } 
 }; 
 
 /** 
  * 存放控件 
  */ 
 public final class viewholder { 
  public textview btnkey; 
 } 
} 

自认为代码注释还是可以的。就是在实现过程中要注意数组的越界问题,在输入逻辑响应中要注意逻辑处理,也就是grideview的onitemclicklistener事件处理。其中用到自定义的接口onpasswordinputfinish来实现输入完成的事件回掉:

/** 
 * belong to the project —— mypayui 
 * created by wangj on 2015/11/25 17:15. 
 * 
 * 自定义接口,用于给密码输入完成添加回掉事件 
 */ 
public interface onpasswordinputfinish { 
 void inputfinish(); 
} 

还有就是adapter中用到的每个按钮item的布局item_gride.xml:

<?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="match_parent"> 
 
 <!-- 模拟键盘按钮,当然你可以用button,但要注意button和grideview的点击响应问题 --> 
 <textview 
  android:id="@+id/btn_keys" 
  android:layout_width="match_parent" 
  android:layout_height="match_parent" 
  android:padding="10dp" 
  android:gravity="center" 
  android:textsize="25sp" 
  android:background="@drawable/selector_gride"/> 
</linearlayout> 

好了,到此我们的自定义控件——模仿支付宝6位支付密码输入控件就完成了,下边我们在activity中用一下,检验一下效果:
我们在mianactivity中用用一下我们定义好的控件:

public class mainactivity extends activity { 
 
 @override 
 protected void oncreate(bundle savedinstancestate) { 
  super.oncreate(savedinstancestate); 
   
  /************* 第一种用法————开始 ***************/ 
  setcontentview(r.layout.activity_main); 
 
  final passwordview pwdview = (passwordview) findviewbyid(r.id.pwd_view); 
   
  //添加密码输入完成的响应 
  pwdview.setonfinishinput(new onpasswordinputfinish() { 
   @override 
   public void inputfinish() { 
    //输入完成后我们简单显示一下输入的密码 
    //也就是说——>实现你的交易逻辑什么的在这里写 
    toast.maketext(mainactivity.this, pwdview.getstrpassword(), toast.length_short).show(); 
   } 
  }); 
   
  /** 
   * 可以用自定义控件中暴露出来的cancelimageview方法,重新提供相应 
   * 如果写了,会覆盖我们在自定义控件中提供的响应 
   * 可以看到这里toast显示 "biu biu biu"而不是"cancel"*/ 
  pwdview.getcancelimageview().setonclicklistener(new view.onclicklistener() { 
   @override 
   public void onclick(view v) { 
    toast.maketext(mainactivity.this, "biu biu biu", toast.length_short).show(); 
   } 
  }); 
  /************ 第一种用法————结束 ******************/ 
 
   
  /************* 第二种用法————开始 *****************/ 
//  final passwordview pwdview = new passwordview(this); 
//  setcontentview(pwdview); 
//  pwdview.setonfinishinput(new onpasswordinputfinish() { 
//   @override 
//   public void inputfinish() { 
//    toast.maketext(mainactivity.this, pwdview.getstrpassword(), toast.length_short).show(); 
//   } 
//  }); 
  /************** 第二种用法————结束 ****************/ 
 } 
} 

在第一种方法中我们用到的布局文件:

<?xml version="1.0" encoding="utf-8"?> 
<relativelayout 
 android:id="@+id/xxx" 
 xmlns:android="http://schemas.android.com/apk/res/android" 
 android:layout_width="match_parent" 
 android:layout_height="match_parent" 
 android:background="#624762"> 
 
 <com.wangj.mypayview.passwordview 
  android:id="@+id/pwd_view" 
  android:layout_width="match_parent" 
  android:layout_height="wrap_content" 
  android:layout_alignparentbottom="true"/> 
</relativelayout> 

更多内容请参考专题:android密码使用教程

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。