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

转自:http://blog.csdn.net/dong_18383219470/article/details/52639066

我们先来照图分析一下:

(1)限制输入6位,每一位都有自己的框格,每个格显示一位;

(2)有回退/取消支付按钮;

(3)有忘记密码链接;

(4)自定义的只能输入数字的键盘输入区;

(5)在6位输完后自动进行密码校验和支付交易。如上图左边是iOS支付宝支付密码输入控件,右边是我模仿实现的效果。


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

[html]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. <?xml version="1.0" encoding="utf-8"?>    
  2. <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"    
  3.     android:layout_width="match_parent"    
  4.     android:layout_height="match_parent"    
  5.     android:background="#EEEEEE"    
  6.     android:gravity="bottom">    
  7.     
  8.     <LinearLayout    
  9.         android:id="@+id/linear_pass"    
  10.         android:layout_width="match_parent"    
  11.         android:layout_height="wrap_content"    
  12.         android:orientation="vertical">    
  13.     
  14.         <RelativeLayout    
  15.             android:layout_width="match_parent"    
  16.             android:layout_height="wrap_content"    
  17.             android:layout_margin="5dp">    
  18.     
  19.             <!-- 取消按钮 -->    
  20.             <ImageView    
  21.                 android:id="@+id/img_cancel"    
  22.                 android:layout_width="wrap_content"    
  23.                 android:layout_height="wrap_content"    
  24.                 android:background="@drawable/icon_clean" />    
  25.     
  26.             <TextView    
  27.                 android:layout_width="wrap_content"    
  28.                 android:layout_height="wrap_content"    
  29.                 android:layout_centerInParent="true"    
  30.                 android:text="输入密码"    
  31.                 android:textColor="#898181"    
  32.                 android:textSize="20sp" />    
  33.         </RelativeLayout>    
  34.     
  35.         <View    
  36.             android:layout_width="match_parent"    
  37.             android:layout_height="0.5dp"    
  38.             android:background="#555555" />    
  39.     
  40.         <!-- 6位密码框布局,需要一个圆角边框的shape作为layout的背景 -->    
  41.         <LinearLayout    
  42.             android:layout_width="match_parent"    
  43.             android:layout_height="wrap_content"    
  44.             android:layout_marginLeft="40dp"    
  45.             android:layout_marginRight="40dp"    
  46.             android:layout_marginTop="20dp"    
  47.             android:background="@drawable/shape_input_area"    
  48.             android:orientation="horizontal">    
  49.     
  50.             <!-- inputType设置隐藏密码明文    
  51.                  textSize设置大一点,否则“点”太小了,不美观 -->    
  52.             <TextView    
  53.                 android:id="@+id/tv_pass1"    
  54.                 android:layout_width="0dp"    
  55.                 android:layout_height="wrap_content"    
  56.                 android:layout_weight="1"    
  57.                 android:gravity="center"    
  58.                 android:inputType="numberPassword"    
  59.                 android:textSize="32sp" />    
  60.     
  61.             <View    
  62.                 android:layout_width="1dp"    
  63.                 android:layout_height="match_parent"    
  64.                 android:background="#999999" />    
  65.     
  66.             <TextView    
  67.                 android:id="@+id/tv_pass2"    
  68.                 android:layout_width="0dp"    
  69.                 android:layout_height="wrap_content"    
  70.                 android:layout_weight="1"    
  71.                 android:gravity="center"    
  72.                 android:inputType="numberPassword"    
  73.                 android:textSize="32sp" />    
  74.     
  75.             <View    
  76.                 android:layout_width="1dp"    
  77.                 android:layout_height="match_parent"    
  78.                 android:background="#999999" />    
  79.     
  80.             <TextView    
  81.                 android:id="@+id/tv_pass3"    
  82.                 android:layout_width="0dp"    
  83.                 android:layout_height="wrap_content"    
  84.                 android:layout_weight="1"    
  85.                 android:gravity="center"    
  86.                 android:inputType="numberPassword"    
  87.                 android:textSize="32sp" />    
  88.     
  89.             <View    
  90.                 android:layout_width="1dp"    
  91.                 android:layout_height="match_parent"    
  92.                 android:background="#999999" />    
  93.     
  94.             <TextView    
  95.                 android:id="@+id/tv_pass4"    
  96.                 android:layout_width="0dp"    
  97.                 android:layout_height="wrap_content"    
  98.                 android:layout_weight="1"    
  99.                 android:gravity="center"    
  100.                 android:inputType="numberPassword"    
  101.                 android:textSize="32sp" />    
  102.     
  103.             <View    
  104.                 android:layout_width="1dp"    
  105.                 android:layout_height="match_parent"    
  106.                 android:background="#999999" />    
  107.     
  108.             <TextView    
  109.                 android:id="@+id/tv_pass5"    
  110.                 android:layout_width="0dp"    
  111.                 android:layout_height="wrap_content"    
  112.                 android:layout_weight="1"    
  113.                 android:gravity="center"    
  114.                 android:inputType="numberPassword"    
  115.                 android:textSize="32sp" />    
  116.     
  117.             <View    
  118.                 android:layout_width="1dp"    
  119.                 android:layout_height="match_parent"    
  120.                 android:background="#999999" />    
  121.     
  122.             <TextView    
  123.                 android:id="@+id/tv_pass6"    
  124.                 android:layout_width="0dp"    
  125.                 android:layout_height="wrap_content"    
  126.                 android:layout_weight="1"    
  127.                 android:gravity="center"    
  128.                 android:inputType="numberPassword"    
  129.                 android:textSize="32sp" />    
  130.         </LinearLayout>    
  131.     
  132.         <!-- 忘记密码链接 -->    
  133.         <TextView    
  134.             android:id="@+id/tv_forgetPwd"    
  135.             android:layout_width="wrap_content"    
  136.             android:layout_height="wrap_content"    
  137.             android:layout_gravity="right"    
  138.             android:layout_margin="15dp"    
  139.             android:text="忘记密码?"    
  140.             android:textColor="#354EEF" />    
  141.     </LinearLayout>    
  142.     
  143.     <!-- 输入键盘 -->    
  144.     <GridView    
  145.         android:id="@+id/gv_keybord"    
  146.         android:layout_width="match_parent"    
  147.         android:layout_height="wrap_content"    
  148.         android:layout_below="@id/linear_pass"    
  149.         android:layout_marginTop="40dp"    
  150.         android:background="@android:color/black"    
  151.         android:horizontalSpacing="0.5dp"    
  152.         android:numColumns="3"    
  153.         android:verticalSpacing="0.5dp" />    
  154. </RelativeLayout>    
  其中需要圆角背景shape_input_area.xml:
[html]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. <?xml version="1.0" encoding="utf-8"?>    
  2. <shape xmlns:android="http://schemas.android.com/apk/res/android">    
  3.     <corners android:radius="5dp"/>    
  4.     <stroke android:color="@android:color/darker_gray"    
  5.         android:width="1dp"/>    
  6.     <solid android:color="@android:color/white"/>    
  7. </shape>   
    需要数字按钮的背景selector_gride.xml:
[html]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. <?xml version="1.0" encoding="utf-8"?>    
  2. <selector xmlns:android="http://schemas.android.com/apk/res/android">    
  3.     <item android:state_enabled="false">    
  4.         <shape>    
  5.             <solid android:color="#C0C4C7" />    
  6.         </shape>    
  7.     </item>    
  8.     <item android:state_enabled="true" android:state_pressed="false">    
  9.         <shape>    
  10.             <solid android:color="@android:color/white" />    
  11.         </shape>    
  12.     </item>    
  13.     <item android:state_enabled="true" android:state_pressed="true">    
  14.         <shape>    
  15.             <solid android:color="#C0C4C7" />    
  16.         </shape>    
  17.     </item>    
  18. </selector>    
  需要回退键背景selector_key_del.xml:
[html]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. <?xml version="1.0" encoding="utf-8"?>    
  2. <selector xmlns:android="http://schemas.android.com/apk/res/android">    
  3.     <item android:state_enabled="false">    
  4.         <shape>    
  5.             <solid android:color="#C0C4C7" />    
  6.         </shape>    
  7.     </item>    
  8.     <item android:state_enabled="true" android:state_pressed="false">    
  9.         <shape>    
  10.             <solid android:color="#C0C4C7" />    
  11.         </shape>    
  12.     </item>    
  13.     <item android:state_enabled="true" android:state_pressed="true">    
  14.         <shape>    
  15.             <solid android:color="@android:color/white" />    
  16.         </shape>    
  17.     </item>    
  18. </selector>    
   下面来完成我们的自定义控件PasswordView. Java
[java]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. public class PasswordView extends RelativeLayout implements View.OnClickListener {    
  2.     Context context;    
  3.     
  4.     private String strPassword;     //输入的密码    
  5.     private TextView[] tvList;      //用数组保存6个TextView,为什么用数组?    
  6.                                     //因为就6个输入框不会变了,用数组内存申请固定空间,比List省空间(自己认为)    
  7.     private GridView gridView;    //用GrideView布局键盘,其实并不是真正的键盘,只是模拟键盘的功能    
  8.     private ArrayList<Map<String, String>> valueList;    //有人可能有疑问,为何这里不用数组了?    
  9.                                                        //因为要用Adapter中适配,用数组不能往adapter中填充    
  10.     
  11.     private ImageView imgCancel;    
  12.     private TextView tvForget;    
  13.     private int currentIndex = -1;    //用于记录当前输入密码格位置    
  14.     
  15.     public PasswordView(Context context) {    
  16.         this(context, null);    
  17.     }    
  18.     
  19.     public PasswordView(Context context, AttributeSet attrs) {    
  20.         super(context, attrs);    
  21.         this.context = context;    
  22.         View view = View.inflate(context, R.layout.layout_popup_bottom, null);    
  23.             
  24.         valueList = new ArrayList<Map<String, String>>();    
  25.         tvList = new TextView[6];    
  26.             
  27.         imgCancel = (ImageView) view.findViewById(R.id.img_cancel);    
  28.         imgCancel.setOnClickListener(this);    
  29.     
  30.         tvForget = (TextView) findViewById(R.id.tv_forgetPwd);    
  31.         tvForget.setOnClickListener(this);    
  32.             
  33.         tvList[0] = (TextView) view.findViewById(R.id.tv_pass1);    
  34.         tvList[1] = (TextView) view.findViewById(R.id.tv_pass2);    
  35.         tvList[2] = (TextView) view.findViewById(R.id.tv_pass3);    
  36.         tvList[3] = (TextView) view.findViewById(R.id.tv_pass4);    
  37.         tvList[4] = (TextView) view.findViewById(R.id.tv_pass5);    
  38.         tvList[5] = (TextView) view.findViewById(R.id.tv_pass6);    
  39.     
  40.         gridView = (GridView) view.findViewById(R.id.gv_keybord);    
  41.     
  42.         setView();    
  43.             
  44.         addView(view);      //必须要,不然不显示控件    
  45.     }    
  46.     
  47.     @Override    
  48.     public void onClick(View v) {    
  49.         switch (v.getId()) {    
  50.             case R.id.img_cancel:    
  51.                 Toast.makeText(context, "Cancel", Toast.LENGTH_SHORT).show();    
  52.                 break;    
  53.             case R.id.tv_forgetPwd:    
  54.                 Toast.makeText(context, "Forget", Toast.LENGTH_SHORT).show();    
  55.                 break;    
  56.         }    
  57.     }    
  58.     
  59.     private void setView() {    
  60.         /* 初始化按钮上应该显示的数字 */    
  61.         for (int i = 1; i < 13; i++) {    
  62.             Map<String, String> map = new HashMap<String, String>();    
  63.             if (i < 10) {    
  64.                 map.put("name", String.valueOf(i));    
  65.             } else if (i == 10) {    
  66.                 map.put("name""");    
  67.             } else if (i == 12) {    
  68.                 map.put("name""<<-");    
  69.             } else if (i == 11) {    
  70.                 map.put("name", String.valueOf(0));    
  71.             }    
  72.             valueList.add(map);    
  73.         }    
  74.     
  75.         gridView.setAdapter(adapter);    
  76.         gridView.setOnItemClickListener(new AdapterView.OnItemClickListener() {    
  77.             @Override    
  78.             public void onItemClick(AdapterView<?> parent, View view, int position, long id) {    
  79.                 if (position < 11 && position != 9) {    //点击0~9按钮    
  80.                     if (currentIndex >= -1 && currentIndex < 5) {      //判断输入位置————要小心数组越界    
  81.                         tvList[++currentIndex].setText(valueList.get(position).get("name"));    
  82.                     }    
  83.                 } else {    
  84.                     if (position == 11) {      //点击退格键    
  85.                         if (currentIndex - 1 >= -1) {      //判断是否删除完毕————要小心数组越界    
  86.                             tvList[currentIndex--].setText("");    
  87.                         }    
  88.                     }    
  89.                 }    
  90.             }    
  91.         });    
  92.     }    
  93.     
  94.     //设置监听方法,在第6位输入完成后触发    
  95.     public void setOnFinishInput(final OnPasswordInputFinish pass) {    
  96.         tvList[5].addTextChangedListener(new TextWatcher() {    
  97.             @Override    
  98.             public void beforeTextChanged(CharSequence s, int start, int count, int after) {    
  99.     
  100.             }    
  101.     
  102.             @Override    
  103.             public void onTextChanged(CharSequence s, int start, int before, int count) {    
  104.     
  105.             }    
  106.     
  107.             @Override    
  108.             public void afterTextChanged(Editable s) {    
  109.                 if (s.toString().length() == 1) {    
  110.                     strPassword = "";     //每次触发都要先将strPassword置空,再重新获取,避免由于输入删除再输入造成混乱    
  111.                     for (int i = 0; i < 6; i++) {    
  112.                         strPassword += tvList[i].getText().toString().trim();    
  113.                     }    
  114.                     pass.inputFinish();    //接口中要实现的方法,完成密码输入完成后的响应逻辑    
  115.                 }    
  116.             }    
  117.         });    
  118.     }    
  119.     
  120.     /* 获取输入的密码 */    
  121.     public String getStrPassword() {    
  122.         return strPassword;    
  123.     }    
  124.     
  125.     /* 暴露取消支付的按钮,可以灵活改变响应 */    
  126.     public ImageView getCancelImageView() {    
  127.         return imgCancel;    
  128.     }    
  129.     
  130.     /* 暴露忘记密码的按钮,可以灵活改变响应 */    
  131.     public TextView getForgetTextView() {    
  132.         return tvForget;    
  133.     }    
  134.     
  135.     //GrideView的适配器    
  136.     BaseAdapter adapter = new BaseAdapter() {    
  137.         @Override    
  138.         public int getCount() {    
  139.             return valueList.size();    
  140.         }    
  141.     
  142.         @Override    
  143.         public Object getItem(int position) {    
  144.             return valueList.get(position);    
  145.         }    
  146.     
  147.         @Override    
  148.         public long getItemId(int position) {    
  149.             return position;    
  150.         }    
  151.     
  152.         @Override    
  153.         public View getView(int position, View convertView, ViewGroup parent) {    
  154.             ViewHolder viewHolder;    
  155.             if (convertView == null) {    
  156.                 convertView = View.inflate(context, R.layout.item_gride, null);    
  157.                 viewHolder = new ViewHolder();    
  158.                 viewHolder.btnKey = (TextView) convertView.findViewById(R.id.btn_keys);    
  159.                 convertView.setTag(viewHolder);    
  160.             } else {    
  161.                 viewHolder = (ViewHolder) convertView.getTag();    
  162.             }    
  163.             viewHolder.btnKey.setText(valueList.get(position).get("name"));    
  164.             if(position == 9){    
  165.                 viewHolder.btnKey.setBackgroundResource(R.drawable.selector_key_del);    
  166.                 viewHolder.btnKey.setEnabled(false);    
  167.             }    
  168.             if(position == 11){    
  169.                 viewHolder.btnKey.setBackgroundResource(R.drawable.selector_key_del);    
  170.             }    
  171.     
  172.             return convertView;    
  173.         }    
  174.     };    
  175.     
  176.     /**  
  177.      * 存放控件  
  178.      */    
  179.     public final class ViewHolder {    
  180.         public TextView btnKey;    
  181.     }    
  182. }    

        自认为代码注释还是可以的。就是在实现过程中要 注意数组的越界问题 ,在输入逻辑响应中要注意逻辑处理,也就是grideView的OnItemClickListener事件处理。其中用到自定义的接口OnPasswordInputFinish来实现输入完成的事件回掉:
[java]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. /**  
  2.  * Belong to the Project —— MyPayUI   
  3.  * Created by WangJ on 2015/11/25 17:15.  
  4.  *   
  5.  * 自定义接口,用于给密码输入完成添加回掉事件  
  6.  */    
  7. public interface OnPasswordInputFinish {    
  8.     void inputFinish();    
  9. }    
还有就是Adapter中用到的每个按钮Item的布局item_gride.xml:
[html]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. <?xml version="1.0" encoding="utf-8"?>    
  2. <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"    
  3.     android:layout_width="match_parent"    
  4.     android:layout_height="match_parent">    
  5.     
  6.     <!-- 模拟键盘按钮,当然你可以用Button,但要注意Button和GrideView的点击响应问题 -->    
  7.     <TextView    
  8.         android:id="@+id/btn_keys"    
  9.         android:layout_width="match_parent"    
  10.         android:layout_height="match_parent"    
  11.         android:padding="10dp"    
  12.         android:gravity="center"    
  13.         android:textSize="25sp"    
  14.         android:background="@drawable/selector_gride"/>    
  15. </LinearLayout>   

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

        我们在MianActivity中用用一下我们定义好的控件:

[java]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. public class MainActivity extends Activity {    
  2.     
  3.     @Override    
  4.     protected void onCreate(Bundle savedInstanceState) {    
  5.         super.onCreate(savedInstanceState);    
  6.             
  7.         /************* 第一种用法————开始 ***************/    
  8.         setContentView(R.layout.activity_main);    
  9.     
  10.         final PasswordView pwdView = (PasswordView) findViewById(R.id.pwd_view);    
  11.             
  12.         //添加密码输入完成的响应    
  13.         pwdView.setOnFinishInput(new OnPasswordInputFinish() {    
  14.             @Override    
  15.             public void inputFinish() {    
  16.                 //输入完成后我们简单显示一下输入的密码    
  17.                 //也就是说——>实现你的交易逻辑什么的在这里写    
  18.                 Toast.makeText(MainActivity.this, pwdView.getStrPassword(), Toast.LENGTH_SHORT).show();    
  19.             }    
  20.         });    
  21.             
  22.         /**  
  23.          *  可以用自定义控件中暴露出来的cancelImageView方法,重新提供相应  
  24.          *  如果写了,会覆盖我们在自定义控件中提供的响应  
  25.          *  可以看到这里toast显示 "Biu Biu Biu"而不是"Cancel"*/    
  26.         pwdView.getCancelImageView().setOnClickListener(new View.OnClickListener() {    
  27.             @Override    
  28.             public void onClick(View v) {    
  29.                 Toast.makeText(MainActivity.this"Biu Biu Biu", Toast.LENGTH_SHORT).show();    
  30.             }    
  31.         });    
  32.         /************ 第一种用法————结束 ******************/    
  33.     
  34.             
  35.         /************* 第二种用法————开始 *****************/    
  36. //        final PasswordView pwdView = new PasswordView(this);    
  37. //        setContentView(pwdView);    
  38. //        pwdView.setOnFinishInput(new OnPasswordInputFinish() {    
  39. //            @Override    
  40. //            public void inputFinish() {    
  41. //                Toast.makeText(MainActivity.this, pwdView.getStrPassword(), Toast.LENGTH_SHORT).show();    
  42. //            }    
  43. //        });    
  44.         /************** 第二种用法————结束 ****************/    
  45.     }    
  46. }    

  在第一种方法中我们用到的布局文件:
[html]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. <?xml version="1.0" encoding="utf-8"?>    
  2. <RelativeLayout    
  3.     android:id="@+id/xxx"    
  4.     xmlns:android="http://schemas.android.com/apk/res/android"    
  5.     android:layout_width="match_parent"    
  6.     android:layout_height="match_parent"    
  7.     android:background="#624762">    
  8.     
  9.     <com.wangj.mypayview.PasswordView    
  10.         android:id="@+id/pwd_view"    
  11.         android:layout_width="match_parent"    
  12.         android:layout_height="wrap_content"    
  13.         android:layout_alignParentBottom="true"/>    
  14. </RelativeLayout>   



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值