我们先来照图分析一下:
(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密码使用教程
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持武林网。
新闻热点
疑难解答