首页 > 系统 > Android > 正文

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

2019-12-12 04:10:23
字体:
来源:转载
供稿:网友

 我们先来照图分析一下:
(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密码使用教程

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

发表评论 共有条评论
用户名: 密码:
验证码: 匿名发表