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

来源:互联网 发布:完美世界 知乎 编辑:程序博客网 时间:2024/05/06 16:56

 我们先来照图分析一下:

(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> 



0 0
原创粉丝点击