├── .gitignore
├── LICENSE.txt
├── README.md
├── build.gradle
├── gradle.properties
├── gradle
├── gradle
│ └── wrapper
│ │ ├── gradle-wrapper.jar
│ │ └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
├── library
├── build.gradle
├── gradle.properties
└── src
│ └── main
│ ├── AndroidManifest.xml
│ ├── java
│ └── me
│ │ └── grantland
│ │ └── widget
│ │ ├── AutofitHelper.java
│ │ ├── AutofitLayout.java
│ │ └── AutofitTextView.java
│ └── res
│ └── values
│ └── attrs.xml
├── sample
├── build.gradle
└── src
│ └── main
│ ├── AndroidManifest.xml
│ ├── java
│ └── me
│ │ └── grantland
│ │ └── autofittextview
│ │ └── sample
│ │ └── SampleActivity.java
│ └── res
│ ├── drawable-hdpi
│ └── ic_launcher.png
│ ├── drawable-ldpi
│ └── ic_launcher.png
│ ├── drawable-mdpi
│ └── ic_launcher.png
│ ├── drawable-xhdpi
│ └── ic_launcher.png
│ ├── layout-land
│ └── main.xml
│ ├── layout
│ └── main.xml
│ ├── values-v11
│ └── styles.xml
│ ├── values-v14
│ └── styles.xml
│ └── values
│ ├── strings.xml
│ └── styles.xml
├── settings.gradle
└── website
└── static
└── autofittextview.gif
/.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 | # Android Studio
19 | .idea
20 | *.iml
21 | *.ipr
22 | *.iws
23 | classes
24 | gen-external-apklibs
25 |
26 | # Gradle
27 | .gradle
28 | build
--------------------------------------------------------------------------------
/LICENSE.txt:
--------------------------------------------------------------------------------
1 |
2 | Apache License
3 | Version 2.0, January 2004
4 | http://www.apache.org/licenses/
5 |
6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
7 |
8 | 1. Definitions.
9 |
10 | "License" shall mean the terms and conditions for use, reproduction,
11 | and distribution as defined by Sections 1 through 9 of this document.
12 |
13 | "Licensor" shall mean the copyright owner or entity authorized by
14 | the copyright owner that is granting the License.
15 |
16 | "Legal Entity" shall mean the union of the acting entity and all
17 | other entities that control, are controlled by, or are under common
18 | control with that entity. For the purposes of this definition,
19 | "control" means (i) the power, direct or indirect, to cause the
20 | direction or management of such entity, whether by contract or
21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the
22 | outstanding shares, or (iii) beneficial ownership of such entity.
23 |
24 | "You" (or "Your") shall mean an individual or Legal Entity
25 | exercising permissions granted by this License.
26 |
27 | "Source" form shall mean the preferred form for making modifications,
28 | including but not limited to software source code, documentation
29 | source, and configuration files.
30 |
31 | "Object" form shall mean any form resulting from mechanical
32 | transformation or translation of a Source form, including but
33 | not limited to compiled object code, generated documentation,
34 | and conversions to other media types.
35 |
36 | "Work" shall mean the work of authorship, whether in Source or
37 | Object form, made available under the License, as indicated by a
38 | copyright notice that is included in or attached to the work
39 | (an example is provided in the Appendix below).
40 |
41 | "Derivative Works" shall mean any work, whether in Source or Object
42 | form, that is based on (or derived from) the Work and for which the
43 | editorial revisions, annotations, elaborations, or other modifications
44 | represent, as a whole, an original work of authorship. For the purposes
45 | of this License, Derivative Works shall not include works that remain
46 | separable from, or merely link (or bind by name) to the interfaces of,
47 | the Work and Derivative Works thereof.
48 |
49 | "Contribution" shall mean any work of authorship, including
50 | the original version of the Work and any modifications or additions
51 | to that Work or Derivative Works thereof, that is intentionally
52 | submitted to Licensor for inclusion in the Work by the copyright owner
53 | or by an individual or Legal Entity authorized to submit on behalf of
54 | the copyright owner. For the purposes of this definition, "submitted"
55 | means any form of electronic, verbal, or written communication sent
56 | to the Licensor or its representatives, including but not limited to
57 | communication on electronic mailing lists, source code control systems,
58 | and issue tracking systems that are managed by, or on behalf of, the
59 | Licensor for the purpose of discussing and improving the Work, but
60 | excluding communication that is conspicuously marked or otherwise
61 | designated in writing by the copyright owner as "Not a Contribution."
62 |
63 | "Contributor" shall mean Licensor and any individual or Legal Entity
64 | on behalf of whom a Contribution has been received by Licensor and
65 | subsequently incorporated within the Work.
66 |
67 | 2. Grant of Copyright License. Subject to the terms and conditions of
68 | this License, each Contributor hereby grants to You a perpetual,
69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
70 | copyright license to reproduce, prepare Derivative Works of,
71 | publicly display, publicly perform, sublicense, and distribute the
72 | Work and such Derivative Works in Source or Object form.
73 |
74 | 3. Grant of Patent License. Subject to the terms and conditions of
75 | this License, each Contributor hereby grants to You a perpetual,
76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
77 | (except as stated in this section) patent license to make, have made,
78 | use, offer to sell, sell, import, and otherwise transfer the Work,
79 | where such license applies only to those patent claims licensable
80 | by such Contributor that are necessarily infringed by their
81 | Contribution(s) alone or by combination of their Contribution(s)
82 | with the Work to which such Contribution(s) was submitted. If You
83 | institute patent litigation against any entity (including a
84 | cross-claim or counterclaim in a lawsuit) alleging that the Work
85 | or a Contribution incorporated within the Work constitutes direct
86 | or contributory patent infringement, then any patent licenses
87 | granted to You under this License for that Work shall terminate
88 | as of the date such litigation is filed.
89 |
90 | 4. Redistribution. You may reproduce and distribute copies of the
91 | Work or Derivative Works thereof in any medium, with or without
92 | modifications, and in Source or Object form, provided that You
93 | meet the following conditions:
94 |
95 | (a) You must give any other recipients of the Work or
96 | Derivative Works a copy of this License; and
97 |
98 | (b) You must cause any modified files to carry prominent notices
99 | stating that You changed the files; and
100 |
101 | (c) You must retain, in the Source form of any Derivative Works
102 | that You distribute, all copyright, patent, trademark, and
103 | attribution notices from the Source form of the Work,
104 | excluding those notices that do not pertain to any part of
105 | the Derivative Works; and
106 |
107 | (d) If the Work includes a "NOTICE" text file as part of its
108 | distribution, then any Derivative Works that You distribute must
109 | include a readable copy of the attribution notices contained
110 | within such NOTICE file, excluding those notices that do not
111 | pertain to any part of the Derivative Works, in at least one
112 | of the following places: within a NOTICE text file distributed
113 | as part of the Derivative Works; within the Source form or
114 | documentation, if provided along with the Derivative Works; or,
115 | within a display generated by the Derivative Works, if and
116 | wherever such third-party notices normally appear. The contents
117 | of the NOTICE file are for informational purposes only and
118 | do not modify the License. You may add Your own attribution
119 | notices within Derivative Works that You distribute, alongside
120 | or as an addendum to the NOTICE text from the Work, provided
121 | that such additional attribution notices cannot be construed
122 | as modifying the License.
123 |
124 | You may add Your own copyright statement to Your modifications and
125 | may provide additional or different license terms and conditions
126 | for use, reproduction, or distribution of Your modifications, or
127 | for any such Derivative Works as a whole, provided Your use,
128 | reproduction, and distribution of the Work otherwise complies with
129 | the conditions stated in this License.
130 |
131 | 5. Submission of Contributions. Unless You explicitly state otherwise,
132 | any Contribution intentionally submitted for inclusion in the Work
133 | by You to the Licensor shall be under the terms and conditions of
134 | this License, without any additional terms or conditions.
135 | Notwithstanding the above, nothing herein shall supersede or modify
136 | the terms of any separate license agreement you may have executed
137 | with Licensor regarding such Contributions.
138 |
139 | 6. Trademarks. This License does not grant permission to use the trade
140 | names, trademarks, service marks, or product names of the Licensor,
141 | except as required for reasonable and customary use in describing the
142 | origin of the Work and reproducing the content of the NOTICE file.
143 |
144 | 7. Disclaimer of Warranty. Unless required by applicable law or
145 | agreed to in writing, Licensor provides the Work (and each
146 | Contributor provides its Contributions) on an "AS IS" BASIS,
147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
148 | implied, including, without limitation, any warranties or conditions
149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
150 | PARTICULAR PURPOSE. You are solely responsible for determining the
151 | appropriateness of using or redistributing the Work and assume any
152 | risks associated with Your exercise of permissions under this License.
153 |
154 | 8. Limitation of Liability. In no event and under no legal theory,
155 | whether in tort (including negligence), contract, or otherwise,
156 | unless required by applicable law (such as deliberate and grossly
157 | negligent acts) or agreed to in writing, shall any Contributor be
158 | liable to You for damages, including any direct, indirect, special,
159 | incidental, or consequential damages of any character arising as a
160 | result of this License or out of the use or inability to use the
161 | Work (including but not limited to damages for loss of goodwill,
162 | work stoppage, computer failure or malfunction, or any and all
163 | other commercial damages or losses), even if such Contributor
164 | has been advised of the possibility of such damages.
165 |
166 | 9. Accepting Warranty or Additional Liability. While redistributing
167 | the Work or Derivative Works thereof, You may choose to offer,
168 | and charge a fee for, acceptance of support, warranty, indemnity,
169 | or other liability obligations and/or rights consistent with this
170 | License. However, in accepting such obligations, You may act only
171 | on Your own behalf and on Your sole responsibility, not on behalf
172 | of any other Contributor, and only if You agree to indemnify,
173 | defend, and hold each Contributor harmless for any liability
174 | incurred by, or claims asserted against, such Contributor by reason
175 | of your accepting any such warranty or additional liability.
176 |
177 | END OF TERMS AND CONDITIONS
178 |
179 | APPENDIX: How to apply the Apache License to your work.
180 |
181 | To apply the Apache License to your work, attach the following
182 | boilerplate notice, with the fields enclosed by brackets "[]"
183 | replaced with your own identifying information. (Don't include
184 | the brackets!) The text should be enclosed in the appropriate
185 | comment syntax for the file format. We also recommend that a
186 | file or class name and description of purpose be included on the
187 | same "printed page" as the copyright notice for easier
188 | identification within third-party archives.
189 |
190 | Copyright [yyyy] [name of copyright owner]
191 |
192 | Licensed under the Apache License, Version 2.0 (the "License");
193 | you may not use this file except in compliance with the License.
194 | You may obtain a copy of the License at
195 |
196 | http://www.apache.org/licenses/LICENSE-2.0
197 |
198 | Unless required by applicable law or agreed to in writing, software
199 | distributed under the License is distributed on an "AS IS" BASIS,
200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
201 | See the License for the specific language governing permissions and
202 | limitations under the License.
203 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # AutoFitTextView
2 |
3 | [](https://maven-badges.herokuapp.com/maven-central/me.grantland/autofittextview)
4 |
5 | A TextView that automatically resizes text to fit perfectly within its bounds.
6 |
7 | 
8 |
9 |
10 | ## Usage
11 |
12 | ```cson
13 | dependencies {
14 | compile 'me.grantland:autofittextview:0.2.+'
15 | }
16 | ```
17 |
18 | Enable any View extending TextView in code:
19 |
20 | ```java
21 | AutofitHelper.create(textView);
22 | ```
23 |
24 | Enable any View extending TextView in XML:
25 |
26 | ```xml
27 |
31 |
36 |
37 | ```
38 |
39 | Use the built in Widget in code or XML:
40 | ```xml
41 |
55 | ```
56 |
57 |
58 | ## License
59 |
60 | Copyright 2014 Grantland Chew
61 |
62 | Licensed under the Apache License, Version 2.0 (the "License");
63 | you may not use this file except in compliance with the License.
64 | You may obtain a copy of the License at
65 |
66 | http://www.apache.org/licenses/LICENSE-2.0
67 |
68 | Unless required by applicable law or agreed to in writing, software
69 | distributed under the License is distributed on an "AS IS" BASIS,
70 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
71 | See the License for the specific language governing permissions and
72 | limitations under the License.
73 |
--------------------------------------------------------------------------------
/build.gradle:
--------------------------------------------------------------------------------
1 | // Top-level build file where you can add configuration options common to all sub-projects/modules.
2 | buildscript {
3 | repositories {
4 | mavenCentral()
5 | }
6 | dependencies {
7 | classpath 'com.android.tools.build:gradle:2.0.0'
8 | }
9 | }
10 |
11 | ext {
12 | compileSdkVersion = 23
13 | buildToolsVersion = "23.0.3"
14 |
15 | minSdkVersion = 14
16 | targetSdkVersion = 23
17 | }
18 |
--------------------------------------------------------------------------------
/gradle.properties:
--------------------------------------------------------------------------------
1 | VERSION_NAME=0.2.2-SNAPSHOT
2 | VERSION_CODE=6
3 | GROUP=me.grantland
4 |
5 | POM_DESCRIPTION=A TextView that automatically resizes text to fit perfectly within its bounds.
6 | POM_URL=https://github.com/grantland/android-autofittextview
7 | POM_SCM_URL=https://github.com/grantland/android-autofittextview
8 | POM_SCM_CONNECTION=scm:git@github.com:grantland/android-autofittextview.git
9 | POM_SCM_DEV_CONNECTION=scm:git@github.com:grantland/android-autofittextview.git
10 | POM_LICENCE_NAME=The Apache Software License, Version 2.0
11 | POM_LICENCE_URL=http://www.apache.org/licenses/LICENSE-2.0.txt
12 | POM_LICENCE_DIST=repo
13 | POM_DEVELOPER_ID=grantland
14 | POM_DEVELOPER_NAME=Grantland Chew
15 |
--------------------------------------------------------------------------------
/gradle/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/grantland/android-autofittextview/5c565aa5c3ed62aaa31140440bb526e7435e7947/gradle/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/gradle/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Tue Feb 04 11:41:21 PST 2014
2 | distributionBase=GRADLE_USER_HOME
3 | distributionPath=wrapper/dists
4 | zipStoreBase=GRADLE_USER_HOME
5 | zipStorePath=wrapper/dists
6 | distributionUrl=http\://services.gradle.org/distributions/gradle-1.10-bin.zip
7 |
--------------------------------------------------------------------------------
/gradle/gradlew:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | ##############################################################################
4 | ##
5 | ## Gradle start up script for UN*X
6 | ##
7 | ##############################################################################
8 |
9 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
10 | DEFAULT_JVM_OPTS=""
11 |
12 | APP_NAME="Gradle"
13 | APP_BASE_NAME=`basename "$0"`
14 |
15 | # Use the maximum available, or set MAX_FD != -1 to use that value.
16 | MAX_FD="maximum"
17 |
18 | warn ( ) {
19 | echo "$*"
20 | }
21 |
22 | die ( ) {
23 | echo
24 | echo "$*"
25 | echo
26 | exit 1
27 | }
28 |
29 | # OS specific support (must be 'true' or 'false').
30 | cygwin=false
31 | msys=false
32 | darwin=false
33 | case "`uname`" in
34 | CYGWIN* )
35 | cygwin=true
36 | ;;
37 | Darwin* )
38 | darwin=true
39 | ;;
40 | MINGW* )
41 | msys=true
42 | ;;
43 | esac
44 |
45 | # For Cygwin, ensure paths are in UNIX format before anything is touched.
46 | if $cygwin ; then
47 | [ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"`
48 | fi
49 |
50 | # Attempt to set APP_HOME
51 | # Resolve links: $0 may be a link
52 | PRG="$0"
53 | # Need this for relative symlinks.
54 | while [ -h "$PRG" ] ; do
55 | ls=`ls -ld "$PRG"`
56 | link=`expr "$ls" : '.*-> \(.*\)$'`
57 | if expr "$link" : '/.*' > /dev/null; then
58 | PRG="$link"
59 | else
60 | PRG=`dirname "$PRG"`"/$link"
61 | fi
62 | done
63 | SAVED="`pwd`"
64 | cd "`dirname \"$PRG\"`/" >&-
65 | APP_HOME="`pwd -P`"
66 | cd "$SAVED" >&-
67 |
68 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
69 |
70 | # Determine the Java command to use to start the JVM.
71 | if [ -n "$JAVA_HOME" ] ; then
72 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
73 | # IBM's JDK on AIX uses strange locations for the executables
74 | JAVACMD="$JAVA_HOME/jre/sh/java"
75 | else
76 | JAVACMD="$JAVA_HOME/bin/java"
77 | fi
78 | if [ ! -x "$JAVACMD" ] ; then
79 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
80 |
81 | Please set the JAVA_HOME variable in your environment to match the
82 | location of your Java installation."
83 | fi
84 | else
85 | JAVACMD="java"
86 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
87 |
88 | Please set the JAVA_HOME variable in your environment to match the
89 | location of your Java installation."
90 | fi
91 |
92 | # Increase the maximum file descriptors if we can.
93 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
94 | MAX_FD_LIMIT=`ulimit -H -n`
95 | if [ $? -eq 0 ] ; then
96 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
97 | MAX_FD="$MAX_FD_LIMIT"
98 | fi
99 | ulimit -n $MAX_FD
100 | if [ $? -ne 0 ] ; then
101 | warn "Could not set maximum file descriptor limit: $MAX_FD"
102 | fi
103 | else
104 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
105 | fi
106 | fi
107 |
108 | # For Darwin, add options to specify how the application appears in the dock
109 | if $darwin; then
110 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
111 | fi
112 |
113 | # For Cygwin, switch paths to Windows format before running java
114 | if $cygwin ; then
115 | APP_HOME=`cygpath --path --mixed "$APP_HOME"`
116 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
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 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
158 | function splitJvmOpts() {
159 | JVM_OPTS=("$@")
160 | }
161 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
162 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
163 |
164 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
165 |
--------------------------------------------------------------------------------
/gradle/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 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
12 | set DEFAULT_JVM_OPTS=
13 |
14 | set DIRNAME=%~dp0
15 | if "%DIRNAME%" == "" set DIRNAME=.
16 | set APP_BASE_NAME=%~n0
17 | set APP_HOME=%DIRNAME%
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 Windowz variants
50 |
51 | if not "%OS%" == "Windows_NT" goto win9xME_args
52 | if "%@eval[2+2]" == "4" goto 4NT_args
53 |
54 | :win9xME_args
55 | @rem Slurp the command line arguments.
56 | set CMD_LINE_ARGS=
57 | set _SKIP=2
58 |
59 | :win9xME_args_slurp
60 | if "x%~1" == "x" goto execute
61 |
62 | set CMD_LINE_ARGS=%*
63 | goto execute
64 |
65 | :4NT_args
66 | @rem Get arguments from the 4NT Shell from JP Software
67 | set CMD_LINE_ARGS=%$
68 |
69 | :execute
70 | @rem Setup the command line
71 |
72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
73 |
74 | @rem Execute Gradle
75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
76 |
77 | :end
78 | @rem End local scope for the variables with windows NT shell
79 | if "%ERRORLEVEL%"=="0" goto mainEnd
80 |
81 | :fail
82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
83 | rem the _cmd.exe /c_ return code!
84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
85 | exit /b 1
86 |
87 | :mainEnd
88 | if "%OS%"=="Windows_NT" endlocal
89 |
90 | :omega
91 |
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/grantland/android-autofittextview/5c565aa5c3ed62aaa31140440bb526e7435e7947/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Mon Apr 25 23:12:46 PDT 2016
2 | distributionBase=GRADLE_USER_HOME
3 | distributionPath=wrapper/dists
4 | zipStoreBase=GRADLE_USER_HOME
5 | zipStorePath=wrapper/dists
6 | distributionUrl=https\://services.gradle.org/distributions/gradle-2.13-bin.zip
7 |
--------------------------------------------------------------------------------
/gradlew:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
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 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
158 | function splitJvmOpts() {
159 | JVM_OPTS=("$@")
160 | }
161 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
162 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
163 |
164 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
165 |
--------------------------------------------------------------------------------
/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 | if "%@eval[2+2]" == "4" goto 4NT_args
53 |
54 | :win9xME_args
55 | @rem Slurp the command line arguments.
56 | set CMD_LINE_ARGS=
57 | set _SKIP=2
58 |
59 | :win9xME_args_slurp
60 | if "x%~1" == "x" goto execute
61 |
62 | set CMD_LINE_ARGS=%*
63 | goto execute
64 |
65 | :4NT_args
66 | @rem Get arguments from the 4NT Shell from JP Software
67 | set CMD_LINE_ARGS=%$
68 |
69 | :execute
70 | @rem Setup the command line
71 |
72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
73 |
74 | @rem Execute Gradle
75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
76 |
77 | :end
78 | @rem End local scope for the variables with windows NT shell
79 | if "%ERRORLEVEL%"=="0" goto mainEnd
80 |
81 | :fail
82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
83 | rem the _cmd.exe /c_ return code!
84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
85 | exit /b 1
86 |
87 | :mainEnd
88 | if "%OS%"=="Windows_NT" endlocal
89 |
90 | :omega
91 |
--------------------------------------------------------------------------------
/library/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.library'
2 |
3 | android {
4 | compileSdkVersion rootProject.ext.compileSdkVersion
5 | buildToolsVersion rootProject.ext.buildToolsVersion
6 |
7 | defaultConfig {
8 | minSdkVersion rootProject.ext.minSdkVersion
9 | targetSdkVersion rootProject.ext.targetSdkVersion
10 | }
11 | }
12 |
13 | apply from: 'https://raw.github.com/chrisbanes/gradle-mvn-push/master/gradle-mvn-push.gradle'
14 |
--------------------------------------------------------------------------------
/library/gradle.properties:
--------------------------------------------------------------------------------
1 | POM_NAME=Android-AutofitTextView Library
2 | POM_ARTIFACT_ID=autofittextview
3 | POM_PACKAGING=aar
--------------------------------------------------------------------------------
/library/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
--------------------------------------------------------------------------------
/library/src/main/java/me/grantland/widget/AutofitHelper.java:
--------------------------------------------------------------------------------
1 | package me.grantland.widget;
2 |
3 | import android.content.Context;
4 | import android.content.res.Resources;
5 | import android.content.res.TypedArray;
6 | import android.os.Build;
7 | import android.text.Editable;
8 | import android.text.Layout;
9 | import android.text.StaticLayout;
10 | import android.text.TextPaint;
11 | import android.text.TextWatcher;
12 | import android.text.method.SingleLineTransformationMethod;
13 | import android.text.method.TransformationMethod;
14 | import android.util.AttributeSet;
15 | import android.util.DisplayMetrics;
16 | import android.util.Log;
17 | import android.util.TypedValue;
18 | import android.view.View;
19 | import android.widget.TextView;
20 |
21 | import java.util.ArrayList;
22 |
23 | /**
24 | * A helper class to enable automatically resizing {@link TextView}`s {@code textSize} to fit
25 | * within its bounds.
26 | *
27 | * @attr ref R.styleable.AutofitTextView_sizeToFit
28 | * @attr ref R.styleable.AutofitTextView_minTextSize
29 | * @attr ref R.styleable.AutofitTextView_precision
30 | */
31 | public class AutofitHelper {
32 |
33 | private static final String TAG = "AutoFitTextHelper";
34 | private static final boolean SPEW = false;
35 |
36 | // Minimum size of the text in pixels
37 | private static final int DEFAULT_MIN_TEXT_SIZE = 8; //sp
38 | // How precise we want to be when reaching the target textWidth size
39 | private static final float DEFAULT_PRECISION = 0.5f;
40 |
41 | /**
42 | * Creates a new instance of {@code AutofitHelper} that wraps a {@link TextView} and enables
43 | * automatically sizing the text to fit.
44 | */
45 | public static AutofitHelper create(TextView view) {
46 | return create(view, null, 0);
47 | }
48 |
49 | /**
50 | * Creates a new instance of {@code AutofitHelper} that wraps a {@link TextView} and enables
51 | * automatically sizing the text to fit.
52 | */
53 | public static AutofitHelper create(TextView view, AttributeSet attrs) {
54 | return create(view, attrs, 0);
55 | }
56 |
57 | /**
58 | * Creates a new instance of {@code AutofitHelper} that wraps a {@link TextView} and enables
59 | * automatically sizing the text to fit.
60 | */
61 | public static AutofitHelper create(TextView view, AttributeSet attrs, int defStyle) {
62 | AutofitHelper helper = new AutofitHelper(view);
63 | boolean sizeToFit = true;
64 | if (attrs != null) {
65 | Context context = view.getContext();
66 | int minTextSize = (int) helper.getMinTextSize();
67 | float precision = helper.getPrecision();
68 |
69 | TypedArray ta = context.obtainStyledAttributes(
70 | attrs,
71 | R.styleable.AutofitTextView,
72 | defStyle,
73 | 0);
74 | sizeToFit = ta.getBoolean(R.styleable.AutofitTextView_sizeToFit, sizeToFit);
75 | minTextSize = ta.getDimensionPixelSize(R.styleable.AutofitTextView_minTextSize,
76 | minTextSize);
77 | precision = ta.getFloat(R.styleable.AutofitTextView_precision, precision);
78 | ta.recycle();
79 |
80 | helper.setMinTextSize(TypedValue.COMPLEX_UNIT_PX, minTextSize)
81 | .setPrecision(precision);
82 | }
83 | helper.setEnabled(sizeToFit);
84 |
85 | return helper;
86 | }
87 |
88 | /**
89 | * Re-sizes the textSize of the TextView so that the text fits within the bounds of the View.
90 | */
91 | private static void autofit(TextView view, TextPaint paint, float minTextSize, float maxTextSize,
92 | int maxLines, float precision) {
93 | if (maxLines <= 0 || maxLines == Integer.MAX_VALUE) {
94 | // Don't auto-size since there's no limit on lines.
95 | return;
96 | }
97 |
98 | int targetWidth = view.getWidth() - view.getPaddingLeft() - view.getPaddingRight();
99 | if (targetWidth <= 0) {
100 | return;
101 | }
102 |
103 | CharSequence text = view.getText();
104 | TransformationMethod method = view.getTransformationMethod();
105 | if (method != null) {
106 | text = method.getTransformation(text, view);
107 | }
108 |
109 | Context context = view.getContext();
110 | Resources r = Resources.getSystem();
111 | DisplayMetrics displayMetrics;
112 |
113 | float size = maxTextSize;
114 | float high = size;
115 | float low = 0;
116 |
117 | if (context != null) {
118 | r = context.getResources();
119 | }
120 | displayMetrics = r.getDisplayMetrics();
121 |
122 | paint.set(view.getPaint());
123 | paint.setTextSize(size);
124 |
125 | if ((maxLines == 1 && paint.measureText(text, 0, text.length()) > targetWidth)
126 | || getLineCount(text, paint, size, targetWidth, displayMetrics) > maxLines) {
127 | size = getAutofitTextSize(text, paint, targetWidth, maxLines, low, high, precision,
128 | displayMetrics);
129 | }
130 |
131 | if (size < minTextSize) {
132 | size = minTextSize;
133 | }
134 |
135 | view.setTextSize(TypedValue.COMPLEX_UNIT_PX, size);
136 | }
137 |
138 | /**
139 | * Recursive binary search to find the best size for the text.
140 | */
141 | private static float getAutofitTextSize(CharSequence text, TextPaint paint,
142 | float targetWidth, int maxLines, float low, float high, float precision,
143 | DisplayMetrics displayMetrics) {
144 | float mid = (low + high) / 2.0f;
145 | int lineCount = 1;
146 | StaticLayout layout = null;
147 |
148 | paint.setTextSize(TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_PX, mid,
149 | displayMetrics));
150 |
151 | if (maxLines != 1) {
152 | layout = new StaticLayout(text, paint, (int)targetWidth, Layout.Alignment.ALIGN_NORMAL,
153 | 1.0f, 0.0f, true);
154 | lineCount = layout.getLineCount();
155 | }
156 |
157 | if (SPEW) Log.d(TAG, "low=" + low + " high=" + high + " mid=" + mid +
158 | " target=" + targetWidth + " maxLines=" + maxLines + " lineCount=" + lineCount);
159 |
160 | if (lineCount > maxLines) {
161 | // For the case that `text` has more newline characters than `maxLines`.
162 | if ((high - low) < precision) {
163 | return low;
164 | }
165 | return getAutofitTextSize(text, paint, targetWidth, maxLines, low, mid, precision,
166 | displayMetrics);
167 | }
168 | else if (lineCount < maxLines) {
169 | return getAutofitTextSize(text, paint, targetWidth, maxLines, mid, high, precision,
170 | displayMetrics);
171 | }
172 | else {
173 | float maxLineWidth = 0;
174 | if (maxLines == 1) {
175 | maxLineWidth = paint.measureText(text, 0, text.length());
176 | } else {
177 | for (int i = 0; i < lineCount; i++) {
178 | if (layout.getLineWidth(i) > maxLineWidth) {
179 | maxLineWidth = layout.getLineWidth(i);
180 | }
181 | }
182 | }
183 |
184 | if ((high - low) < precision) {
185 | return low;
186 | } else if (maxLineWidth > targetWidth) {
187 | return getAutofitTextSize(text, paint, targetWidth, maxLines, low, mid, precision,
188 | displayMetrics);
189 | } else if (maxLineWidth < targetWidth) {
190 | return getAutofitTextSize(text, paint, targetWidth, maxLines, mid, high, precision,
191 | displayMetrics);
192 | } else {
193 | return mid;
194 | }
195 | }
196 | }
197 |
198 | private static int getLineCount(CharSequence text, TextPaint paint, float size, float width,
199 | DisplayMetrics displayMetrics) {
200 | paint.setTextSize(TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_PX, size,
201 | displayMetrics));
202 | StaticLayout layout = new StaticLayout(text, paint, (int)width,
203 | Layout.Alignment.ALIGN_NORMAL, 1.0f, 0.0f, true);
204 | return layout.getLineCount();
205 | }
206 |
207 | private static int getMaxLines(TextView view) {
208 | int maxLines = -1; // No limit (Integer.MAX_VALUE also means no limit)
209 |
210 | TransformationMethod method = view.getTransformationMethod();
211 | if (method != null && method instanceof SingleLineTransformationMethod) {
212 | maxLines = 1;
213 | }
214 | else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
215 | // setMaxLines() and getMaxLines() are only available on android-16+
216 | maxLines = view.getMaxLines();
217 | }
218 |
219 | return maxLines;
220 | }
221 |
222 | // Attributes
223 | private TextView mTextView;
224 | private TextPaint mPaint;
225 | /**
226 | * Original textSize of the TextView.
227 | */
228 | private float mTextSize;
229 |
230 | private int mMaxLines;
231 | private float mMinTextSize;
232 | private float mMaxTextSize;
233 | private float mPrecision;
234 |
235 | private boolean mEnabled;
236 | private boolean mIsAutofitting;
237 |
238 | private ArrayList mListeners;
239 |
240 | private TextWatcher mTextWatcher = new AutofitTextWatcher();
241 |
242 | private View.OnLayoutChangeListener mOnLayoutChangeListener =
243 | new AutofitOnLayoutChangeListener();
244 |
245 | private AutofitHelper(TextView view) {
246 | final Context context = view.getContext();
247 | float scaledDensity = context.getResources().getDisplayMetrics().scaledDensity;
248 |
249 | mTextView = view;
250 | mPaint = new TextPaint();
251 | setRawTextSize(view.getTextSize());
252 |
253 | mMaxLines = getMaxLines(view);
254 | mMinTextSize = scaledDensity * DEFAULT_MIN_TEXT_SIZE;
255 | mMaxTextSize = mTextSize;
256 | mPrecision = DEFAULT_PRECISION;
257 | }
258 |
259 | /**
260 | * Adds an {@link OnTextSizeChangeListener} to the list of those whose methods are called
261 | * whenever the {@link TextView}'s {@code textSize} changes.
262 | */
263 | public AutofitHelper addOnTextSizeChangeListener(OnTextSizeChangeListener listener) {
264 | if (mListeners == null) {
265 | mListeners = new ArrayList();
266 | }
267 | mListeners.add(listener);
268 | return this;
269 | }
270 |
271 | /**
272 | * Removes the specified {@link OnTextSizeChangeListener} from the list of those whose methods
273 | * are called whenever the {@link TextView}'s {@code textSize} changes.
274 | */
275 | public AutofitHelper removeOnTextSizeChangeListener(OnTextSizeChangeListener listener) {
276 | if (mListeners != null) {
277 | mListeners.remove(listener);
278 | }
279 | return this;
280 | }
281 |
282 | /**
283 | * Returns the amount of precision used to calculate the correct text size to fit within its
284 | * bounds.
285 | */
286 | public float getPrecision() {
287 | return mPrecision;
288 | }
289 |
290 | /**
291 | * Set the amount of precision used to calculate the correct text size to fit within its
292 | * bounds. Lower precision is more precise and takes more time.
293 | *
294 | * @param precision The amount of precision.
295 | */
296 | public AutofitHelper setPrecision(float precision) {
297 | if (mPrecision != precision) {
298 | mPrecision = precision;
299 |
300 | autofit();
301 | }
302 | return this;
303 | }
304 |
305 | /**
306 | * Returns the minimum size (in pixels) of the text.
307 | */
308 | public float getMinTextSize() {
309 | return mMinTextSize;
310 | }
311 |
312 | /**
313 | * Set the minimum text size to the given value, interpreted as "scaled pixel" units. This size
314 | * is adjusted based on the current density and user font size preference.
315 | *
316 | * @param size The scaled pixel size.
317 | *
318 | * @attr ref me.grantland.R.styleable#AutofitTextView_minTextSize
319 | */
320 | public AutofitHelper setMinTextSize(float size) {
321 | return setMinTextSize(TypedValue.COMPLEX_UNIT_SP, size);
322 | }
323 |
324 | /**
325 | * Set the minimum text size to a given unit and value. See TypedValue for the possible
326 | * dimension units.
327 | *
328 | * @param unit The desired dimension unit.
329 | * @param size The desired size in the given units.
330 | *
331 | * @attr ref me.grantland.R.styleable#AutofitTextView_minTextSize
332 | */
333 | public AutofitHelper setMinTextSize(int unit, float size) {
334 | Context context = mTextView.getContext();
335 | Resources r = Resources.getSystem();
336 |
337 | if (context != null) {
338 | r = context.getResources();
339 | }
340 |
341 | setRawMinTextSize(TypedValue.applyDimension(unit, size, r.getDisplayMetrics()));
342 | return this;
343 | }
344 |
345 | private void setRawMinTextSize(float size) {
346 | if (size != mMinTextSize) {
347 | mMinTextSize = size;
348 |
349 | autofit();
350 | }
351 | }
352 |
353 | /**
354 | * Returns the maximum size (in pixels) of the text.
355 | */
356 | public float getMaxTextSize() {
357 | return mMaxTextSize;
358 | }
359 |
360 | /**
361 | * Set the maximum text size to the given value, interpreted as "scaled pixel" units. This size
362 | * is adjusted based on the current density and user font size preference.
363 | *
364 | * @param size The scaled pixel size.
365 | *
366 | * @attr ref android.R.styleable#TextView_textSize
367 | */
368 | public AutofitHelper setMaxTextSize(float size) {
369 | return setMaxTextSize(TypedValue.COMPLEX_UNIT_SP, size);
370 | }
371 |
372 | /**
373 | * Set the maximum text size to a given unit and value. See TypedValue for the possible
374 | * dimension units.
375 | *
376 | * @param unit The desired dimension unit.
377 | * @param size The desired size in the given units.
378 | *
379 | * @attr ref android.R.styleable#TextView_textSize
380 | */
381 | public AutofitHelper setMaxTextSize(int unit, float size) {
382 | Context context = mTextView.getContext();
383 | Resources r = Resources.getSystem();
384 |
385 | if (context != null) {
386 | r = context.getResources();
387 | }
388 |
389 | setRawMaxTextSize(TypedValue.applyDimension(unit, size, r.getDisplayMetrics()));
390 | return this;
391 | }
392 |
393 | private void setRawMaxTextSize(float size) {
394 | if (size != mMaxTextSize) {
395 | mMaxTextSize = size;
396 |
397 | autofit();
398 | }
399 | }
400 |
401 | /**
402 | * @see TextView#getMaxLines()
403 | */
404 | public int getMaxLines() {
405 | return mMaxLines;
406 | }
407 |
408 | /**
409 | * @see TextView#setMaxLines(int)
410 | */
411 | public AutofitHelper setMaxLines(int lines) {
412 | if (mMaxLines != lines) {
413 | mMaxLines = lines;
414 |
415 | autofit();
416 | }
417 | return this;
418 | }
419 |
420 | /**
421 | * Returns whether or not automatically resizing text is enabled.
422 | */
423 | public boolean isEnabled() {
424 | return mEnabled;
425 | }
426 |
427 | /**
428 | * Set the enabled state of automatically resizing text.
429 | */
430 | public AutofitHelper setEnabled(boolean enabled) {
431 | if (mEnabled != enabled) {
432 | mEnabled = enabled;
433 |
434 | if (enabled) {
435 | mTextView.addTextChangedListener(mTextWatcher);
436 | mTextView.addOnLayoutChangeListener(mOnLayoutChangeListener);
437 |
438 | autofit();
439 | } else {
440 | mTextView.removeTextChangedListener(mTextWatcher);
441 | mTextView.removeOnLayoutChangeListener(mOnLayoutChangeListener);
442 |
443 | mTextView.setTextSize(TypedValue.COMPLEX_UNIT_PX, mTextSize);
444 | }
445 | }
446 | return this;
447 | }
448 |
449 | /**
450 | * Returns the original text size of the View.
451 | *
452 | * @see TextView#getTextSize()
453 | */
454 | public float getTextSize() {
455 | return mTextSize;
456 | }
457 |
458 | /**
459 | * Set the original text size of the View.
460 | *
461 | * @see TextView#setTextSize(float)
462 | */
463 | public void setTextSize(float size) {
464 | setTextSize(TypedValue.COMPLEX_UNIT_SP, size);
465 | }
466 |
467 | /**
468 | * Set the original text size of the View.
469 | *
470 | * @see TextView#setTextSize(int, float)
471 | */
472 | public void setTextSize(int unit, float size) {
473 | if (mIsAutofitting) {
474 | // We don't want to update the TextView's actual textSize while we're autofitting
475 | // since it'd get set to the autofitTextSize
476 | return;
477 | }
478 | Context context = mTextView.getContext();
479 | Resources r = Resources.getSystem();
480 |
481 | if (context != null) {
482 | r = context.getResources();
483 | }
484 |
485 | setRawTextSize(TypedValue.applyDimension(unit, size, r.getDisplayMetrics()));
486 | }
487 |
488 | private void setRawTextSize(float size) {
489 | if (mTextSize != size) {
490 | mTextSize = size;
491 | }
492 | }
493 |
494 | private void autofit() {
495 | float oldTextSize = mTextView.getTextSize();
496 | float textSize;
497 |
498 | mIsAutofitting = true;
499 | autofit(mTextView, mPaint, mMinTextSize, mMaxTextSize, mMaxLines, mPrecision);
500 | mIsAutofitting = false;
501 |
502 | textSize = mTextView.getTextSize();
503 | if (textSize != oldTextSize) {
504 | sendTextSizeChange(textSize, oldTextSize);
505 | }
506 | }
507 |
508 | private void sendTextSizeChange(float textSize, float oldTextSize) {
509 | if (mListeners == null) {
510 | return;
511 | }
512 |
513 | for (OnTextSizeChangeListener listener : mListeners) {
514 | listener.onTextSizeChange(textSize, oldTextSize);
515 | }
516 | }
517 |
518 | private class AutofitTextWatcher implements TextWatcher {
519 | @Override
520 | public void beforeTextChanged(CharSequence charSequence, int start, int count, int after) {
521 | // do nothing
522 | }
523 |
524 | @Override
525 | public void onTextChanged(CharSequence charSequence, int start, int before, int count) {
526 | autofit();
527 | }
528 |
529 | @Override
530 | public void afterTextChanged(Editable editable) {
531 | // do nothing
532 | }
533 | }
534 |
535 | private class AutofitOnLayoutChangeListener implements View.OnLayoutChangeListener {
536 | @Override
537 | public void onLayoutChange(View view, int left, int top, int right, int bottom,
538 | int oldLeft, int oldTop, int oldRight, int oldBottom) {
539 | autofit();
540 | }
541 | }
542 |
543 | /**
544 | * When an object of a type is attached to an {@code AutofitHelper}, its methods will be called
545 | * when the {@code textSize} is changed.
546 | */
547 | public interface OnTextSizeChangeListener {
548 | /**
549 | * This method is called to notify you that the size of the text has changed to
550 | * {@code textSize} from {@code oldTextSize}.
551 | */
552 | public void onTextSizeChange(float textSize, float oldTextSize);
553 | }
554 | }
555 |
--------------------------------------------------------------------------------
/library/src/main/java/me/grantland/widget/AutofitLayout.java:
--------------------------------------------------------------------------------
1 | package me.grantland.widget;
2 |
3 | import android.content.Context;
4 | import android.content.res.TypedArray;
5 | import android.util.AttributeSet;
6 | import android.util.TypedValue;
7 | import android.view.View;
8 | import android.view.ViewGroup;
9 | import android.widget.FrameLayout;
10 | import android.widget.TextView;
11 |
12 | import java.util.WeakHashMap;
13 |
14 | /**
15 | * A {@link ViewGroup} that re-sizes the text of it's children to be no larger than the width of the
16 | * view.
17 | *
18 | * @attr ref R.styleable.AutofitTextView_sizeToFit
19 | * @attr ref R.styleable.AutofitTextView_minTextSize
20 | * @attr ref R.styleable.AutofitTextView_precision
21 | */
22 | public class AutofitLayout extends FrameLayout {
23 |
24 | private boolean mEnabled;
25 | private float mMinTextSize;
26 | private float mPrecision;
27 | private WeakHashMap mHelpers = new WeakHashMap();
28 |
29 | public AutofitLayout(Context context) {
30 | super(context);
31 | init(context, null, 0);
32 | }
33 |
34 | public AutofitLayout(Context context, AttributeSet attrs) {
35 | super(context, attrs);
36 | init(context, attrs, 0);
37 | }
38 |
39 | public AutofitLayout(Context context, AttributeSet attrs, int defStyle) {
40 | super(context, attrs, defStyle);
41 | init(context, attrs, defStyle);
42 | }
43 |
44 | private void init(Context context, AttributeSet attrs, int defStyle) {
45 | boolean sizeToFit = true;
46 | int minTextSize = -1;
47 | float precision = -1;
48 |
49 | if (attrs != null) {
50 | TypedArray ta = context.obtainStyledAttributes(
51 | attrs,
52 | R.styleable.AutofitTextView,
53 | defStyle,
54 | 0);
55 | sizeToFit = ta.getBoolean(R.styleable.AutofitTextView_sizeToFit, sizeToFit);
56 | minTextSize = ta.getDimensionPixelSize(R.styleable.AutofitTextView_minTextSize,
57 | minTextSize);
58 | precision = ta.getFloat(R.styleable.AutofitTextView_precision, precision);
59 | ta.recycle();
60 | }
61 |
62 | mEnabled = sizeToFit;
63 | mMinTextSize = minTextSize;
64 | mPrecision = precision;
65 | }
66 |
67 | @Override
68 | public void addView(View child, int index, ViewGroup.LayoutParams params) {
69 | super.addView(child, index, params);
70 | TextView textView = (TextView) child;
71 | AutofitHelper helper = AutofitHelper.create(textView)
72 | .setEnabled(mEnabled);
73 | if (mPrecision > 0) {
74 | helper.setPrecision(mPrecision);
75 | }
76 | if (mMinTextSize > 0) {
77 | helper.setMinTextSize(TypedValue.COMPLEX_UNIT_PX, mMinTextSize);
78 | }
79 | mHelpers.put(textView, helper);
80 | }
81 |
82 | /**
83 | * Returns the {@link AutofitHelper} for this child View.
84 | */
85 | public AutofitHelper getAutofitHelper(TextView textView) {
86 | return mHelpers.get(textView);
87 | }
88 |
89 | /**
90 | * Returns the {@link AutofitHelper} for this child View.
91 | */
92 | public AutofitHelper getAutofitHelper(int index) {
93 | return mHelpers.get(getChildAt(index));
94 | }
95 | }
96 |
--------------------------------------------------------------------------------
/library/src/main/java/me/grantland/widget/AutofitTextView.java:
--------------------------------------------------------------------------------
1 | package me.grantland.widget;
2 |
3 | import android.content.Context;
4 | import android.util.AttributeSet;
5 | import android.util.TypedValue;
6 | import android.widget.TextView;
7 |
8 | /**
9 | * A {@link TextView} that re-sizes its text to be no larger than the width of the view.
10 | *
11 | * @attr ref R.styleable.AutofitTextView_sizeToFit
12 | * @attr ref R.styleable.AutofitTextView_minTextSize
13 | * @attr ref R.styleable.AutofitTextView_precision
14 | */
15 | public class AutofitTextView extends TextView implements AutofitHelper.OnTextSizeChangeListener {
16 |
17 | private AutofitHelper mHelper;
18 |
19 | public AutofitTextView(Context context) {
20 | super(context);
21 | init(context, null, 0);
22 | }
23 |
24 | public AutofitTextView(Context context, AttributeSet attrs) {
25 | super(context, attrs);
26 | init(context, attrs, 0);
27 | }
28 |
29 | public AutofitTextView(Context context, AttributeSet attrs, int defStyle) {
30 | super(context, attrs, defStyle);
31 | init(context, attrs, defStyle);
32 | }
33 |
34 | private void init(Context context, AttributeSet attrs, int defStyle) {
35 | mHelper = AutofitHelper.create(this, attrs, defStyle)
36 | .addOnTextSizeChangeListener(this);
37 | }
38 |
39 | // Getters and Setters
40 |
41 | /**
42 | * {@inheritDoc}
43 | */
44 | @Override
45 | public void setTextSize(int unit, float size) {
46 | super.setTextSize(unit, size);
47 | if (mHelper != null) {
48 | mHelper.setTextSize(unit, size);
49 | }
50 | }
51 |
52 | /**
53 | * {@inheritDoc}
54 | */
55 | @Override
56 | public void setLines(int lines) {
57 | super.setLines(lines);
58 | if (mHelper != null) {
59 | mHelper.setMaxLines(lines);
60 | }
61 | }
62 |
63 | /**
64 | * {@inheritDoc}
65 | */
66 | @Override
67 | public void setMaxLines(int maxLines) {
68 | super.setMaxLines(maxLines);
69 | if (mHelper != null) {
70 | mHelper.setMaxLines(maxLines);
71 | }
72 | }
73 |
74 | /**
75 | * Returns the {@link AutofitHelper} for this View.
76 | */
77 | public AutofitHelper getAutofitHelper() {
78 | return mHelper;
79 | }
80 |
81 | /**
82 | * Returns whether or not the text will be automatically re-sized to fit its constraints.
83 | */
84 | public boolean isSizeToFit() {
85 | return mHelper.isEnabled();
86 | }
87 |
88 | /**
89 | * Sets the property of this field (sizeToFit), to automatically resize the text to fit its
90 | * constraints.
91 | */
92 | public void setSizeToFit() {
93 | setSizeToFit(true);
94 | }
95 |
96 | /**
97 | * If true, the text will automatically be re-sized to fit its constraints; if false, it will
98 | * act like a normal TextView.
99 | *
100 | * @param sizeToFit
101 | */
102 | public void setSizeToFit(boolean sizeToFit) {
103 | mHelper.setEnabled(sizeToFit);
104 | }
105 |
106 | /**
107 | * Returns the maximum size (in pixels) of the text in this View.
108 | */
109 | public float getMaxTextSize() {
110 | return mHelper.getMaxTextSize();
111 | }
112 |
113 | /**
114 | * Set the maximum text size to the given value, interpreted as "scaled pixel" units. This size
115 | * is adjusted based on the current density and user font size preference.
116 | *
117 | * @param size The scaled pixel size.
118 | *
119 | * @attr ref android.R.styleable#TextView_textSize
120 | */
121 | public void setMaxTextSize(float size) {
122 | mHelper.setMaxTextSize(size);
123 | }
124 |
125 | /**
126 | * Set the maximum text size to a given unit and value. See TypedValue for the possible
127 | * dimension units.
128 | *
129 | * @param unit The desired dimension unit.
130 | * @param size The desired size in the given units.
131 | *
132 | * @attr ref android.R.styleable#TextView_textSize
133 | */
134 | public void setMaxTextSize(int unit, float size) {
135 | mHelper.setMaxTextSize(unit, size);
136 | }
137 |
138 | /**
139 | * Returns the minimum size (in pixels) of the text in this View.
140 | */
141 | public float getMinTextSize() {
142 | return mHelper.getMinTextSize();
143 | }
144 |
145 | /**
146 | * Set the minimum text size to the given value, interpreted as "scaled pixel" units. This size
147 | * is adjusted based on the current density and user font size preference.
148 | *
149 | * @param minSize The scaled pixel size.
150 | *
151 | * @attr ref me.grantland.R.styleable#AutofitTextView_minTextSize
152 | */
153 | public void setMinTextSize(int minSize) {
154 | mHelper.setMinTextSize(TypedValue.COMPLEX_UNIT_SP, minSize);
155 | }
156 |
157 | /**
158 | * Set the minimum text size to a given unit and value. See TypedValue for the possible
159 | * dimension units.
160 | *
161 | * @param unit The desired dimension unit.
162 | * @param minSize The desired size in the given units.
163 | *
164 | * @attr ref me.grantland.R.styleable#AutofitTextView_minTextSize
165 | */
166 | public void setMinTextSize(int unit, float minSize) {
167 | mHelper.setMinTextSize(unit, minSize);
168 | }
169 |
170 | /**
171 | * Returns the amount of precision used to calculate the correct text size to fit within its
172 | * bounds.
173 | */
174 | public float getPrecision() {
175 | return mHelper.getPrecision();
176 | }
177 |
178 | /**
179 | * Set the amount of precision used to calculate the correct text size to fit within its
180 | * bounds. Lower precision is more precise and takes more time.
181 | *
182 | * @param precision The amount of precision.
183 | */
184 | public void setPrecision(float precision) {
185 | mHelper.setPrecision(precision);
186 | }
187 |
188 | @Override
189 | public void onTextSizeChange(float textSize, float oldTextSize) {
190 | // do nothing
191 | }
192 | }
193 |
--------------------------------------------------------------------------------
/library/src/main/res/values/attrs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/sample/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.application'
2 |
3 | dependencies {
4 | compile project (':library')
5 | }
6 |
7 | android {
8 | compileSdkVersion rootProject.ext.compileSdkVersion
9 | buildToolsVersion rootProject.ext.buildToolsVersion
10 |
11 | defaultConfig {
12 | minSdkVersion rootProject.ext.minSdkVersion
13 | targetSdkVersion rootProject.ext.targetSdkVersion
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/sample/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
12 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
--------------------------------------------------------------------------------
/sample/src/main/java/me/grantland/autofittextview/sample/SampleActivity.java:
--------------------------------------------------------------------------------
1 | package me.grantland.autofittextview.sample;
2 |
3 | import android.app.Activity;
4 | import android.os.Bundle;
5 | import android.text.Editable;
6 | import android.text.TextWatcher;
7 | import android.widget.EditText;
8 | import android.widget.TextView;
9 |
10 | public class SampleActivity extends Activity {
11 |
12 | private TextView mOutput, mAutofitOutput;
13 |
14 | /** Called when the activity is first created. */
15 | @Override
16 | public void onCreate(Bundle savedInstanceState) {
17 | super.onCreate(savedInstanceState);
18 | setContentView(R.layout.main);
19 |
20 | mOutput = (TextView)findViewById(R.id.output);
21 | mAutofitOutput = (TextView)findViewById(R.id.output_autofit);
22 |
23 | ((EditText)findViewById(R.id.input)).addTextChangedListener(new TextWatcher() {
24 | @Override
25 | public void beforeTextChanged(CharSequence charSequence, int i, int i2, int i3) {
26 | // do nothing
27 | }
28 |
29 | @Override
30 | public void onTextChanged(CharSequence charSequence, int i, int i2, int i3) {
31 | mOutput.setText(charSequence);
32 | mAutofitOutput.setText(charSequence);
33 | }
34 |
35 | @Override
36 | public void afterTextChanged(Editable editable) {
37 | // do nothing
38 | }
39 | });
40 | }
41 | }
--------------------------------------------------------------------------------
/sample/src/main/res/drawable-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/grantland/android-autofittextview/5c565aa5c3ed62aaa31140440bb526e7435e7947/sample/src/main/res/drawable-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/sample/src/main/res/drawable-ldpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/grantland/android-autofittextview/5c565aa5c3ed62aaa31140440bb526e7435e7947/sample/src/main/res/drawable-ldpi/ic_launcher.png
--------------------------------------------------------------------------------
/sample/src/main/res/drawable-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/grantland/android-autofittextview/5c565aa5c3ed62aaa31140440bb526e7435e7947/sample/src/main/res/drawable-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/sample/src/main/res/drawable-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/grantland/android-autofittextview/5c565aa5c3ed62aaa31140440bb526e7435e7947/sample/src/main/res/drawable-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/sample/src/main/res/layout-land/main.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
14 |
19 |
25 |
30 |
35 |
43 |
44 |
49 |
54 |
64 |
65 |
66 |
67 |
68 |
--------------------------------------------------------------------------------
/sample/src/main/res/layout/main.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
11 |
18 |
23 |
31 |
36 |
46 |
47 |
48 |
--------------------------------------------------------------------------------
/sample/src/main/res/values-v11/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
8 |
11 |
12 |
--------------------------------------------------------------------------------
/sample/src/main/res/values-v14/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
9 |
12 |
13 |
--------------------------------------------------------------------------------
/sample/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | AutoFitTextView
5 |
6 | text
7 | Normal:
8 | Autofit:
9 |
10 | This is an example
11 |
12 |
--------------------------------------------------------------------------------
/sample/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
8 |
15 |
16 |
17 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/settings.gradle:
--------------------------------------------------------------------------------
1 | rootProject.name='autofittextview'
2 | include ':library', \
3 | ':sample'
4 |
--------------------------------------------------------------------------------
/website/static/autofittextview.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/grantland/android-autofittextview/5c565aa5c3ed62aaa31140440bb526e7435e7947/website/static/autofittextview.gif
--------------------------------------------------------------------------------