├── settings.gradle
├── app
├── src
│ ├── main
│ │ ├── res
│ │ │ ├── values
│ │ │ │ ├── strings.xml
│ │ │ │ ├── colors.xml
│ │ │ │ ├── dimen.xml
│ │ │ │ └── styles.xml
│ │ │ ├── mipmap-hdpi
│ │ │ │ ├── ic_launcher.png
│ │ │ │ └── ic_launcher_round.png
│ │ │ ├── mipmap-mdpi
│ │ │ │ ├── ic_launcher.png
│ │ │ │ └── ic_launcher_round.png
│ │ │ ├── mipmap-xhdpi
│ │ │ │ ├── icon_del.png
│ │ │ │ ├── ic_input_del.png
│ │ │ │ ├── ic_launcher.png
│ │ │ │ ├── ic_arrow_down.png
│ │ │ │ └── ic_launcher_round.png
│ │ │ ├── mipmap-xxhdpi
│ │ │ │ ├── ic_launcher.png
│ │ │ │ └── ic_launcher_round.png
│ │ │ ├── mipmap-xxxhdpi
│ │ │ │ ├── ic_launcher.png
│ │ │ │ └── ic_launcher_round.png
│ │ │ ├── drawable
│ │ │ │ ├── shape_dialog.xml
│ │ │ │ └── selector_item_pressed.xml
│ │ │ ├── anim
│ │ │ │ ├── push_bottom_out.xml
│ │ │ │ └── push_bottom_in.xml
│ │ │ └── layout
│ │ │ │ ├── activity_main.xml
│ │ │ │ ├── fragment_pay.xml
│ │ │ │ └── view_password_input.xml
│ │ ├── AndroidManifest.xml
│ │ └── java
│ │ │ └── cn
│ │ │ └── xdeveloper
│ │ │ └── payui
│ │ │ ├── MainActivity.java
│ │ │ ├── PwdInputMethodView.java
│ │ │ ├── PayFragment.java
│ │ │ └── PayPwdView.java
│ ├── test
│ │ └── java
│ │ │ └── cn
│ │ │ └── xdeveloper
│ │ │ └── payui
│ │ │ └── ExampleUnitTest.java
│ └── androidTest
│ │ └── java
│ │ └── cn
│ │ └── xdeveloper
│ │ └── payui
│ │ └── ExampleInstrumentedTest.java
├── proguard-rules.pro
└── build.gradle
└── README.md
/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':app'
2 |
--------------------------------------------------------------------------------
/app/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | PayUI
3 |
4 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/a5533348/PayUI/HEAD/app/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/a5533348/PayUI/HEAD/app/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/icon_del.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/a5533348/PayUI/HEAD/app/src/main/res/mipmap-xhdpi/icon_del.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_input_del.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/a5533348/PayUI/HEAD/app/src/main/res/mipmap-xhdpi/ic_input_del.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/a5533348/PayUI/HEAD/app/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/a5533348/PayUI/HEAD/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_arrow_down.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/a5533348/PayUI/HEAD/app/src/main/res/mipmap-xhdpi/ic_arrow_down.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/a5533348/PayUI/HEAD/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/a5533348/PayUI/HEAD/app/src/main/res/mipmap-hdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/a5533348/PayUI/HEAD/app/src/main/res/mipmap-mdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/a5533348/PayUI/HEAD/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/a5533348/PayUI/HEAD/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/a5533348/PayUI/HEAD/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable/shape_dialog.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/app/src/main/res/anim/push_bottom_out.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
9 |
--------------------------------------------------------------------------------
/app/src/main/res/anim/push_bottom_in.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
10 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/selector_item_pressed.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_main.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/app/src/test/java/cn/xdeveloper/payui/ExampleUnitTest.java:
--------------------------------------------------------------------------------
1 | package cn.xdeveloper.payui;
2 |
3 | import org.junit.Test;
4 |
5 | import static org.junit.Assert.*;
6 |
7 | /**
8 | * Example local unit test, which will execute on the development machine (host).
9 | *
10 | * @see Testing documentation
11 | */
12 | public class ExampleUnitTest {
13 | @Test
14 | public void addition_isCorrect() throws Exception {
15 | assertEquals(4, 2 + 2);
16 | }
17 | }
--------------------------------------------------------------------------------
/app/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #3F51B5
4 | #303F9F
5 | #FF4081
6 |
7 | #919191
8 | #EBEBEB
9 |
10 | #f0eff5
11 | #ebebeb
12 | #FFFFFF
13 | #000000
14 | #212121
15 |
16 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # PayUI
2 |
3 |
4 | #### 预览
5 | 
6 |
7 | #### 使用
8 | 这个弹出层是一个DialogFragment,逻辑都封装在其内部,使用起来很简单:
9 |
10 | ```
11 | Bundle bundle = new Bundle();
12 | bundle.putString(PayFragment.EXTRA_CONTENT, "提现:¥ " + 100.00);
13 |
14 | PayFragment fragment = new PayFragment();
15 | fragment.setArguments(bundle);
16 | fragment.setPaySuccessCallBack(MainActivity.this);
17 | fragment.show(getSupportFragmentManager(), "Pay");
18 | ```
19 |
20 | 通过InputCallBack接口回调输入的支付密码,可以在回调方法中请求判断支付密码是不是正确的,也可以在PayFragment内部自己修改判断,没有用到什么高深的技术,大家看代码自然就明白了。
21 |
22 | 博客地址: [Blog](http://xdeveloper.cn/pay-ui/)
23 |
--------------------------------------------------------------------------------
/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/app/src/main/res/values/dimen.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | 20sp
6 | 18sp
7 | 15sp
8 | 12sp
9 | 10sp
10 |
11 |
12 | 20dp
13 | 15dp
14 | 10dp
15 | 4dp
16 | 0.5dp
17 | 10dp
18 | 10dp
19 |
20 |
21 | 43dp
22 | 2px
23 |
--------------------------------------------------------------------------------
/app/src/androidTest/java/cn/xdeveloper/payui/ExampleInstrumentedTest.java:
--------------------------------------------------------------------------------
1 | package cn.xdeveloper.payui;
2 |
3 | import android.content.Context;
4 | import android.support.test.InstrumentationRegistry;
5 | import android.support.test.runner.AndroidJUnit4;
6 |
7 | import org.junit.Test;
8 | import org.junit.runner.RunWith;
9 |
10 | import static org.junit.Assert.*;
11 |
12 | /**
13 | * Instrumentation test, which will execute on an Android device.
14 | *
15 | * @see Testing documentation
16 | */
17 | @RunWith(AndroidJUnit4.class)
18 | public class ExampleInstrumentedTest {
19 | @Test
20 | public void useAppContext() throws Exception {
21 | // Context of the app under test.
22 | Context appContext = InstrumentationRegistry.getTargetContext();
23 |
24 | assertEquals("cn.xdeveloper.payui", appContext.getPackageName());
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/app/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # By default, the flags in this file are appended to flags specified
3 | # in D:\AndroidStudio\sdk\android-sdk/tools/proguard/proguard-android.txt
4 | # You can edit the include path and order by changing the proguardFiles
5 | # directive in build.gradle.
6 | #
7 | # For more details, see
8 | # http://developer.android.com/guide/developing/tools/proguard.html
9 |
10 | # Add any project specific keep options here:
11 |
12 | # If your project uses WebView with JS, uncomment the following
13 | # and specify the fully qualified class name to the JavaScript interface
14 | # class:
15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
16 | # public *;
17 | #}
18 |
19 | # Uncomment this to preserve the line number information for
20 | # debugging stack traces.
21 | #-keepattributes SourceFile,LineNumberTable
22 |
23 | # If you keep the line number information, uncomment this to
24 | # hide the original source file name.
25 | #-renamesourcefileattribute SourceFile
26 |
--------------------------------------------------------------------------------
/app/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.application'
2 |
3 | android {
4 | compileSdkVersion 25
5 | buildToolsVersion "25.0.2"
6 | defaultConfig {
7 | applicationId "cn.xdeveloper.payui"
8 | minSdkVersion 16
9 | targetSdkVersion 25
10 | versionCode 1
11 | versionName "1.0"
12 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
13 | }
14 | buildTypes {
15 | release {
16 | minifyEnabled false
17 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
18 | }
19 | }
20 | }
21 |
22 | dependencies {
23 | compile fileTree(dir: 'libs', include: ['*.jar'])
24 | androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', {
25 | exclude group: 'com.android.support', module: 'support-annotations'
26 | })
27 | compile 'com.android.support:appcompat-v7:25.1.1'
28 | compile 'com.android.support.constraint:constraint-layout:1.0.2'
29 | testCompile 'junit:junit:4.12'
30 | }
31 |
--------------------------------------------------------------------------------
/app/src/main/java/cn/xdeveloper/payui/MainActivity.java:
--------------------------------------------------------------------------------
1 | package cn.xdeveloper.payui;
2 |
3 | import android.support.v7.app.AppCompatActivity;
4 | import android.os.Bundle;
5 | import android.view.View;
6 | import android.widget.Toast;
7 |
8 | public class MainActivity extends AppCompatActivity implements PayPwdView.InputCallBack, View.OnClickListener {
9 |
10 | @Override
11 | protected void onCreate(Bundle savedInstanceState) {
12 | super.onCreate(savedInstanceState);
13 | setContentView(R.layout.activity_main);
14 |
15 | findViewById(R.id.btn_pay).setOnClickListener(this);
16 | }
17 |
18 |
19 | @Override
20 | public void onClick(View v) {
21 | switch (v.getId()) {
22 | case R.id.btn_pay:
23 | Bundle bundle = new Bundle();
24 | bundle.putString(PayFragment.EXTRA_CONTENT, "提现:¥ " + 100.00);
25 |
26 | PayFragment fragment = new PayFragment();
27 | fragment.setArguments(bundle);
28 | fragment.setPaySuccessCallBack(MainActivity.this);
29 | fragment.show(getSupportFragmentManager(), "Pay");
30 | break;
31 | }
32 | }
33 |
34 | @Override
35 | public void onInputFinish(String result) {
36 | Toast.makeText(this, result, Toast.LENGTH_SHORT).show();
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/app/src/main/java/cn/xdeveloper/payui/PwdInputMethodView.java:
--------------------------------------------------------------------------------
1 | package cn.xdeveloper.payui;
2 |
3 | import android.content.Context;
4 | import android.support.annotation.Nullable;
5 | import android.util.AttributeSet;
6 | import android.view.LayoutInflater;
7 | import android.view.View;
8 | import android.widget.LinearLayout;
9 |
10 | /**
11 | * 输入键盘
12 | * Created by Administrator on 2017/4/19.
13 | */
14 |
15 | public class PwdInputMethodView extends LinearLayout implements View.OnClickListener {
16 |
17 | private InputReceiver inputReceiver;
18 |
19 | public PwdInputMethodView(Context context, @Nullable AttributeSet attrs) {
20 | super(context, attrs);
21 | LayoutInflater.from(context).inflate(R.layout.view_password_input, this);
22 |
23 | initView();
24 | }
25 |
26 | private void initView() {
27 | findViewById(R.id.btn_1).setOnClickListener(this);
28 | findViewById(R.id.btn_2).setOnClickListener(this);
29 | findViewById(R.id.btn_3).setOnClickListener(this);
30 | findViewById(R.id.btn_4).setOnClickListener(this);
31 | findViewById(R.id.btn_5).setOnClickListener(this);
32 | findViewById(R.id.btn_6).setOnClickListener(this);
33 | findViewById(R.id.btn_7).setOnClickListener(this);
34 | findViewById(R.id.btn_8).setOnClickListener(this);
35 | findViewById(R.id.btn_9).setOnClickListener(this);
36 | findViewById(R.id.btn_0).setOnClickListener(this);
37 | findViewById(R.id.btn_del).setOnClickListener(this);
38 |
39 | findViewById(R.id.layout_hide).setOnClickListener(new OnClickListener() {
40 | @Override
41 | public void onClick(View v) {
42 | setVisibility(GONE);
43 | }
44 | });
45 | }
46 |
47 | @Override
48 | public void onClick(View v) {
49 | String num = (String) v.getTag();
50 | this.inputReceiver.receive(num);
51 | }
52 |
53 |
54 | /**
55 | * 设置接收器
56 | * @param receiver
57 | */
58 | public void setInputReceiver(InputReceiver receiver){
59 | this.inputReceiver = receiver;
60 | }
61 |
62 | /**
63 | * 输入接收器
64 | */
65 | public interface InputReceiver{
66 |
67 | void receive(String num);
68 | }
69 | }
70 |
71 |
72 |
73 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/fragment_pay.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
9 |
18 |
19 |
22 |
23 |
24 |
31 |
32 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
53 |
54 |
61 |
62 |
63 |
64 |
69 |
--------------------------------------------------------------------------------
/app/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
10 |
11 |
12 |
18 |
19 |
23 |
24 |
25 |
31 |
32 |
33 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
52 |
53 |
54 |
55 |
64 |
65 |
66 |
--------------------------------------------------------------------------------
/app/src/main/java/cn/xdeveloper/payui/PayFragment.java:
--------------------------------------------------------------------------------
1 | package cn.xdeveloper.payui;
2 |
3 | import android.app.Dialog;
4 | import android.graphics.Color;
5 | import android.graphics.drawable.ColorDrawable;
6 | import android.os.Bundle;
7 | import android.support.annotation.NonNull;
8 | import android.support.v4.app.DialogFragment;
9 | import android.view.Gravity;
10 | import android.view.View;
11 | import android.view.Window;
12 | import android.view.WindowManager;
13 | import android.widget.TextView;
14 |
15 | /**
16 | * Created by Laiyimin on 2017/4/20.
17 | */
18 |
19 | public class PayFragment extends DialogFragment implements View.OnClickListener {
20 |
21 | public static final String EXTRA_CONTENT = "extra_content"; //提示框内容
22 |
23 | private PayPwdView psw_input;
24 | private PayPwdView.InputCallBack inputCallBack;
25 |
26 | @NonNull
27 | @Override
28 | public Dialog onCreateDialog(Bundle savedInstanceState) {
29 | // 使用不带Theme的构造器, 获得的dialog边框距离屏幕仍有几毫米的缝隙。
30 | Dialog dialog = new Dialog(getActivity(), R.style.BottomDialog);
31 | dialog.requestWindowFeature(Window.FEATURE_NO_TITLE); // 设置Content前设定
32 | dialog.setContentView(R.layout.fragment_pay);
33 | dialog.setCanceledOnTouchOutside(false); // 外部点击取消
34 |
35 | // 设置宽度为屏宽, 靠近屏幕底部。
36 | final Window window = dialog.getWindow();
37 | window.setWindowAnimations(R.style.AnimBottom);
38 | window.setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT));
39 | final WindowManager.LayoutParams lp = window.getAttributes();
40 | lp.width = WindowManager.LayoutParams.MATCH_PARENT; // 宽度持平
41 | lp.gravity = Gravity.TOP;
42 | window.setAttributes(lp);
43 |
44 | initView(dialog);
45 | return dialog;
46 | }
47 |
48 | private void initView(Dialog dialog) {
49 | Bundle bundle = getArguments();
50 | if (bundle != null) {
51 | TextView tv_content = (TextView) dialog.findViewById(R.id.tv_content);
52 | tv_content.setText(bundle.getString(EXTRA_CONTENT));
53 | }
54 |
55 | psw_input = (PayPwdView) dialog.findViewById(R.id.payPwdView);
56 | PwdInputMethodView inputMethodView = (PwdInputMethodView) dialog.findViewById(R.id.inputMethodView);
57 | psw_input.setInputMethodView(inputMethodView);
58 | psw_input.setInputCallBack(inputCallBack);
59 |
60 | dialog.findViewById(R.id.iv_close).setOnClickListener(this);
61 | }
62 |
63 |
64 | @Override
65 | public void onClick(View v) {
66 | switch (v.getId()) {
67 | case R.id.iv_close:
68 | dismiss();
69 | break;
70 |
71 | }
72 | }
73 |
74 | /**
75 | * 设置输入回调
76 | *
77 | * @param inputCallBack
78 | */
79 | public void setPaySuccessCallBack(PayPwdView.InputCallBack inputCallBack) {
80 | this.inputCallBack = inputCallBack;
81 | }
82 |
83 | }
84 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/view_password_input.xml:
--------------------------------------------------------------------------------
1 |
2 |
9 |
10 |
11 |
12 |
17 |
18 |
23 |
24 |
25 |
26 |
27 |
28 |
36 |
37 |
44 |
45 |
51 |
52 |
53 |
54 |
55 |
61 |
62 |
68 |
69 |
74 |
75 |
76 |
77 |
78 |
84 |
85 |
91 |
92 |
97 |
98 |
99 |
100 |
101 |
105 |
106 |
112 |
113 |
114 |
122 |
123 |
124 |
125 |
--------------------------------------------------------------------------------
/app/src/main/java/cn/xdeveloper/payui/PayPwdView.java:
--------------------------------------------------------------------------------
1 | package cn.xdeveloper.payui;
2 |
3 | import android.content.Context;
4 | import android.content.res.TypedArray;
5 | import android.graphics.Canvas;
6 | import android.graphics.Color;
7 | import android.graphics.Paint;
8 | import android.graphics.Rect;
9 | import android.graphics.RectF;
10 | import android.text.InputType;
11 | import android.util.AttributeSet;
12 | import android.view.KeyEvent;
13 | import android.view.MotionEvent;
14 | import android.view.View;
15 | import android.view.inputmethod.BaseInputConnection;
16 | import android.view.inputmethod.EditorInfo;
17 | import android.view.inputmethod.InputConnection;
18 |
19 | import java.util.ArrayList;
20 |
21 | /**
22 | * 引用网上的一位朋友的类,具体出处找不到了
23 | *
24 | */
25 | public class PayPwdView extends View {
26 |
27 | private ArrayList result;//输入结果保存
28 | private int count;//密码位数
29 | private int size;//默认每一格的大小
30 | private Paint mBorderPaint;//边界画笔
31 | private Paint mDotPaint;//掩盖点的画笔
32 | private int mBorderColor;//边界颜色
33 | private int mDotColor;//掩盖点的颜色
34 | private RectF mRoundRect;//外面的圆角矩形
35 | private int mRoundRadius;//圆角矩形的圆角程度
36 |
37 | public PayPwdView(Context context) {
38 | super(context);
39 | init(null);
40 | }
41 |
42 | private InputCallBack inputCallBack;//输入完成的回调
43 | private PwdInputMethodView inputMethodView; //输入键盘
44 |
45 |
46 | public interface InputCallBack {
47 | void onInputFinish(String result);
48 | }
49 |
50 | public PayPwdView(Context context, AttributeSet attrs) {
51 | super(context, attrs);
52 | init(attrs);
53 | }
54 |
55 | public PayPwdView(Context context, AttributeSet attrs, int defStyleAttr) {
56 | super(context, attrs, defStyleAttr);
57 | init(attrs);
58 | }
59 |
60 | /**
61 | * 初始化相关参数
62 | */
63 | void init(AttributeSet attrs) {
64 | final float dp = getResources().getDisplayMetrics().density;
65 | this.setFocusable(true);
66 | this.setFocusableInTouchMode(true);
67 | result = new ArrayList<>();
68 | if (attrs != null) {
69 | TypedArray ta = getContext().obtainStyledAttributes(attrs, R.styleable.PayPwdView);
70 | mBorderColor = ta.getColor(R.styleable.PayPwdView_border_color, Color.LTGRAY);
71 | mDotColor = ta.getColor(R.styleable.PayPwdView_dot_color, Color.BLACK);
72 | count = ta.getInt(R.styleable.PayPwdView_count, 6);
73 | ta.recycle();
74 | } else {
75 | mBorderColor = Color.LTGRAY;
76 | mDotColor = Color.GRAY;
77 | count = 6;//默认6位密码
78 | }
79 | size = (int) (dp * 30);//默认30dp一格
80 | //color
81 | mBorderPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
82 | mBorderPaint.setStrokeWidth(3);
83 | mBorderPaint.setStyle(Paint.Style.STROKE);
84 | mBorderPaint.setColor(mBorderColor);
85 |
86 | mDotPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
87 | mDotPaint.setStrokeWidth(3);
88 | mDotPaint.setStyle(Paint.Style.FILL);
89 | mDotPaint.setColor(mDotColor);
90 | mRoundRect = new RectF();
91 | mRoundRadius = (int) (5 * dp);
92 | }
93 |
94 | @Override
95 | protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
96 | int w = measureWidth(widthMeasureSpec);
97 | int h = measureHeight(heightMeasureSpec);
98 | int wsize = MeasureSpec.getSize(widthMeasureSpec);
99 | int hsize = MeasureSpec.getSize(heightMeasureSpec);
100 | //宽度没指定,但高度指定
101 | if (w == -1) {
102 | if (h != -1) {
103 | w = h * count;//宽度=高*数量
104 | size = h;
105 | } else {//两个都不知道,默认宽高
106 | w = size * count;
107 | h = size;
108 | }
109 | } else {//宽度已知
110 | if (h == -1) {//高度不知道
111 | h = w / count;
112 | size = h;
113 | }
114 | }
115 | setMeasuredDimension(Math.min(w, wsize), Math.min(h, hsize));
116 | }
117 |
118 | private int measureWidth(int widthMeasureSpec) {
119 | //宽度
120 | int wmode = MeasureSpec.getMode(widthMeasureSpec);
121 | int wsize = MeasureSpec.getSize(widthMeasureSpec);
122 | if (wmode == MeasureSpec.AT_MOST) {//wrap_content
123 | return -1;
124 | }
125 | return wsize;
126 | }
127 |
128 | private int measureHeight(int heightMeasureSpec) {
129 | //高度
130 | int hmode = MeasureSpec.getMode(heightMeasureSpec);
131 | int hsize = MeasureSpec.getSize(heightMeasureSpec);
132 | if (hmode == MeasureSpec.AT_MOST) {//wrap_content
133 | return -1;
134 | }
135 | return hsize;
136 | }
137 |
138 | @Override
139 | public boolean onTouchEvent(MotionEvent event) {
140 | if (event.getAction() == MotionEvent.ACTION_DOWN) {//点击控件弹出输入键盘
141 | requestFocus();
142 | inputMethodView.setVisibility(VISIBLE);
143 | return true;
144 | }
145 | return true;
146 | }
147 |
148 | @Override
149 | protected void onFocusChanged(boolean gainFocus, int direction, Rect previouslyFocusedRect) {
150 | super.onFocusChanged(gainFocus, direction, previouslyFocusedRect);
151 | if (gainFocus) {
152 | inputMethodView.setVisibility(VISIBLE);
153 | } else {
154 | inputMethodView.setVisibility(GONE);
155 | }
156 | }
157 |
158 | @Override
159 | protected void onDraw(Canvas canvas) {
160 | super.onDraw(canvas);
161 | final int width = getWidth() - 2;
162 | final int height = getHeight() - 2;
163 | //先画个圆角矩形
164 | mRoundRect.set(0, 0, width, height);
165 | canvas.drawRoundRect(mRoundRect, 0, 0, mBorderPaint);
166 | //画分割线
167 | for (int i = 1; i < count; i++) {
168 | final int x = i * size;
169 | canvas.drawLine(x, 0, x, height, mBorderPaint);
170 | }
171 | //画掩盖点,
172 | // 这是前面定义的变量 private ArrayList result;//输入结果保存
173 | int dotRadius = size / 8;//圆圈占格子的三分之一
174 | for (int i = 0; i < result.size(); i++) {
175 | final float x = (float) (size * (i + 0.5));
176 | final float y = size / 2;
177 | canvas.drawCircle(x, y, dotRadius, mDotPaint);
178 | }
179 | }
180 |
181 | @Override
182 | public boolean onCheckIsTextEditor() {
183 | return true;
184 | }
185 |
186 | @Override
187 | public InputConnection onCreateInputConnection(EditorInfo outAttrs) {
188 | outAttrs.inputType = InputType.TYPE_CLASS_NUMBER;//输入类型为数字
189 | outAttrs.imeOptions = EditorInfo.IME_ACTION_DONE;
190 | return new MyInputConnection(this, false);
191 | }
192 |
193 | public void setInputCallBack(InputCallBack inputCallBack) {
194 | this.inputCallBack = inputCallBack;
195 | }
196 |
197 | public void clearResult() {
198 | result.clear();
199 | invalidate();
200 | }
201 |
202 |
203 | private class MyInputConnection extends BaseInputConnection {
204 | public MyInputConnection(View targetView, boolean fullEditor) {
205 | super(targetView, fullEditor);
206 | }
207 |
208 | @Override
209 | public boolean commitText(CharSequence text, int newCursorPosition) {
210 | //这里是接受输入法的文本的,我们只处理数字,所以什么操作都不做
211 | return super.commitText(text, newCursorPosition);
212 | }
213 |
214 | @Override
215 | public boolean deleteSurroundingText(int beforeLength, int afterLength) {
216 | //软键盘的删除键 DEL 无法直接监听,自己发送del事件
217 | if (beforeLength == 1 && afterLength == 0) {
218 | return super.sendKeyEvent(new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_DEL))
219 | && super.sendKeyEvent(new KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_DEL));
220 | }
221 | return super.deleteSurroundingText(beforeLength, afterLength);
222 | }
223 | }
224 |
225 |
226 | /**
227 | * 设置输入键盘view
228 | *
229 | * @param inputMethodView
230 | */
231 | public void setInputMethodView(PwdInputMethodView inputMethodView) {
232 | this.inputMethodView = inputMethodView;
233 | this.inputMethodView.setInputReceiver(new PwdInputMethodView.InputReceiver() {
234 | @Override
235 | public void receive(String num) {
236 | if (num.equals("-1")) {
237 | if (!result.isEmpty()) {
238 | result.remove(result.size() - 1);
239 | invalidate();
240 | }
241 | } else {
242 | if (result.size() < count) {
243 | result.add(num);
244 | invalidate();
245 | ensureFinishInput();
246 | }
247 | }
248 |
249 |
250 | }
251 | });
252 | }
253 |
254 | /**
255 | * 判断是否输入完成,输入完成后调用callback
256 | */
257 | void ensureFinishInput() {
258 | if (result.size() == count && inputCallBack != null) {//输入完成
259 | StringBuffer sb = new StringBuffer();
260 | for (String i : result) {
261 | sb.append(i);
262 | }
263 | inputCallBack.onInputFinish(sb.toString());
264 | }
265 | }
266 |
267 | /**
268 | * 获取输入文字
269 | *
270 | * @return
271 | */
272 | public String getInputText() {
273 | if (result.size() == count) {
274 | StringBuffer sb = new StringBuffer();
275 | for (String i : result) {
276 | sb.append(i);
277 | }
278 | return sb.toString();
279 | }
280 | return null;
281 | }
282 | }
283 |
--------------------------------------------------------------------------------