首页 > 系统 > Android > 正文

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

2019-10-23 19:55:38
字体:
来源:转载
供稿:网友

 我们先来照图分析一下:
(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> 

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


注:相关教程知识阅读请移步到Android开发频道。
发表评论 共有条评论
用户名: 密码:
验证码: 匿名发表