├── .gitignore
├── LICENSE
├── README.md
├── app
├── .gitignore
├── build.gradle
├── proguard-rules.pro
└── src
│ ├── androidTest
│ └── java
│ │ └── com
│ │ └── dk_exp
│ │ └── patheffecttextview
│ │ └── ApplicationTest.java
│ └── main
│ ├── AndroidManifest.xml
│ ├── java
│ └── com
│ │ └── alokvnair
│ │ └── patheffectdemo
│ │ └── MainActivity.java
│ └── res
│ ├── layout
│ └── activity_main.xml
│ ├── mipmap-hdpi
│ └── ic_launcher.png
│ ├── mipmap-mdpi
│ └── ic_launcher.png
│ ├── mipmap-xhdpi
│ └── ic_launcher.png
│ ├── mipmap-xxhdpi
│ └── ic_launcher.png
│ ├── values-w820dp
│ └── dimens.xml
│ └── values
│ ├── colors.xml
│ ├── dimens.xml
│ ├── strings.xml
│ └── styles.xml
├── build.gradle
├── gradle.properties
├── patheffecttextview
├── .gitignore
├── build.gradle
├── proguard-rules.pro
└── src
│ ├── androidTest
│ └── java
│ │ └── com
│ │ └── alokvnair
│ │ └── patheffect
│ │ └── ApplicationTest.java
│ └── main
│ ├── AndroidManifest.xml
│ ├── java
│ └── com
│ │ └── alokvnair
│ │ └── patheffect
│ │ ├── MatchPath.java
│ │ └── PathEffectTextView.java
│ └── res
│ └── values
│ └── attrs.xml
├── screens
└── pathEffective.gif
└── settings.gradle
/.gitignore:
--------------------------------------------------------------------------------
1 | # built application files
2 | *.apk
3 | *.ap_
4 |
5 | # files for the dex VM
6 | *.dex
7 |
8 | # Java class files
9 | *.class
10 |
11 | # generated files
12 | bin/
13 | gen/
14 |
15 | # Local configuration file (sdk path, etc)
16 | local.properties
17 |
18 | # Eclipse project files
19 | .classpath
20 | .project
21 |
22 | # Proguard folder generated by Eclipse
23 | proguard/
24 |
25 | # Intellij project files
26 | *.iml
27 | *.ipr
28 | *.iws
29 | .idea/
30 |
31 | .gradle/
32 | gradle/
33 | gradlew
34 | gradlew.bat
35 | build/
36 | .DS_Store
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2015 Alok Nair
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 |
23 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # PathEffectTextView [](https://bintray.com/alokvnair/maven/PathEffectTextView/_latestVersion) [](http://android-arsenal.com/details/1/2305)
2 |
3 | TextView with path tracing effect.
4 |
5 | 
6 |
7 | ###Usage
8 |
9 | You can add to your project by adding the following dependency to your `build.gradle`
10 |
11 | ```
12 | dependencies {
13 | compile 'com.alokvnair:patheffecttextview:1.0.1'
14 | }
15 | ```
16 |
17 | has three attribute `text_color`, `stroke_width`, `multiple`
18 |
19 | ```xml
20 |
27 | ```
28 |
29 | `text_color` is for setting color of the text. Default value is Black.
30 | `stroke_width` is for setting width of text strokes. Default value is 2. Value suggested is within the range 2 to 8.
31 | `multiple` is for setting whether to draw path for all characters in text at once or character by character. If `true`, then path of all characters in text will be drawn together. If `false`, then text will be drawn character by character from first to last. Default value is false.
32 |
33 | ### License
34 |
35 | ```
36 | The MIT License (MIT)
37 |
38 | Copyright (c) 2015 Alok Nair
39 |
40 | Permission is hereby granted, free of charge, to any person obtaining a copy
41 | of this software and associated documentation files (the "Software"), to deal
42 | in the Software without restriction, including without limitation the rights
43 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
44 | copies of the Software, and to permit persons to whom the Software is
45 | furnished to do so, subject to the following conditions:
46 |
47 | The above copyright notice and this permission notice shall be included in all
48 | copies or substantial portions of the Software.
49 |
50 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
51 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
52 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
53 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
54 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
55 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
56 | SOFTWARE.
57 | ```
58 |
--------------------------------------------------------------------------------
/app/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/app/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.application'
2 |
3 | android {
4 | compileSdkVersion 22
5 | buildToolsVersion "22.0.1"
6 |
7 | defaultConfig {
8 | applicationId "com.alokvnair.patheffectdemo"
9 | minSdkVersion 16
10 | targetSdkVersion 22
11 | versionCode 1
12 | versionName "1.0"
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 | compile 'com.android.support:appcompat-v7:22.2.1'
25 | compile project(':patheffecttextview')
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 C:\Users\DK\Documents\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 |
--------------------------------------------------------------------------------
/app/src/androidTest/java/com/dk_exp/patheffecttextview/ApplicationTest.java:
--------------------------------------------------------------------------------
1 | package com.dk_exp.patheffecttextview;
2 |
3 | import android.app.Application;
4 | import android.test.ApplicationTestCase;
5 |
6 | /**
7 | * Testing Fundamentals
8 | */
9 | public class ApplicationTest extends ApplicationTestCase {
10 | public ApplicationTest() {
11 | super(Application.class);
12 | }
13 | }
--------------------------------------------------------------------------------
/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
10 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/app/src/main/java/com/alokvnair/patheffectdemo/MainActivity.java:
--------------------------------------------------------------------------------
1 | package com.alokvnair.patheffectdemo;
2 |
3 | import android.os.Bundle;
4 | import android.support.v7.app.AppCompatActivity;
5 | import android.view.View;
6 | import android.widget.EditText;
7 |
8 | import com.alokvnair.patheffect.PathEffectTextView;
9 |
10 | public class MainActivity extends AppCompatActivity {
11 | private PathEffectTextView mPathEffectTextView;
12 | private EditText mEditText;
13 |
14 | @Override
15 | protected void onCreate(Bundle savedInstanceState) {
16 | super.onCreate(savedInstanceState);
17 | setContentView(R.layout.activity_main);
18 | mPathEffectTextView = (PathEffectTextView) findViewById(R.id.path);
19 | mEditText = (EditText) findViewById(R.id.edit);
20 | findViewById(R.id.btn).setOnClickListener(new View.OnClickListener() {
21 | @Override
22 | public void onClick(View v) {
23 | mPathEffectTextView.init(mEditText.getText().toString());
24 | }
25 | });
26 | }
27 |
28 |
29 | }
30 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_main.xml:
--------------------------------------------------------------------------------
1 |
10 |
11 |
19 |
20 |
21 |
25 |
26 |
32 |
33 |
40 |
41 |
42 |
43 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jiyuren/PathEffectTextView/4f5e779ee98f8f6c98e2dc0088420ed6e7dfb032/app/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jiyuren/PathEffectTextView/4f5e779ee98f8f6c98e2dc0088420ed6e7dfb032/app/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jiyuren/PathEffectTextView/4f5e779ee98f8f6c98e2dc0088420ed6e7dfb032/app/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jiyuren/PathEffectTextView/4f5e779ee98f8f6c98e2dc0088420ed6e7dfb032/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/values-w820dp/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 | 64dp
3 |
4 |
--------------------------------------------------------------------------------
/app/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #00BCD4
4 | #0097A7
5 | #18FFFF
6 | #FFFFFF
7 | #4CAF50
8 | #E91E63
9 |
--------------------------------------------------------------------------------
/app/src/main/res/values/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 16dp
4 | 16dp
5 |
6 |
--------------------------------------------------------------------------------
/app/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | PathEffectTextView
3 | MainActivity
4 |
5 | Hello world!
6 | Settings
7 | Display
8 |
9 |
--------------------------------------------------------------------------------
/app/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/build.gradle:
--------------------------------------------------------------------------------
1 | // Top-level build file where you can add configuration options common to all sub-projects/modules.
2 |
3 | buildscript {
4 | repositories {
5 | jcenter()
6 | }
7 | dependencies {
8 | classpath 'com.android.tools.build:gradle:1.3.0'
9 | classpath 'com.jfrog.bintray.gradle:gradle-bintray-plugin:1.2'
10 | classpath 'com.github.dcendents:android-maven-plugin:1.2'
11 |
12 | // NOTE: Do not place your application dependencies here; they belong
13 | // in the individual module build.gradle files
14 | }
15 | }
16 |
17 | allprojects {
18 | repositories {
19 | jcenter()
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/gradle.properties:
--------------------------------------------------------------------------------
1 | # Project-wide Gradle settings.
2 |
3 | # IDE (e.g. Android Studio) users:
4 | # Gradle settings configured through the IDE *will override*
5 | # any settings specified in this file.
6 |
7 | # For more details on how to configure your build environment visit
8 | # http://www.gradle.org/docs/current/userguide/build_environment.html
9 |
10 | # Specifies the JVM arguments used for the daemon process.
11 | # The setting is particularly useful for tweaking memory settings.
12 | # Default value: -Xmx10248m -XX:MaxPermSize=256m
13 | # org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8
14 |
15 | # When configured, Gradle will run in incubating parallel mode.
16 | # This option should only be used with decoupled projects. More details, visit
17 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
18 | # org.gradle.parallel=true
--------------------------------------------------------------------------------
/patheffecttextview/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/patheffecttextview/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.library'
2 |
3 | android {
4 | compileSdkVersion 22
5 | buildToolsVersion "22.0.1"
6 |
7 | defaultConfig {
8 | minSdkVersion 16
9 | targetSdkVersion 22
10 | versionCode 1
11 | versionName "1.0"
12 | }
13 | buildTypes {
14 | release {
15 | minifyEnabled false
16 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
17 | }
18 | }
19 | }
20 |
21 | dependencies {
22 | compile fileTree(dir: 'libs', include: ['*.jar'])
23 | }
24 |
25 | ext {
26 | bintrayRepo = 'maven'
27 | bintrayName = 'PathEffectTextView'
28 |
29 | publishedGroupId = 'com.alokvnair'
30 | libraryName = 'PathEffectTextView'
31 |
32 | artifact = 'patheffecttextview'
33 |
34 | libraryDescription = 'TextView with path tracing effect'
35 | libraryVersion = '1.0.1'
36 |
37 | developerId = 'alokvnair'
38 | developerName = 'Alok Nair'
39 | developerEmail = 'alokvnair@gmail.com'
40 | }
41 |
42 | apply from: 'https://raw.githubusercontent.com/attwellbrian/JCenter/master/installv1.gradle'
43 | apply from: 'https://raw.githubusercontent.com/attwellbrian/JCenter/master/bintrayv1.gradle'
--------------------------------------------------------------------------------
/patheffecttextview/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 C:/Users/DK/Documents/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 |
--------------------------------------------------------------------------------
/patheffecttextview/src/androidTest/java/com/alokvnair/patheffect/ApplicationTest.java:
--------------------------------------------------------------------------------
1 | package com.alokvnair.patheffect;
2 |
3 | import android.app.Application;
4 | import android.test.ApplicationTestCase;
5 |
6 | /**
7 | * Testing Fundamentals
8 | */
9 | public class ApplicationTest extends ApplicationTestCase {
10 | public ApplicationTest() {
11 | super(Application.class);
12 | }
13 | }
--------------------------------------------------------------------------------
/patheffecttextview/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/patheffecttextview/src/main/java/com/alokvnair/patheffect/MatchPath.java:
--------------------------------------------------------------------------------
1 | package com.alokvnair.patheffect;
2 |
3 | import android.util.SparseArray;
4 |
5 | import java.util.ArrayList;
6 |
7 | /**
8 | * This code comes from https://github.com/liaohuqiu/android-Ultra-Pull-To-Refresh/blob/master/ptr-lib/src/in/srain/cube/views/ptr/header/StoreHousePath.java
9 | */
10 | public class MatchPath {
11 |
12 | private static final SparseArray sPointList;
13 |
14 | static {
15 | sPointList = new SparseArray<>();
16 | float[][] LETTERS = new float[][]{
17 | new float[]{
18 | // A
19 | 24, 0, 1, 22,
20 | 1, 22, 1, 72,
21 | 24, 0, 47, 22,
22 | 47, 22, 47, 72,
23 | 1, 48, 47, 48
24 | },
25 |
26 | new float[]{
27 | // B
28 | 0, 0, 0, 72,
29 | 0, 0, 37, 0,
30 | 37, 0, 47, 11,
31 | 47, 11, 47, 26,
32 | 47, 26, 38, 36,
33 | 38, 36, 0, 36,
34 | 38, 36, 47, 46,
35 | 47, 46, 47, 61,
36 | 47, 61, 38, 71,
37 | 37, 72, 0, 72,
38 | },
39 |
40 | new float[]{
41 | // C
42 | 47, 0, 0, 0,
43 | 0, 0, 0, 72,
44 | 0, 72, 47, 72,
45 | },
46 |
47 | new float[]{
48 | // D
49 | 0, 0, 0, 72,
50 | 0, 0, 24, 0,
51 | 24, 0, 47, 22,
52 | 47, 22, 47, 48,
53 | 47, 48, 23, 72,
54 | 23, 72, 0, 72,
55 | },
56 |
57 | new float[]{
58 | // E
59 | 0, 0, 0, 72,
60 | 0, 0, 47, 0,
61 | 0, 36, 37, 36,
62 | 0, 72, 47, 72,
63 | },
64 |
65 | new float[]{
66 | // F
67 | 0, 0, 0, 72,
68 | 0, 0, 47, 0,
69 | 0, 36, 37, 36,
70 | },
71 |
72 | new float[]{
73 | // G
74 | 47, 23, 47, 0,
75 | 47, 0, 0, 0,
76 | 0, 0, 0, 72,
77 | 0, 72, 47, 72,
78 | 47, 72, 47, 48,
79 | 47, 48, 24, 48,
80 | },
81 |
82 | new float[]{
83 | // H
84 | 0, 0, 0, 72,
85 | 0, 36, 47, 36,
86 | 47, 0, 47, 72,
87 | },
88 |
89 | new float[]{
90 | // I
91 | 0, 0, 47, 0,
92 | 24, 0, 24, 72,
93 | 0, 72, 47, 72,
94 | },
95 |
96 | new float[]{
97 | // J
98 | 47, 0, 47, 72,
99 | 47, 72, 24, 72,
100 | 24, 72, 0, 48,
101 | },
102 |
103 | new float[]{
104 | // K
105 | 0, 0, 0, 72,
106 | 47, 0, 3, 33,
107 | 3, 38, 47, 72,
108 | },
109 |
110 | new float[]{
111 | // L
112 | 0, 0, 0, 72,
113 | 0, 72, 47, 72,
114 | },
115 |
116 | new float[]{
117 | // M
118 | 0, 0, 0, 72,
119 | 0, 0, 24, 23,
120 | 24, 23, 47, 0,
121 | 47, 0, 47, 72,
122 | },
123 |
124 | new float[]{
125 | // N
126 | 0, 0, 0, 72,
127 | 0, 0, 47, 72,
128 | 47, 72, 47, 0,
129 | },
130 |
131 | new float[]{
132 | // O
133 | 0, 0, 0, 72,
134 | 0, 72, 47, 72,
135 | 47, 72, 47, 0,
136 | 47, 0, 0, 0,
137 | },
138 |
139 | new float[]{
140 | // P
141 | 0, 0, 0, 72,
142 | 0, 0, 47, 0,
143 | 47, 0, 47, 36,
144 | 47, 36, 0, 36,
145 | },
146 |
147 | new float[]{
148 | // Q
149 | 0, 0, 0, 72,
150 | 0, 72, 23, 72,
151 | 23, 72, 47, 48,
152 | 47, 48, 47, 0,
153 | 47, 0, 0, 0,
154 | 24, 28, 47, 71,
155 | },
156 |
157 | new float[]{
158 | // R
159 | 0, 0, 0, 72,
160 | 0, 0, 47, 0,
161 | 47, 0, 47, 36,
162 | 47, 36, 0, 36,
163 | 0, 37, 47, 72,
164 | },
165 |
166 | new float[]{
167 | // S
168 | 47, 0, 0, 0,
169 | 0, 0, 0, 36,
170 | 0, 36, 47, 36,
171 | 47, 36, 47, 72,
172 | 47, 72, 0, 72,
173 | },
174 |
175 | new float[]{
176 | // T
177 | 0, 0, 47, 0,
178 | 24, 0, 24, 72,
179 | },
180 |
181 | new float[]{
182 | // U
183 | 0, 0, 0, 72,
184 | 0, 72, 47, 72,
185 | 47, 72, 47, 0,
186 | },
187 |
188 | new float[]{
189 | // V
190 | 0, 0, 24, 72,
191 | 24, 72, 47, 0,
192 | },
193 |
194 | new float[]{
195 | // W
196 | 0, 0, 0, 72,
197 | 0, 72, 24, 49,
198 | 24, 49, 47, 72,
199 | 47, 72, 47, 0
200 | },
201 |
202 | new float[]{
203 | // X
204 | 0, 0, 47, 72,
205 | 47, 0, 0, 72
206 | },
207 |
208 | new float[]{
209 | // Y
210 | 0, 0, 24, 23,
211 | 47, 0, 24, 23,
212 | 24, 23, 24, 72
213 | },
214 |
215 | new float[]{
216 | // Z
217 | 0, 0, 47, 0,
218 | 47, 0, 0, 72,
219 | 0, 72, 47, 72
220 | },
221 | };
222 | final float[][] NUMBERS = new float[][]{
223 | new float[]{
224 | // 0
225 | 0, 0, 0, 72,
226 | 0, 72, 47, 72,
227 | 47, 72, 47, 0,
228 | 47, 0, 0, 0,
229 | },
230 | new float[]{
231 | // 1
232 | 24, 0, 24, 72,
233 | },
234 |
235 | new float[]{
236 | // 2
237 | 0, 0, 47, 0,
238 | 47, 0, 47, 36,
239 | 47, 36, 0, 36,
240 | 0, 36, 0, 72,
241 | 0, 72, 47, 72
242 | },
243 |
244 | new float[]{
245 | // 3
246 | 0, 0, 47, 0,
247 | 47, 0, 47, 36,
248 | 47, 36, 0, 36,
249 | 47, 36, 47, 72,
250 | 47, 72, 0, 72,
251 | },
252 |
253 | new float[]{
254 | // 4
255 | 0, 0, 0, 36,
256 | 0, 36, 47, 36,
257 | 47, 0, 47, 72,
258 | },
259 |
260 | new float[]{
261 | // 5
262 | 0, 0, 0, 36,
263 | 0, 36, 47, 36,
264 | 47, 36, 47, 72,
265 | 47, 72, 0, 72,
266 | 0, 0, 47, 0
267 | },
268 |
269 | new float[]{
270 | // 6
271 | 0, 0, 0, 72,
272 | 0, 72, 47, 72,
273 | 47, 72, 47, 36,
274 | 47, 36, 0, 36
275 | },
276 |
277 | new float[]{
278 | // 7
279 | 0, 0, 47, 0,
280 | 47, 0, 47, 72
281 | },
282 |
283 | new float[]{
284 | // 8
285 | 0, 0, 0, 72,
286 | 0, 72, 47, 72,
287 | 47, 72, 47, 0,
288 | 47, 0, 0, 0,
289 | 0, 36, 47, 36
290 | },
291 |
292 | new float[]{
293 | // 9
294 | 47, 0, 0, 0,
295 | 0, 0, 0, 36,
296 | 0, 36, 47, 36,
297 | 47, 0, 47, 72,
298 | }
299 | };
300 | // A - Z
301 | for (int i = 0; i < LETTERS.length; i++) {
302 | sPointList.append(i + 65, LETTERS[i]);
303 | }
304 | // a - z
305 | for (int i = 0; i < LETTERS.length; i++) {
306 | sPointList.append(i + 65 + 32, LETTERS[i]);
307 | }
308 | // 0 - 9
309 | for (int i = 0; i < NUMBERS.length; i++) {
310 | sPointList.append(i + 48, NUMBERS[i]);
311 | }
312 | // blank
313 | addChar(' ', new float[]{});
314 | // -
315 | addChar('-', new float[]{
316 | 0, 36, 47, 36
317 | });
318 | // .
319 | addChar('.', new float[]{
320 | 24, 60, 24, 72
321 | });
322 | }
323 |
324 | public static void addChar(char c, float[] points) {
325 | sPointList.append(c, points);
326 | }
327 |
328 | public static ArrayList getPath(String str) {
329 | return getPath(str, 1, 14);
330 | }
331 |
332 | /**
333 | * @param str
334 | * @param scale
335 | * @param gapBetweenLetter
336 | * @return ArrayList of float[] {x1, y1, x2, y2}
337 | */
338 | public static ArrayList getPath(String str, float scale, int gapBetweenLetter) {
339 | ArrayList list = new ArrayList<>();
340 | float offsetForWidth = 0;
341 | for (int i = 0; i < str.length(); i++) {
342 | int pos = str.charAt(i);
343 | int key = sPointList.indexOfKey(pos);
344 | if (key == -1) {
345 | continue;
346 | }
347 | float[] points = sPointList.get(pos);
348 | int pointCount = points.length / 4;
349 |
350 | for (int j = 0; j < pointCount; j++) {
351 | float[] line = new float[4];
352 | for (int k = 0; k < 4; k++) {
353 | float l = points[j * 4 + k];
354 | // x
355 | if (k % 2 == 0) {
356 | line[k] = (l + offsetForWidth) * scale;
357 | }
358 | // y
359 | else {
360 | line[k] = l * scale;
361 | }
362 | }
363 | list.add(line);
364 | }
365 | offsetForWidth += 57 + gapBetweenLetter;
366 | }
367 | return list;
368 | }
369 | }
370 |
--------------------------------------------------------------------------------
/patheffecttextview/src/main/java/com/alokvnair/patheffect/PathEffectTextView.java:
--------------------------------------------------------------------------------
1 | package com.alokvnair.patheffect;
2 |
3 | import android.animation.ObjectAnimator;
4 | import android.content.Context;
5 | import android.content.res.TypedArray;
6 | import android.graphics.Canvas;
7 | import android.graphics.Color;
8 | import android.graphics.Paint;
9 | import android.graphics.Path;
10 | import android.graphics.PathMeasure;
11 | import android.util.AttributeSet;
12 | import android.view.View;
13 |
14 | import java.util.ArrayList;
15 |
16 | public class PathEffectTextView extends View {
17 |
18 | private static final float BASE_SQUARE_UNIT = 72f;
19 | private static final int DEFAULT_COLOR = Color.rgb(0, 0, 0);
20 | private String mText = "";
21 | private float mPhase;
22 | private TYPE mType = TYPE.SINGLE;
23 | private float mScaleFactor = 1.0f;
24 | private ArrayList mDatas;
25 | private ArrayList mPaths = new ArrayList<>();
26 | private Paint mPaint = new Paint();
27 | private final Object mSvgLock = new Object();
28 |
29 | public enum TYPE {
30 | SINGLE, MULTIPLE
31 | }
32 |
33 | public PathEffectTextView(Context context, AttributeSet attrs) {
34 | super(context, attrs);
35 |
36 | final TypedArray attributes = context.getTheme().obtainStyledAttributes(attrs, R.styleable.PathEffectTextView,
37 | 0, 0);
38 | mPaint.setColor(attributes.getColor(R.styleable.PathEffectTextView_text_color, DEFAULT_COLOR));
39 | mPaint.setStrokeWidth(attributes.getFloat(R.styleable.PathEffectTextView_stroke_width, 2));
40 | mPaint.setStyle(Paint.Style.STROKE);
41 | boolean isMultiple = attributes.getBoolean(R.styleable.PathEffectTextView_multiple, false);
42 |
43 | if (isMultiple)
44 | mType = TYPE.MULTIPLE;
45 | else
46 | mType = TYPE.SINGLE;
47 |
48 | attributes.recycle();
49 |
50 | }
51 |
52 |
53 | @Override
54 | protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
55 | setMeasuredDimension(measureWidth(widthMeasureSpec),
56 | measureHeight(heightMeasureSpec));
57 |
58 | }
59 |
60 | public void init(String text) {
61 | if (text == null || text.length() == 0)
62 | return;
63 |
64 | requestLayout();
65 | invalidate();
66 |
67 | mText = text;
68 | mDatas = MatchPath.getPath(mText);
69 | ObjectAnimator mSvgAnimator = ObjectAnimator.ofFloat(this, "phase", 0.0f, 1.0f).setDuration(3000);
70 | mSvgAnimator.start();
71 | }
72 |
73 |
74 | @Override
75 | protected void onDraw(Canvas canvas) {
76 | super.onDraw(canvas);
77 | if (mPaths == null)
78 | return;
79 | synchronized (mSvgLock) {
80 | for (int i = 0; i < mPaths.size(); i++)
81 | canvas.drawPath(mPaths.get(i), mPaint);
82 | }
83 | }
84 |
85 |
86 | private void updatePathsPhaseLocked() {
87 | mPaths.clear();
88 | float singleFactor = mPhase * mDatas.size();
89 | for (int i = 0; i < mDatas.size(); i++) {
90 | Path path = new Path();
91 | path.moveTo(mDatas.get(i)[0], mDatas.get(i)[1]);
92 | path.lineTo(mDatas.get(i)[2], mDatas.get(i)[3]);
93 |
94 | if (mType == TYPE.MULTIPLE) {
95 | PathMeasure measure = new PathMeasure(path, false);
96 | Path dst = new Path();
97 | measure.getSegment(0.0f, mPhase * measure.getLength(), dst, true);
98 | mPaths.add(dst);
99 | } else {
100 | if (singleFactor - (i + 1) >= -0.01)
101 | mPaths.add(path);
102 | else if (i - Math.floor(singleFactor) < 0.0001) {
103 | Path dst = new Path();
104 | PathMeasure measure = new PathMeasure(path, false);
105 | measure.getSegment(0.0f, (singleFactor % 1) * measure.getLength(), dst, true);
106 | mPaths.add(dst);
107 | }
108 | }
109 |
110 | }
111 | }
112 |
113 | private int measureWidth(int measureSpec) {
114 | int result;
115 | int specMode = MeasureSpec.getMode(measureSpec);
116 | int specSize = MeasureSpec.getSize(measureSpec);
117 |
118 | if (specMode == MeasureSpec.EXACTLY) {
119 | result = specSize;
120 | } else {
121 | result = (int) (mText.length() * BASE_SQUARE_UNIT * mScaleFactor + getPaddingLeft()
122 | + getPaddingRight());
123 | if (specMode == MeasureSpec.AT_MOST) {
124 | result = Math.min(result, specSize);
125 | }
126 | }
127 |
128 | return result;
129 | }
130 |
131 | private int measureHeight(int measureSpec) {
132 | int result;
133 | int specMode = MeasureSpec.getMode(measureSpec);
134 | int specSize = MeasureSpec.getSize(measureSpec);
135 |
136 | if (specMode == MeasureSpec.EXACTLY) {
137 | result = specSize;
138 | } else {
139 | result = (int) (BASE_SQUARE_UNIT * mScaleFactor) + getPaddingTop()
140 | + getPaddingBottom();
141 | if (specMode == MeasureSpec.AT_MOST) {
142 | result = Math.min(result, specSize);
143 | }
144 | }
145 | return result;
146 | }
147 | }
148 |
--------------------------------------------------------------------------------
/patheffecttextview/src/main/res/values/attrs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/screens/pathEffective.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jiyuren/PathEffectTextView/4f5e779ee98f8f6c98e2dc0088420ed6e7dfb032/screens/pathEffective.gif
--------------------------------------------------------------------------------
/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':app', ':patheffecttextview'
2 |
--------------------------------------------------------------------------------