Android實(shí)現(xiàn)支付寶6位密碼輸入界面
我們先來(lái)照?qǐng)D分析一下:
(1)限制輸入6位,每一位都有自己的框格,每個(gè)格顯示一位;
(2)有回退/取消支付按鈕;
(3)有忘記密碼鏈接;
(4)自定義的只能輸入數(shù)字的鍵盤(pán)輸入?yún)^(qū);
(5)在6位輸完后自動(dòng)進(jìn)行密碼校驗(yàn)和支付交易。如上圖左邊是iOS支付寶支付密碼輸入控件,右邊是我模仿實(shí)現(xiàn)的效果。
首先,我們需要一個(gè)頁(yè)面來(lái)完成以上的靜態(tài)布局,.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位密碼框布局,需要一個(gè)圓角邊框的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設(shè)置隱藏密碼明文
textSize設(shè)置大一點(diǎn),否則“點(diǎn)”太小了,不美觀 -->
<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>
<!-- 輸入鍵盤(pán) -->
<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>
需要數(shù)字按鈕的背景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>
下面來(lái)完成我們的自定義控件PasswordView.Java:
public class PasswordView extends RelativeLayout implements View.OnClickListener {
Context context;
private String strPassword; //輸入的密碼
private TextView[] tvList; //用數(shù)組保存6個(gè)TextView,為什么用數(shù)組?
//因?yàn)榫?個(gè)輸入框不會(huì)變了,用數(shù)組內(nèi)存申請(qǐng)固定空間,比List省空間(自己認(rèn)為)
private GridView gridView; //用GrideView布局鍵盤(pán),其實(shí)并不是真正的鍵盤(pán),只是模擬鍵盤(pán)的功能
private ArrayList<Map<String, String>> valueList; //有人可能有疑問(wèn),為何這里不用數(shù)組了?
//因?yàn)橐肁dapter中適配,用數(shù)組不能往adapter中填充
private ImageView imgCancel;
private TextView tvForget;
private int currentIndex = -1; //用于記錄當(dāng)前輸入密碼格位置
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() {
/* 初始化按鈕上應(yīng)該顯示的數(shù)字 */
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) { //點(diǎn)擊0~9按鈕
if (currentIndex >= -1 && currentIndex < 5) { //判斷輸入位置————要小心數(shù)組越界
tvList[++currentIndex].setText(valueList.get(position).get("name"));
}
} else {
if (position == 11) { //點(diǎn)擊退格鍵
if (currentIndex - 1 >= -1) { //判斷是否刪除完畢————要小心數(shù)組越界
tvList[currentIndex--].setText("");
}
}
}
}
});
}
//設(shè)置監(jiān)聽(tīng)方法,在第6位輸入完成后觸發(fā)
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 = ""; //每次觸發(fā)都要先將strPassword置空,再重新獲取,避免由于輸入刪除再輸入造成混亂
for (int i = 0; i < 6; i++) {
strPassword += tvList[i].getText().toString().trim();
}
pass.inputFinish(); //接口中要實(shí)現(xiàn)的方法,完成密碼輸入完成后的響應(yīng)邏輯
}
}
});
}
/* 獲取輸入的密碼 */
public String getStrPassword() {
return strPassword;
}
/* 暴露取消支付的按鈕,可以靈活改變響應(yīng) */
public ImageView getCancelImageView() {
return imgCancel;
}
/* 暴露忘記密碼的按鈕,可以靈活改變響應(yīng) */
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;
}
}
自認(rèn)為代碼注釋還是可以的。就是在實(shí)現(xiàn)過(guò)程中要注意數(shù)組的越界問(wèn)題,在輸入邏輯響應(yīng)中要注意邏輯處理,也就是grideView的OnItemClickListener事件處理。其中用到自定義的接口OnPasswordInputFinish來(lái)實(shí)現(xiàn)輸入完成的事件回掉:
/**
* Belong to the Project —— MyPayUI
* Created by WangJ on 2015/11/25 17:15.
*
* 自定義接口,用于給密碼輸入完成添加回掉事件
*/
public interface OnPasswordInputFinish {
void inputFinish();
}
還有就是Adapter中用到的每個(gè)按鈕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"> <!-- 模擬鍵盤(pán)按鈕,當(dāng)然你可以用Button,但要注意Button和GrideView的點(diǎn)擊響應(yīng)問(wèn)題 --> <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位支付密碼輸入控件就完成了,下邊我們?cè)贏ctivity中用一下,檢驗(yàn)一下效果:
我們?cè)贛ianActivity中用用一下我們定義好的控件:
public class MainActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
/************* 第一種用法————開(kāi)始 ***************/
setContentView(R.layout.activity_main);
final PasswordView pwdView = (PasswordView) findViewById(R.id.pwd_view);
//添加密碼輸入完成的響應(yīng)
pwdView.setOnFinishInput(new OnPasswordInputFinish() {
@Override
public void inputFinish() {
//輸入完成后我們簡(jiǎn)單顯示一下輸入的密碼
//也就是說(shuō)——>實(shí)現(xiàn)你的交易邏輯什么的在這里寫(xiě)
Toast.makeText(MainActivity.this, pwdView.getStrPassword(), Toast.LENGTH_SHORT).show();
}
});
/**
* 可以用自定義控件中暴露出來(lái)的cancelImageView方法,重新提供相應(yīng)
* 如果寫(xiě)了,會(huì)覆蓋我們?cè)谧远x控件中提供的響應(yīng)
* 可以看到這里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();
}
});
/************ 第一種用法————結(jié)束 ******************/
/************* 第二種用法————開(kāi)始 *****************/
// 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();
// }
// });
/************** 第二種用法————結(jié)束 ****************/
}
}
在第一種方法中我們用到的布局文件:
<?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>
更多內(nèi)容請(qǐng)參考專題:Android密碼使用教程
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
Android onClick方法與setOnClickListener方法對(duì)比
這篇文章主要介紹了Android onClick方法與setOnClickListener方法對(duì)比的相關(guān)資料,這兩個(gè)方法都是點(diǎn)擊事件處理函數(shù)的方法,它們之間到底有什么區(qū)別呢,下面就給大家說(shuō)下,需要的朋友可以參考下2016-12-12
掃二維碼下載apk并統(tǒng)計(jì)被掃描次數(shù)
本文主要對(duì)實(shí)現(xiàn)用戶掃描一個(gè)二維碼就能下載APP,并統(tǒng)計(jì)被掃描次數(shù)的方法進(jìn)行詳細(xì)介紹,具有一定的參考作用,下面跟著小編一起來(lái)看下吧2017-01-01
Android編程實(shí)現(xiàn)圖片拍照剪裁的方法
這篇文章主要介紹了Android編程實(shí)現(xiàn)圖片拍照剪裁的方法,涉及Android調(diào)用裁剪工具操作圖片的相關(guān)技巧,具有一定參考借鑒價(jià)值,需要的朋友可以參考下2015-12-12
Android開(kāi)發(fā)中TextView 實(shí)現(xiàn)右上角跟隨文本動(dòng)態(tài)追加圓形紅點(diǎn)
這篇文章主要介紹了android textview 右上角跟隨文本動(dòng)態(tài)追加圓形紅點(diǎn)的實(shí)例代碼,非常不錯(cuò),具有參考借鑒價(jià)值,需要的朋友可以參考下2016-11-11
Android 簡(jiǎn)單的圖片查看器源碼實(shí)現(xiàn)
本篇文章主要介紹了Android 簡(jiǎn)單的圖片查看器源碼實(shí)現(xiàn),小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2017-09-09
Android實(shí)現(xiàn)簡(jiǎn)單手機(jī)震動(dòng)效果
這篇文章主要為大家詳細(xì)介紹了Android實(shí)現(xiàn)手機(jī)震動(dòng)效果,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-09-09
Android ScrollView只能添加一個(gè)子控件問(wèn)題解決方法
這篇文章主要介紹了Android ScrollView只能添加一個(gè)子控件問(wèn)題解決方法,涉及Android界面布局的相關(guān)技巧,需要的朋友可以參考下2016-02-02
Android判斷是否有拍照權(quán)限的實(shí)例代碼
android在開(kāi)發(fā)中有時(shí)候要判斷應(yīng)用中是否有某項(xiàng)權(quán)限,下面通過(guò)本文給大家分享Android判斷是否有拍照權(quán)限的實(shí)例代碼,需要的的朋友參考下吧2017-07-07

