├── .gitignore
├── .jitpack.yml
├── LICENSE
├── README.md
├── app
├── .gitignore
├── build.gradle
├── proguard-rules.pro
└── src
│ ├── androidTest
│ └── java
│ │ └── com
│ │ └── simple
│ │ └── vclayout_demo
│ │ └── ExampleInstrumentedTest.java
│ ├── main
│ ├── AndroidManifest.xml
│ ├── java
│ │ └── com
│ │ │ └── simple
│ │ │ └── vclayout_demo
│ │ │ └── MainActivity.java
│ └── res
│ │ ├── drawable-v24
│ │ └── ic_launcher_foreground.xml
│ │ ├── drawable
│ │ ├── ic_launcher_background.xml
│ │ ├── sp_circle.xml
│ │ └── sr_et_bg_accent.xml
│ │ ├── layout
│ │ └── activity_main.xml
│ │ ├── mipmap-anydpi-v26
│ │ ├── ic_launcher.xml
│ │ └── ic_launcher_round.xml
│ │ ├── mipmap-hdpi
│ │ ├── ic_launcher.png
│ │ └── ic_launcher_round.png
│ │ ├── mipmap-mdpi
│ │ ├── ic_launcher.png
│ │ └── ic_launcher_round.png
│ │ ├── mipmap-xhdpi
│ │ ├── ic_launcher.png
│ │ └── ic_launcher_round.png
│ │ ├── mipmap-xxhdpi
│ │ ├── ic_launcher.png
│ │ └── ic_launcher_round.png
│ │ ├── mipmap-xxxhdpi
│ │ ├── ic_launcher.png
│ │ └── ic_launcher_round.png
│ │ └── values
│ │ ├── colors.xml
│ │ ├── strings.xml
│ │ └── styles.xml
│ └── test
│ └── java
│ └── com
│ └── simple
│ └── vclayout_demo
│ └── ExampleUnitTest.java
├── build.gradle
├── gradle.properties
├── gradle
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
├── library
├── .gitignore
├── build.gradle
├── proguard-rules.pro
└── src
│ └── main
│ ├── AndroidManifest.xml
│ ├── java
│ └── com
│ │ └── simple
│ │ └── vclayout
│ │ ├── Utils.java
│ │ ├── VerCodeEditText.java
│ │ └── VerCodeLayout.java
│ └── res
│ ├── drawable
│ ├── sp_cursor.xml
│ ├── sp_focused_one.xml
│ ├── sp_focused_two.xml
│ ├── sp_normal_one.xml
│ ├── sp_normal_two.xml
│ └── sr_one.xml
│ └── values
│ ├── atts.xml
│ ├── colors.xml
│ └── strings.xml
├── settings.gradle
└── statics
└── vercode.gif
/.gitignore:
--------------------------------------------------------------------------------
1 | *.iml
2 | .gradle
3 | /local.properties
4 | /.idea/caches
5 | /.idea/libraries
6 | /.idea/modules.xml
7 | /.idea/workspace.xml
8 | /.idea/navEditor.xml
9 | /.idea/assetWizardSettings.xml
10 | /.idea
11 | .DS_Store
12 | /build
13 | /captures
14 | .externalNativeBuild
15 |
--------------------------------------------------------------------------------
/.jitpack.yml:
--------------------------------------------------------------------------------
1 | jdk:
2 | - openjdk11
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2019 ChenPeng
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # VerCodeLayout
2 |
3 | [](https://jitpack.io/#simplepeng/VerCodeLayout)
4 |
5 | 可能是支持属性最多类似校验验证码的输入控件。
6 |
7 | 
8 |
9 | ## 引入依赖
10 |
11 | ```groovy
12 | maven { url 'https://jitpack.io' }
13 | ```
14 |
15 | ```groovy
16 | implementation 'com.github.simplepeng:VerCodeLayout:v1.0.2'
17 | ```
18 |
19 | ## 基本使用
20 |
21 | 使用`VerCodeEditText`控件
22 |
23 | ```xml
24 |
33 | ```
34 |
35 | ### 可用的属性
36 |
37 | | 属性名 | 作用 |
38 | | -------------------- | --------------------------------------------- |
39 | | vcCount | EditText的个数 |
40 | | vcMaxLength | EditText最大输入长度 |
41 | | vcNormalBackground | 未选中的背景 |
42 | | vcFocusedBackground | 选中的背景 |
43 | | vcWidth | EditText的宽 |
44 | | vcHeight | EditText的高 |
45 | | vcMinWidth | EditText的最小宽度 |
46 | | vcMinHeight | EditText的最小高度 |
47 | | vcMargin | EditText的外边距,当然还有MarginLeft等小属性 |
48 | | vcPadding | EditText的内边距,当然还有PaddingLeft等小属性 |
49 | | vcTextSize | EditText的文本大小 |
50 | | vcTextColor | EditText的文本颜色 |
51 | | vcTextCursorDrawable | EditText的游标Drawable |
52 | | vcGravity | EditText的Gravity |
53 | | vcInputType | EditText的输入类型 |
54 |
55 | ## 高级使用
56 |
57 | 使用`VerCodeLayout`控件,VerCodeEditText继承于VerCodeLayout。VerCodeLayout自带了当一个EditText输入完成选中下一个或删除完成选中上一个的功能,但是EditText必须设置`maxLength`。
58 |
59 | ```xml
60 |
61 |
64 |
65 |
73 |
74 |
82 |
83 |
91 |
92 |
100 |
101 |
109 |
110 | ```
111 |
112 | ## 可用的方法
113 |
114 | | 方法名 | 方法作用 |
115 | | --------------------- | ------------------ |
116 | | setOnCompleteListener | 监听输入完成 |
117 | | clear | 清除所有的输入 |
118 | | getEditTexts | 获取所有的EditText |
119 |
120 | ## 混淆
121 |
122 | ```shell
123 | -keep class com.simple.vclayout.** {*;}
124 | ```
125 |
126 | ## 版本更新
127 |
128 | * v1.0.2:增加`autoFocus`的支持
129 |
130 | * v1.0.1:迁移到`jitpack`,`AndroidX`
131 |
132 | * v1.0.0: 首次上传
--------------------------------------------------------------------------------
/app/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/app/build.gradle:
--------------------------------------------------------------------------------
1 | plugins {
2 | id 'com.android.application'
3 | id 'kotlin-android'
4 | }
5 |
6 | android {
7 | compileSdk 30
8 |
9 | defaultConfig {
10 | applicationId "com.simple.vclayout_demo"
11 | minSdk 19
12 | targetSdk 30
13 | versionCode 1
14 | versionName "1.0"
15 |
16 | viewBinding {
17 | enabled = true
18 | }
19 | }
20 |
21 | buildTypes {
22 | release {
23 | minifyEnabled false
24 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
25 | }
26 | }
27 |
28 | compileOptions {
29 | sourceCompatibility JavaVersion.VERSION_1_8
30 | targetCompatibility JavaVersion.VERSION_1_8
31 | }
32 |
33 | kotlinOptions {
34 | jvmTarget = '1.8'
35 | }
36 | }
37 |
38 | dependencies {
39 | implementation fileTree(include: ['*.jar'], dir: 'libs')
40 | implementation 'androidx.appcompat:appcompat:1.0.0'
41 | implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
42 | testImplementation 'junit:junit:4.12'
43 | androidTestImplementation 'androidx.test.ext:junit:1.1.1'
44 | androidTestImplementation 'androidx.test.espresso:espresso-core:3.1.0'
45 |
46 | //
47 | // implementation project(':library')
48 | implementation 'com.github.simplepeng:VerCodeLayout:v1.0.2'
49 | }
50 |
--------------------------------------------------------------------------------
/app/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # You can control the set of applied configuration files using the
3 | # proguardFiles setting in build.gradle.
4 | #
5 | # For more details, see
6 | # http://developer.android.com/guide/developing/tools/proguard.html
7 |
8 | # If your project uses WebView with JS, uncomment the following
9 | # and specify the fully qualified class name to the JavaScript interface
10 | # class:
11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
12 | # public *;
13 | #}
14 |
15 | # Uncomment this to preserve the line number information for
16 | # debugging stack traces.
17 | #-keepattributes SourceFile,LineNumberTable
18 |
19 | # If you keep the line number information, uncomment this to
20 | # hide the original source file name.
21 | #-renamesourcefileattribute SourceFile
22 |
23 | -keep class com.simple.vclayout.** {*;}
24 |
--------------------------------------------------------------------------------
/app/src/androidTest/java/com/simple/vclayout_demo/ExampleInstrumentedTest.java:
--------------------------------------------------------------------------------
1 | package com.simple.vclayout_demo;
2 |
3 | import android.content.Context;
4 | import androidx.test.platform.app.InstrumentationRegistry;
5 | import androidx.test.ext.junit.runners.AndroidJUnit4;
6 |
7 | import org.junit.Test;
8 | import org.junit.runner.RunWith;
9 |
10 | import static org.junit.Assert.*;
11 |
12 | /**
13 | * Instrumented 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() {
21 | // Context of the app under test.
22 | Context appContext = InstrumentationRegistry.getTargetContext();
23 |
24 | assertEquals("com.simple.vclayout", appContext.getPackageName());
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/app/src/main/java/com/simple/vclayout_demo/MainActivity.java:
--------------------------------------------------------------------------------
1 | package com.simple.vclayout_demo;
2 |
3 | import androidx.appcompat.app.AppCompatActivity;
4 | import android.os.Bundle;
5 | import android.text.Editable;
6 | import android.view.View;
7 | import android.widget.Toast;
8 |
9 | import com.simple.vclayout.VerCodeEditText;
10 | import com.simple.vclayout.VerCodeLayout;
11 |
12 | public class MainActivity extends AppCompatActivity {
13 |
14 | @Override
15 | protected void onCreate(Bundle savedInstanceState) {
16 | super.onCreate(savedInstanceState);
17 | setContentView(R.layout.activity_main);
18 |
19 | VerCodeEditText vcEt1 = findViewById(R.id.vcEt1);
20 | vcEt1.setOnCompleteListener(new VerCodeLayout.OnCompleteListener() {
21 | @Override
22 | public void onComplete(Editable editable, String code) {
23 | showToast(code);
24 | }
25 | });
26 | }
27 |
28 | public void clear(View view) {
29 | VerCodeEditText vcEt1 = findViewById(R.id.vcEt1);
30 | vcEt1.clear();
31 | }
32 |
33 | private void showToast(CharSequence text) {
34 | Toast.makeText(getApplicationContext(), text, Toast.LENGTH_SHORT).show();
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable-v24/ic_launcher_foreground.xml:
--------------------------------------------------------------------------------
1 |
7 |
12 |
13 |
19 |
22 |
25 |
26 |
27 |
28 |
34 |
35 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_launcher_background.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
10 |
15 |
20 |
25 |
30 |
35 |
40 |
45 |
50 |
55 |
60 |
65 |
70 |
75 |
80 |
85 |
90 |
95 |
100 |
105 |
110 |
115 |
120 |
125 |
130 |
135 |
140 |
145 |
150 |
155 |
160 |
165 |
170 |
171 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/sp_circle.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/sr_et_bg_accent.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | -
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 | -
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_main.xml:
--------------------------------------------------------------------------------
1 |
2 |
9 |
10 |
17 |
18 |
21 |
22 |
31 |
32 |
38 |
39 |
40 |
41 |
50 |
51 |
60 |
61 |
72 |
73 |
76 |
77 |
85 |
86 |
91 |
92 |
100 |
101 |
106 |
107 |
115 |
116 |
117 |
120 |
121 |
129 |
130 |
138 |
139 |
147 |
148 |
156 |
157 |
165 |
166 |
167 |
168 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/simplepeng/VerCodeLayout/fa99fcb26b94e74d488b6a07c113075fd8b534fb/app/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/simplepeng/VerCodeLayout/fa99fcb26b94e74d488b6a07c113075fd8b534fb/app/src/main/res/mipmap-hdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/simplepeng/VerCodeLayout/fa99fcb26b94e74d488b6a07c113075fd8b534fb/app/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/simplepeng/VerCodeLayout/fa99fcb26b94e74d488b6a07c113075fd8b534fb/app/src/main/res/mipmap-mdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/simplepeng/VerCodeLayout/fa99fcb26b94e74d488b6a07c113075fd8b534fb/app/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/simplepeng/VerCodeLayout/fa99fcb26b94e74d488b6a07c113075fd8b534fb/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/simplepeng/VerCodeLayout/fa99fcb26b94e74d488b6a07c113075fd8b534fb/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/simplepeng/VerCodeLayout/fa99fcb26b94e74d488b6a07c113075fd8b534fb/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/simplepeng/VerCodeLayout/fa99fcb26b94e74d488b6a07c113075fd8b534fb/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/simplepeng/VerCodeLayout/fa99fcb26b94e74d488b6a07c113075fd8b534fb/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #008577
4 | #00574B
5 | #D81B60
6 |
7 |
--------------------------------------------------------------------------------
/app/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | VerCodeLayout
3 |
4 |
--------------------------------------------------------------------------------
/app/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/app/src/test/java/com/simple/vclayout_demo/ExampleUnitTest.java:
--------------------------------------------------------------------------------
1 | package com.simple.vclayout_demo;
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() {
15 | assertEquals(4, 2 + 2);
16 | }
17 | }
--------------------------------------------------------------------------------
/build.gradle:
--------------------------------------------------------------------------------
1 | // Top-level build file where you can add configuration options common to all sub-projects/modules.
2 | buildscript {
3 | repositories {
4 | google()
5 | mavenCentral()
6 | }
7 | dependencies {
8 | classpath "com.android.tools.build:gradle:7.0.3"
9 | classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:1.5.20"
10 |
11 | // NOTE: Do not place your application dependencies here; they belong
12 | // in the individual module build.gradle files
13 | }
14 | }
15 |
16 | task clean(type: Delete) {
17 | delete rootProject.buildDir
18 | }
19 |
--------------------------------------------------------------------------------
/gradle.properties:
--------------------------------------------------------------------------------
1 | # Project-wide Gradle settings.
2 | # IDE (e.g. Android Studio) users:
3 | # Gradle settings configured through the IDE *will override*
4 | # any settings specified in this file.
5 | # For more details on how to configure your build environment visit
6 | # http://www.gradle.org/docs/current/userguide/build_environment.html
7 | # Specifies the JVM arguments used for the daemon process.
8 | # The setting is particularly useful for tweaking memory settings.
9 | org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8
10 | # When configured, Gradle will run in incubating parallel mode.
11 | # This option should only be used with decoupled projects. More details, visit
12 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
13 | # org.gradle.parallel=true
14 | # AndroidX package structure to make it clearer which packages are bundled with the
15 | # Android operating system, and which are packaged with your app"s APK
16 | # https://developer.android.com/topic/libraries/support-library/androidx-rn
17 | android.useAndroidX=true
18 | # Kotlin code style for this project: "official" or "obsolete":
19 | kotlin.code.style=official
20 |
21 |
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/simplepeng/VerCodeLayout/fa99fcb26b94e74d488b6a07c113075fd8b534fb/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Sat Nov 27 22:49:18 CST 2021
2 | distributionBase=GRADLE_USER_HOME
3 | distributionUrl=https\://services.gradle.org/distributions/gradle-7.0.2-bin.zip
4 | distributionPath=wrapper/dists
5 | zipStorePath=wrapper/dists
6 | zipStoreBase=GRADLE_USER_HOME
7 |
8 |
--------------------------------------------------------------------------------
/gradlew:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env sh
2 |
3 | ##############################################################################
4 | ##
5 | ## Gradle start up script for UN*X
6 | ##
7 | ##############################################################################
8 |
9 | # Attempt to set APP_HOME
10 | # Resolve links: $0 may be a link
11 | PRG="$0"
12 | # Need this for relative symlinks.
13 | while [ -h "$PRG" ] ; do
14 | ls=`ls -ld "$PRG"`
15 | link=`expr "$ls" : '.*-> \(.*\)$'`
16 | if expr "$link" : '/.*' > /dev/null; then
17 | PRG="$link"
18 | else
19 | PRG=`dirname "$PRG"`"/$link"
20 | fi
21 | done
22 | SAVED="`pwd`"
23 | cd "`dirname \"$PRG\"`/" >/dev/null
24 | APP_HOME="`pwd -P`"
25 | cd "$SAVED" >/dev/null
26 |
27 | APP_NAME="Gradle"
28 | APP_BASE_NAME=`basename "$0"`
29 |
30 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
31 | DEFAULT_JVM_OPTS=""
32 |
33 | # Use the maximum available, or set MAX_FD != -1 to use that value.
34 | MAX_FD="maximum"
35 |
36 | warn () {
37 | echo "$*"
38 | }
39 |
40 | die () {
41 | echo
42 | echo "$*"
43 | echo
44 | exit 1
45 | }
46 |
47 | # OS specific support (must be 'true' or 'false').
48 | cygwin=false
49 | msys=false
50 | darwin=false
51 | nonstop=false
52 | case "`uname`" in
53 | CYGWIN* )
54 | cygwin=true
55 | ;;
56 | Darwin* )
57 | darwin=true
58 | ;;
59 | MINGW* )
60 | msys=true
61 | ;;
62 | NONSTOP* )
63 | nonstop=true
64 | ;;
65 | esac
66 |
67 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
68 |
69 | # Determine the Java command to use to start the JVM.
70 | if [ -n "$JAVA_HOME" ] ; then
71 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
72 | # IBM's JDK on AIX uses strange locations for the executables
73 | JAVACMD="$JAVA_HOME/jre/sh/java"
74 | else
75 | JAVACMD="$JAVA_HOME/bin/java"
76 | fi
77 | if [ ! -x "$JAVACMD" ] ; then
78 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
79 |
80 | Please set the JAVA_HOME variable in your environment to match the
81 | location of your Java installation."
82 | fi
83 | else
84 | JAVACMD="java"
85 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
86 |
87 | Please set the JAVA_HOME variable in your environment to match the
88 | location of your Java installation."
89 | fi
90 |
91 | # Increase the maximum file descriptors if we can.
92 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
93 | MAX_FD_LIMIT=`ulimit -H -n`
94 | if [ $? -eq 0 ] ; then
95 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
96 | MAX_FD="$MAX_FD_LIMIT"
97 | fi
98 | ulimit -n $MAX_FD
99 | if [ $? -ne 0 ] ; then
100 | warn "Could not set maximum file descriptor limit: $MAX_FD"
101 | fi
102 | else
103 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
104 | fi
105 | fi
106 |
107 | # For Darwin, add options to specify how the application appears in the dock
108 | if $darwin; then
109 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
110 | fi
111 |
112 | # For Cygwin, switch paths to Windows format before running java
113 | if $cygwin ; then
114 | APP_HOME=`cygpath --path --mixed "$APP_HOME"`
115 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
116 | JAVACMD=`cygpath --unix "$JAVACMD"`
117 |
118 | # We build the pattern for arguments to be converted via cygpath
119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
120 | SEP=""
121 | for dir in $ROOTDIRSRAW ; do
122 | ROOTDIRS="$ROOTDIRS$SEP$dir"
123 | SEP="|"
124 | done
125 | OURCYGPATTERN="(^($ROOTDIRS))"
126 | # Add a user-defined pattern to the cygpath arguments
127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then
128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
129 | fi
130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
131 | i=0
132 | for arg in "$@" ; do
133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
135 |
136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
138 | else
139 | eval `echo args$i`="\"$arg\""
140 | fi
141 | i=$((i+1))
142 | done
143 | case $i in
144 | (0) set -- ;;
145 | (1) set -- "$args0" ;;
146 | (2) set -- "$args0" "$args1" ;;
147 | (3) set -- "$args0" "$args1" "$args2" ;;
148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
154 | esac
155 | fi
156 |
157 | # Escape application args
158 | save () {
159 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
160 | echo " "
161 | }
162 | APP_ARGS=$(save "$@")
163 |
164 | # Collect all arguments for the java command, following the shell quoting and substitution rules
165 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
166 |
167 | # by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
168 | if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
169 | cd "$(dirname "$0")"
170 | fi
171 |
172 | exec "$JAVACMD" "$@"
173 |
--------------------------------------------------------------------------------
/gradlew.bat:
--------------------------------------------------------------------------------
1 | @if "%DEBUG%" == "" @echo off
2 | @rem ##########################################################################
3 | @rem
4 | @rem Gradle startup script for Windows
5 | @rem
6 | @rem ##########################################################################
7 |
8 | @rem Set local scope for the variables with windows NT shell
9 | if "%OS%"=="Windows_NT" setlocal
10 |
11 | set DIRNAME=%~dp0
12 | if "%DIRNAME%" == "" set DIRNAME=.
13 | set APP_BASE_NAME=%~n0
14 | set APP_HOME=%DIRNAME%
15 |
16 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
17 | set DEFAULT_JVM_OPTS=
18 |
19 | @rem Find java.exe
20 | if defined JAVA_HOME goto findJavaFromJavaHome
21 |
22 | set JAVA_EXE=java.exe
23 | %JAVA_EXE% -version >NUL 2>&1
24 | if "%ERRORLEVEL%" == "0" goto init
25 |
26 | echo.
27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
28 | echo.
29 | echo Please set the JAVA_HOME variable in your environment to match the
30 | echo location of your Java installation.
31 |
32 | goto fail
33 |
34 | :findJavaFromJavaHome
35 | set JAVA_HOME=%JAVA_HOME:"=%
36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
37 |
38 | if exist "%JAVA_EXE%" goto init
39 |
40 | echo.
41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
42 | echo.
43 | echo Please set the JAVA_HOME variable in your environment to match the
44 | echo location of your Java installation.
45 |
46 | goto fail
47 |
48 | :init
49 | @rem Get command-line arguments, handling Windows variants
50 |
51 | if not "%OS%" == "Windows_NT" goto win9xME_args
52 |
53 | :win9xME_args
54 | @rem Slurp the command line arguments.
55 | set CMD_LINE_ARGS=
56 | set _SKIP=2
57 |
58 | :win9xME_args_slurp
59 | if "x%~1" == "x" goto execute
60 |
61 | set CMD_LINE_ARGS=%*
62 |
63 | :execute
64 | @rem Setup the command line
65 |
66 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
67 |
68 | @rem Execute Gradle
69 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
70 |
71 | :end
72 | @rem End local scope for the variables with windows NT shell
73 | if "%ERRORLEVEL%"=="0" goto mainEnd
74 |
75 | :fail
76 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
77 | rem the _cmd.exe /c_ return code!
78 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
79 | exit /b 1
80 |
81 | :mainEnd
82 | if "%OS%"=="Windows_NT" endlocal
83 |
84 | :omega
85 |
--------------------------------------------------------------------------------
/library/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/library/build.gradle:
--------------------------------------------------------------------------------
1 | plugins {
2 | id 'com.android.library'
3 | id 'kotlin-android'
4 | id 'maven-publish'
5 | }
6 |
7 | afterEvaluate {
8 | publishing {
9 | publications {
10 | release(MavenPublication) {
11 | groupId = 'com.github.simplepeng'
12 | artifactId = 'VerCodeLayout'
13 | version = 'v1.0.2'
14 | from components.release
15 | }
16 | }
17 | }
18 | }
19 |
20 | android {
21 | compileSdk 31
22 |
23 | defaultConfig {
24 | minSdk 16
25 | targetSdk 31
26 | }
27 |
28 | compileOptions {
29 | sourceCompatibility JavaVersion.VERSION_1_8
30 | targetCompatibility JavaVersion.VERSION_1_8
31 | }
32 |
33 | kotlinOptions {
34 | jvmTarget = '1.8'
35 | }
36 |
37 | //指定不生成BuildConfig.java
38 | buildFeatures{
39 | buildConfig = false
40 | }
41 | }
42 |
43 | dependencies {
44 | implementation 'androidx.appcompat:appcompat:1.3.1'
45 | }
46 |
--------------------------------------------------------------------------------
/library/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # You can control the set of applied configuration files using the
3 | # proguardFiles setting in build.gradle.
4 | #
5 | # For more details, see
6 | # http://developer.android.com/guide/developing/tools/proguard.html
7 |
8 | # If your project uses WebView with JS, uncomment the following
9 | # and specify the fully qualified class name to the JavaScript interface
10 | # class:
11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
12 | # public *;
13 | #}
14 |
15 | # Uncomment this to preserve the line number information for
16 | # debugging stack traces.
17 | #-keepattributes SourceFile,LineNumberTable
18 |
19 | # If you keep the line number information, uncomment this to
20 | # hide the original source file name.
21 | #-renamesourcefileattribute SourceFile
22 |
--------------------------------------------------------------------------------
/library/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
3 |
--------------------------------------------------------------------------------
/library/src/main/java/com/simple/vclayout/Utils.java:
--------------------------------------------------------------------------------
1 | package com.simple.vclayout;
2 |
3 | import android.annotation.SuppressLint;
4 | import android.content.Context;
5 | import android.os.Build;
6 | import android.text.InputFilter;
7 | import android.util.Log;
8 | import android.widget.EditText;
9 | import android.widget.TextView;
10 |
11 | import java.lang.reflect.Field;
12 |
13 | public class Utils {
14 |
15 | public static int px2dip(Context context, float pxValue) {
16 | final float scale = context.getResources().getDisplayMetrics().density;
17 | return (int) (pxValue / scale + 0.5f);
18 | }
19 |
20 | public static int dip2px(Context context, float dipValue) {
21 | final float scale = context.getResources().getDisplayMetrics().density;
22 | return (int) (dipValue * scale + 0.5f);
23 | }
24 |
25 | public static int px2sp(Context context, float pxValue) {
26 | final float fontScale = context.getResources().getDisplayMetrics().scaledDensity;
27 | return (int) (pxValue / fontScale + 0.5f);
28 | }
29 |
30 | public static int sp2px(Context context, float spValue) {
31 | final float fontScale = context.getResources().getDisplayMetrics().scaledDensity;
32 | return (int) (spValue * fontScale + 0.5f);
33 | }
34 |
35 | public static void log(String msg) {
36 | Log.d(VerCodeLayout.TAG, msg);
37 | }
38 |
39 |
40 | public static int getMaxLength(EditText editText) {
41 | int maxLength = 0;
42 | InputFilter[] filters = editText.getFilters();
43 |
44 | if (filters == null || filters.length == 0) {
45 | return maxLength;
46 | }
47 |
48 | for (InputFilter filter : filters) {
49 | if (filter instanceof InputFilter.LengthFilter) {
50 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
51 | InputFilter.LengthFilter lengthFilter = ((InputFilter.LengthFilter) filter);
52 | maxLength = lengthFilter.getMax();
53 | } else {
54 | //below 21
55 | try {
56 | Field field = filter.getClass().getDeclaredField("mMax");
57 | field.setAccessible(true);
58 |
59 | if (field.isAccessible()) {
60 | maxLength = field.getInt(filter);
61 | }
62 | } catch (Exception e) {
63 | e.printStackTrace();
64 | }
65 |
66 | }
67 | }
68 | }
69 | return maxLength;
70 | }
71 |
72 | @SuppressLint("SoonBlockedPrivateApi")
73 | public static void setTextCursorDrawable(EditText editText, int resId) {
74 | try {
75 | Field f = TextView.class.getDeclaredField("mCursorDrawableRes");
76 | f.setAccessible(true);
77 | f.set(editText, resId);
78 | } catch (Exception e) {
79 |
80 | }
81 | }
82 |
83 | }
84 |
--------------------------------------------------------------------------------
/library/src/main/java/com/simple/vclayout/VerCodeEditText.java:
--------------------------------------------------------------------------------
1 | package com.simple.vclayout;
2 |
3 | import android.content.Context;
4 | import android.content.res.TypedArray;
5 | import android.graphics.Color;
6 | import android.graphics.Rect;
7 | import android.graphics.drawable.Drawable;
8 | import android.text.InputFilter;
9 | import android.text.InputType;
10 | import android.util.AttributeSet;
11 | import android.util.TypedValue;
12 | import android.view.Gravity;
13 | import android.view.View;
14 | import android.view.ViewGroup;
15 | import android.widget.EditText;
16 |
17 | import androidx.annotation.ColorInt;
18 |
19 | public class VerCodeEditText extends VerCodeLayout {
20 |
21 | //输入框的总数
22 | private int mCount;
23 | //最大输入长度
24 | private int mMaxLength;
25 | /**
26 | *
27 | */
28 | private Drawable mNormalBackground;
29 | private Drawable mFocusedBackground;
30 | /**
31 | *
32 | */
33 | private int mWidth;
34 | private int mHeight;
35 | private int mMinWidth;
36 | private int mMinHeight;
37 | /**
38 | * set
39 | */
40 | private float mTextSize;
41 | @ColorInt
42 | private int mTextColor;
43 | private int mTextCursorDrawable;
44 | private int mGravity;
45 | private int mInputType;
46 | /**
47 | *
48 | */
49 | private int mMargin;
50 | private int mMarginLeft;
51 | private int mMarginTop;
52 | private int mMarginRight;
53 | private int mMarginBottom;
54 | /**
55 | *
56 | */
57 | private int mPadding;
58 | private int mPaddingLeft;
59 | private int mPaddingTop;
60 | private int mPaddingRight;
61 | private int mPaddingBottom;
62 |
63 |
64 | public VerCodeEditText(Context context) {
65 | this(context, null);
66 | }
67 |
68 | public VerCodeEditText(Context context, AttributeSet attrs) {
69 | this(context, attrs, 0);
70 | }
71 |
72 | public VerCodeEditText(Context context, AttributeSet attrs, int defStyleAttr) {
73 | super(context, attrs, defStyleAttr);
74 |
75 | TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.VerCodeEditText);
76 | //
77 | mCount = ta.getInt(R.styleable.VerCodeEditText_vcCount, 0);
78 | mMaxLength = ta.getInt(R.styleable.VerCodeEditText_vcMaxLength, 1);
79 | //
80 | mNormalBackground = ta.getDrawable(R.styleable.VerCodeEditText_vcNormalBackground);
81 | mFocusedBackground = ta.getDrawable(R.styleable.VerCodeEditText_vcFocusedBackground);
82 | //
83 | mTextSize = ta.getDimensionPixelSize(R.styleable.VerCodeEditText_vcTextSize, 0);
84 | mTextColor = ta.getColor(R.styleable.VerCodeEditText_vcTextColor, Color.BLACK);
85 | mTextCursorDrawable = ta.getResourceId(R.styleable.VerCodeEditText_vcTextCursorDrawable, -1);
86 | mGravity = ta.getInt(R.styleable.VerCodeEditText_vcGravity, Gravity.CENTER);
87 | mInputType = ta.getInt(R.styleable.VerCodeEditText_vcInputType, InputType.TYPE_CLASS_NUMBER);
88 | //
89 | mWidth = (int) ta.getDimension(R.styleable.VerCodeEditText_vcWidth, 0f);
90 | mHeight = (int) ta.getDimension(R.styleable.VerCodeEditText_vcHeight, 0f);
91 | mMinWidth = ta.getDimensionPixelSize(R.styleable.VerCodeEditText_vcMinWidth, 0);
92 | mMinHeight = ta.getDimensionPixelSize(R.styleable.VerCodeEditText_vcMinHeight, 0);
93 | //
94 | mMargin = (int) ta.getDimension(R.styleable.VerCodeEditText_vcMargin, 0);
95 | mMarginLeft = (int) ta.getDimension(R.styleable.VerCodeEditText_vcMarginLeft, 0);
96 | mMarginTop = (int) ta.getDimension(R.styleable.VerCodeEditText_vcMarginTop, 0);
97 | mMarginRight = (int) ta.getDimension(R.styleable.VerCodeEditText_vcMarginRight, 0);
98 | mMarginBottom = (int) ta.getDimension(R.styleable.VerCodeEditText_vcMarginBottom, 0);
99 | //
100 | mPadding = (int) ta.getDimension(R.styleable.VerCodeEditText_vcPadding, -1);
101 | mPaddingLeft = (int) ta.getDimension(R.styleable.VerCodeEditText_vcPaddingLeft, -1);
102 | mPaddingTop = (int) ta.getDimension(R.styleable.VerCodeEditText_vcPaddingTop, -1);
103 | mPaddingRight = (int) ta.getDimension(R.styleable.VerCodeEditText_vcPaddingRight, -1);
104 | mPaddingBottom = (int) ta.getDimension(R.styleable.VerCodeEditText_vcPaddingBottom, -1);
105 | //
106 | autoFocus = ta.getBoolean(R.styleable.VerCodeEditText_vcAutoFocus, true);
107 | enterFocus = ta.getBoolean(R.styleable.VerCodeEditText_vcEnterFocus, true);
108 |
109 | ta.recycle();
110 |
111 | createEditTexts();
112 | }
113 |
114 | //创建EditText们
115 | private void createEditTexts() {
116 | if (mCount <= 0) return;
117 |
118 | EditText editText;
119 | for (int i = 0; i < mCount; i++) {
120 | editText = new EditText(getContext());
121 | //
122 | if (i == 0 && enterFocus) editText.requestFocus();
123 | if (i > 0 && autoFocus) editText.setEnabled(false);
124 | //
125 | setDefault(editText);
126 | //margin
127 | MarginLayoutParams params = getMarginLayoutParams();
128 | //background
129 | setBackground(editText);
130 | //padding
131 | setPadding(editText);
132 | //addView
133 | this.addView(editText, params);
134 | }
135 | }
136 |
137 | //设置默认的属性
138 | private void setDefault(EditText editText) {
139 | editText.setMaxLines(1);
140 | if (mTextSize != 0) {
141 | editText.setTextSize(TypedValue.COMPLEX_UNIT_PX, mTextSize);
142 | }
143 | editText.setTextColor(mTextColor);
144 | if (mTextCursorDrawable != -1) {
145 | Utils.setTextCursorDrawable(editText, mTextCursorDrawable);
146 | }
147 | editText.setFilters(new InputFilter[]{new InputFilter.LengthFilter(mMaxLength)});
148 | editText.setGravity(Gravity.CENTER);
149 | editText.setMinWidth(mMinWidth);
150 | editText.setMinHeight(mMinHeight);
151 | editText.setGravity(mGravity);
152 | editText.setInputType(mInputType);
153 | }
154 |
155 | private void setBackground(EditText editText) {
156 | editText.setOnFocusChangeListener(new OnFocusChangeListener() {
157 | @Override
158 | public void onFocusChange(View v, boolean hasFocus) {
159 | Drawable d = hasFocus ? mFocusedBackground : mNormalBackground;
160 | if (d == null) return;
161 | v.setBackgroundDrawable(d);
162 | }
163 | });
164 | if (mNormalBackground != null) {
165 | editText.setBackgroundDrawable(mNormalBackground);
166 | }
167 | }
168 |
169 | private void setPadding(EditText editText) {
170 | if (mPadding != -1) {
171 | editText.setPadding(mPadding, mPadding, mPadding, mPadding);
172 | } else {
173 | if (editText.getBackground() != null) {
174 | Drawable d = editText.getBackground();
175 | Rect r = new Rect();
176 | d.getPadding(r);
177 | int left = mPaddingLeft != -1 ? mPaddingLeft : r.left;
178 | int top = mPaddingTop != -1 ? mPaddingTop : r.top;
179 | int right = mPaddingRight != -1 ? mPaddingRight : r.right;
180 | int bottom = mPaddingBottom != -1 ? mPaddingBottom : r.bottom;
181 | editText.setPadding(left, top, right, bottom);
182 | } else {
183 | editText.setPadding(mPaddingLeft, mPaddingTop, mPaddingRight, mPaddingBottom);
184 | }
185 | }
186 | }
187 |
188 | private MarginLayoutParams getMarginLayoutParams() {
189 | MarginLayoutParams params = new MarginLayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT,
190 | ViewGroup.LayoutParams.WRAP_CONTENT);
191 | if (mMargin != 0) {
192 | params.leftMargin = mMargin;
193 | params.topMargin = mMargin;
194 | params.rightMargin = mMargin;
195 | params.bottomMargin = mMargin;
196 | } else {
197 | params.leftMargin = mMarginLeft;
198 | params.topMargin = mMarginTop;
199 | params.rightMargin = mMarginRight;
200 | params.bottomMargin = mMarginBottom;
201 | }
202 | if (mWidth != 0) {
203 | params.width = mWidth;
204 | }
205 | if (mHeight != 0) {
206 | params.height = mHeight;
207 | }
208 | return params;
209 | }
210 | }
211 |
--------------------------------------------------------------------------------
/library/src/main/java/com/simple/vclayout/VerCodeLayout.java:
--------------------------------------------------------------------------------
1 | package com.simple.vclayout;
2 |
3 | import android.content.Context;
4 | import android.text.Editable;
5 | import android.text.TextWatcher;
6 | import android.util.AttributeSet;
7 | import android.view.KeyEvent;
8 | import android.view.View;
9 | import android.widget.EditText;
10 | import android.widget.LinearLayout;
11 |
12 | import java.util.ArrayList;
13 | import java.util.List;
14 |
15 | public class VerCodeLayout extends LinearLayout {
16 |
17 | public static final String TAG = "VerCodeLayout";
18 |
19 | protected List mEditTexts;
20 | private OnCompleteListener mOnCompleteListener;
21 |
22 | //是否自动聚焦,不可点击聚焦
23 | public boolean autoFocus = true;
24 |
25 | //焦点是否默认选中
26 | public boolean enterFocus = true;
27 |
28 | public VerCodeLayout(Context context) {
29 | this(context, null);
30 | }
31 |
32 | public VerCodeLayout(Context context, AttributeSet attrs) {
33 | this(context, attrs, 0);
34 | }
35 |
36 | public VerCodeLayout(Context context, AttributeSet attrs, int defStyleAttr) {
37 | super(context, attrs, defStyleAttr);
38 |
39 | mEditTexts = new ArrayList<>();
40 | }
41 |
42 | @Override
43 | protected void onFinishInflate() {
44 | super.onFinishInflate();
45 |
46 | final int childCount = getChildCount();
47 | EditText et_child;
48 | for (int i = 0; i < childCount; i++) {
49 | View child = getChildAt(i);
50 | if (child instanceof EditText) {
51 | et_child = ((EditText) child);
52 | setupEditText(et_child);
53 | }
54 | }
55 | }
56 |
57 | //基本配置
58 | private void setupEditText(EditText editText) {
59 | mEditTexts.add(editText);
60 | editText.setOnKeyListener(new InnerKeyListener(editText));
61 | editText.addTextChangedListener(new InnerTextWatcher(editText));
62 | }
63 |
64 | //按键监听
65 | class InnerKeyListener implements OnKeyListener {
66 |
67 | EditText innerEditText;
68 | int maxLength;
69 |
70 | public InnerKeyListener(EditText editText) {
71 | this.innerEditText = editText;
72 | this.maxLength = Utils.getMaxLength(editText);
73 | }
74 |
75 | @Override
76 | public boolean onKey(View v, int keyCode, KeyEvent event) {
77 | int textLength = innerEditText.getText().length();
78 | if (keyCode == KeyEvent.KEYCODE_DEL && textLength == 0) {
79 | focusPrevious(innerEditText);
80 | return true;
81 | }
82 | return false;
83 | }
84 | }
85 |
86 | //输入文本监听
87 | class InnerTextWatcher implements TextWatcher {
88 |
89 | EditText innerEditText;
90 | int maxLength;
91 |
92 | public InnerTextWatcher(EditText editText) {
93 | innerEditText = editText;
94 | this.maxLength = Utils.getMaxLength(editText);
95 | }
96 |
97 | @Override
98 | public void beforeTextChanged(CharSequence s, int start, int count, int after) {
99 | }
100 |
101 | @Override
102 | public void onTextChanged(CharSequence s, int start, int before, int count) {
103 | }
104 |
105 | @Override
106 | public void afterTextChanged(Editable s) {
107 | final int count = s.length();
108 | if (maxLength == 0) {
109 | throw new IllegalArgumentException(getResources().getString(R.string.exception_no_max_length));
110 | }
111 | if (count >= maxLength) {
112 | focusNext(innerEditText);
113 | }
114 | }
115 | }
116 |
117 | //移动焦点到下一个输入框
118 | protected void focusNext(EditText et) {
119 | final int index = mEditTexts.indexOf(et);
120 | if (index < mEditTexts.size() - 1) {
121 | EditText nextEt = mEditTexts.get(index + 1);
122 | nextEt.setEnabled(true);
123 | nextEt.requestFocus();
124 |
125 | if (autoFocus) {
126 | et.setEnabled(false);
127 | }
128 | return;
129 | }
130 | //完成监听
131 | if (mOnCompleteListener != null) {
132 | Editable editable = Editable.Factory.getInstance().newEditable("");
133 | for (EditText editText : mEditTexts) {
134 | editable.append(editText.getText());
135 | }
136 | mOnCompleteListener.onComplete(editable, editable.toString());
137 | }
138 |
139 | }
140 |
141 | //移动焦点到上一个输入框
142 | protected void focusPrevious(EditText focusEditText) {
143 | final int index = mEditTexts.indexOf(focusEditText);
144 | if (index != 0) {
145 | EditText preEditText = mEditTexts.get(index - 1);
146 | preEditText.setEnabled(true);
147 | preEditText.requestFocus();
148 |
149 | int preMaxLength = Utils.getMaxLength(preEditText);
150 | if (preMaxLength == 1) {
151 | preEditText.post(new Runnable() {
152 | @Override
153 | public void run() {
154 | preEditText.setText("");
155 | }
156 | });
157 | }
158 |
159 | if (autoFocus) {
160 | focusEditText.setEnabled(false);
161 | }
162 | }
163 | }
164 |
165 | //清除所有文本
166 | public void clear() {
167 | if (mEditTexts.isEmpty()) return;
168 | for (EditText editText : mEditTexts) {
169 | editText.setText("");
170 | editText.clearFocus();
171 | }
172 | EditText first = mEditTexts.get(0);
173 | if (first != null) {
174 | first.setEnabled(true);
175 | first.requestFocus();
176 | }
177 | }
178 |
179 | //所有的EditText
180 | public List getEditTexts() {
181 | return mEditTexts;
182 | }
183 |
184 | //输入完成监听事件
185 | public void setOnCompleteListener(OnCompleteListener listener) {
186 | this.mOnCompleteListener = listener;
187 | }
188 |
189 | public interface OnCompleteListener {
190 | void onComplete(Editable editable, String code);
191 | }
192 |
193 | }
194 |
--------------------------------------------------------------------------------
/library/src/main/res/drawable/sp_cursor.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/library/src/main/res/drawable/sp_focused_one.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
9 |
12 |
--------------------------------------------------------------------------------
/library/src/main/res/drawable/sp_focused_two.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | -
9 |
10 |
11 |
14 |
17 |
18 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/library/src/main/res/drawable/sp_normal_one.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
9 |
12 |
--------------------------------------------------------------------------------
/library/src/main/res/drawable/sp_normal_two.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | -
9 |
10 |
11 |
14 |
17 |
18 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/library/src/main/res/drawable/sr_one.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/library/src/main/res/values/atts.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
--------------------------------------------------------------------------------
/library/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #008577
4 | #00574B
5 | #D81B60
6 |
7 |
--------------------------------------------------------------------------------
/library/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | VerCodeLayout
3 |
4 | max count can not be 0
5 | editText not set maxLength? go home you are drunk
6 |
7 |
--------------------------------------------------------------------------------
/settings.gradle:
--------------------------------------------------------------------------------
1 | dependencyResolutionManagement {
2 | repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
3 | repositories {
4 | google()
5 | mavenCentral()
6 | jcenter() // Warning: this repository is going to shut down soon
7 | maven { url 'https://jitpack.io' }
8 | }
9 | }
10 | include ':app'
11 | include ':library'
12 | rootProject.name = "VerCodeLayout"
13 |
--------------------------------------------------------------------------------
/statics/vercode.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/simplepeng/VerCodeLayout/fa99fcb26b94e74d488b6a07c113075fd8b534fb/statics/vercode.gif
--------------------------------------------------------------------------------