├── .gitignore
├── .idea
├── copyright
│ └── profiles_settings.xml
├── dictionaries
│ └── ccheng.xml
├── inspectionProfiles
│ └── profiles_settings.xml
└── scopes
│ └── scope_settings.xml
├── MIT License.txt
├── README.md
├── build.gradle
├── en.png
├── gradle.properties
├── gradle
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
├── justifytext-library
├── .gitignore
├── build.gradle
├── gradle.properties
├── library_justifytext
│ ├── .gitignore
│ ├── build.gradle
│ ├── proguard-rules.txt
│ └── src
│ │ └── main
│ │ ├── AndroidManifest.xml
│ │ └── java
│ │ └── me
│ │ └── biubiubiu
│ │ └── justifytext
│ │ └── library
│ │ └── JustifyTextView.java
├── proguard-rules.txt
└── src
│ └── main
│ ├── AndroidManifest.xml
│ └── java
│ └── me
│ └── biubiubiu
│ └── justifytext
│ └── library
│ └── JustifyTextView.java
├── sample
├── .gitignore
├── build.gradle
├── proguard-rules.txt
└── src
│ └── main
│ ├── AndroidManifest.xml
│ ├── assets
│ ├── 1.txt
│ └── 1en.txt
│ ├── java
│ └── me
│ │ └── biubiubiu
│ │ └── justifytext
│ │ ├── DemoActivity.java
│ │ ├── MyScrollView.java
│ │ └── TextActivity.java
│ └── res
│ ├── drawable-hdpi
│ └── ic_launcher.png
│ ├── drawable-mdpi
│ └── ic_launcher.png
│ ├── drawable-xhdpi
│ └── ic_launcher.png
│ ├── drawable-xxhdpi
│ └── ic_launcher.png
│ ├── layout
│ ├── activity_demo.xml
│ ├── activity_main.xml
│ └── activity_text.xml
│ ├── menu
│ └── main.xml
│ ├── values-w820dp
│ └── dimens.xml
│ └── values
│ ├── attrs.xml
│ ├── colors.xml
│ ├── dimens.xml
│ ├── strings.xml
│ └── styles.xml
├── settings.gradle
├── texttest.iml
└── zh.png
/.gitignore:
--------------------------------------------------------------------------------
1 | /.idea/*
2 | .gradle
3 | /local.properties
4 | /.idea/workspace.xml
5 | .DS_Store
6 | **/*.iml
7 | .idea
8 | /sample/sample.apk
9 | /.idea/misc.xml
10 |
--------------------------------------------------------------------------------
/.idea/copyright/profiles_settings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/.idea/dictionaries/ccheng.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/.idea/inspectionProfiles/profiles_settings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/.idea/scopes/scope_settings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/MIT License.txt:
--------------------------------------------------------------------------------
1 | Copyright (c) 2012-2023 Chao Cheng and others
2 |
3 | Permission is hereby granted, free of charge, to any person obtaining
4 | a copy of this software and associated documentation files (the
5 | "Software"), to deal in the Software without restriction, including
6 | without limitation the rights to use, copy, modify, merge, publish,
7 | distribute, sublicense, and/or sell copies of the Software, and to
8 | permit persons to whom the Software is furnished to do so, subject to
9 | the following conditions:
10 |
11 | The above copyright notice and this permission notice shall be
12 | included in all copies or substantial portions of the Software.
13 |
14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 | # JustifiedTextView
3 |
4 | Implement justified textview base on the native TextView. Let text displays fill the screen width without extra blanks in the end of line.
5 |
6 | ### English
7 | 
8 |
9 | ### Chinese
10 | 
11 |
12 | ## Usage
13 |
14 | You can import in build.gradle like this
15 |
16 | compile 'me.biubiubiu.justifytext:library:1.1'
17 |
18 | If you use maven, add this to pom.xml.
19 |
20 |
21 | me.biubiubiu.justifytext
22 | library
23 | 1.1
24 | aar
25 |
26 |
27 | Then add put this into layout file.
28 |
29 | ```xml
30 |
35 | ```
36 |
37 | ## demo
38 |
39 | [directly download](http://pan.baidu.com/s/1bnq2rk7)
40 |
41 |
42 |
44 |
45 |
46 | ## Change list
47 |
48 | - **1.1** Display ok when using custom font.
49 |
50 |
51 | ----------------------------------
52 |
53 | MIT License
54 |
--------------------------------------------------------------------------------
/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 | mavenCentral()
6 | }
7 | dependencies {
8 | classpath 'com.android.tools.build:gradle:1.0.0'
9 | }
10 | }
11 |
12 | allprojects {
13 | repositories {
14 | mavenCentral()
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/en.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ufo22940268/android-justifiedtextview/9329c7f68a79f78c0eabed35f6dc8d030b322d0d/en.png
--------------------------------------------------------------------------------
/gradle.properties:
--------------------------------------------------------------------------------
1 | # Project-wide Gradle settings.
2 |
3 | # IDE (e.g. Android Studio) users:
4 | # Settings specified in this file will override any Gradle settings
5 | # configured through the IDE.
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
19 |
20 | VERSION_NAME=1.1
21 | VERSION_CODE=2
22 | GROUP=me.biubiubiu.justifytext
23 |
24 | POM_DESCRIPTION=android justified textview
25 | POM_URL=https://github.com/ufo22940268/Android-Flipperview
26 | POM_SCM_URL=https://github.com/ufo22940268/Android-Flipperview
27 | POM_SCM_CONNECTION=scm:git@github.com:ufo22940268/Android-Flipperview
28 | POM_SCM_DEV_CONNECTION=scm:git@github.com:ufo22940268/Android-Flipperview
29 | POM_LICENCE_NAME=The Apache Software License, Version 2.0
30 | POM_LICENCE_URL=http://www.apache.org/licenses/LICENSE-2.0.txt
31 | POM_LICENCE_DIST=repo
32 | POM_DEVELOPER_ID=ufo22940268
33 | POM_DEVELOPER_NAME=frank cheng
34 |
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ufo22940268/android-justifiedtextview/9329c7f68a79f78c0eabed35f6dc8d030b322d0d/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Tue Mar 24 14:24:39 CST 2015
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.2.1-all.zip
7 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/justifytext-library/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/justifytext-library/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.library'
2 |
3 | android {
4 | compileSdkVersion 19
5 | buildToolsVersion "19.1.0"
6 |
7 | defaultConfig {
8 | minSdkVersion 8
9 | targetSdkVersion 19
10 | versionCode 1
11 | versionName "1.0"
12 | }
13 | buildTypes {
14 | release {
15 | minifyEnabled false
16 | }
17 | }
18 | }
19 |
20 | dependencies {
21 | compile fileTree(dir: 'libs', include: ['*.jar'])
22 | }
23 |
24 | apply from: 'https://raw.githubusercontent.com/daimajia/AndroidImageSlider/master/library/gradle-mvn-push.gradle'
25 |
--------------------------------------------------------------------------------
/justifytext-library/gradle.properties:
--------------------------------------------------------------------------------
1 | # Project-wide Gradle settings.
2 |
3 | # IDE (e.g. Android Studio) users:
4 | # Settings specified in this file will override any Gradle settings
5 | # configured through the IDE.
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
19 |
20 | POM_NAME=Android-JustifiedTextView Library
21 | POM_ARTIFACT_ID=library
22 | POM_PACKAGING=aar
23 |
--------------------------------------------------------------------------------
/justifytext-library/library_justifytext/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/justifytext-library/library_justifytext/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'android-library'
2 |
3 | android {
4 | compileSdkVersion 19
5 | buildToolsVersion "19.1.0"
6 |
7 | defaultConfig {
8 | minSdkVersion 8
9 | targetSdkVersion 19
10 | versionCode 1
11 | versionName "1.0"
12 | }
13 | buildTypes {
14 | release {
15 | runProguard false
16 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.txt'
17 | }
18 | }
19 | }
20 |
21 | dependencies {
22 | compile fileTree(dir: 'libs', include: ['*.jar'])
23 | }
24 |
--------------------------------------------------------------------------------
/justifytext-library/library_justifytext/proguard-rules.txt:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # By default, the flags in this file are appended to flags specified
3 | # in /Users/ccheng/program/adt-bundle-mac-x86_64-20130219/sdk/tools/proguard/proguard-android.txt
4 | # You can edit the include path and order by changing the ProGuard
5 | # include property in project.properties.
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 | #}
--------------------------------------------------------------------------------
/justifytext-library/library_justifytext/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/justifytext-library/library_justifytext/src/main/java/me/biubiubiu/justifytext/library/JustifyTextView.java:
--------------------------------------------------------------------------------
1 | package me.biubiubiu.justifytext.library;
2 |
3 | import android.content.Context;
4 | import android.graphics.Canvas;
5 | import android.graphics.Paint;
6 | import android.text.Layout;
7 | import android.text.StaticLayout;
8 | import android.text.TextPaint;
9 | import android.util.AttributeSet;
10 | import android.widget.TextView;
11 |
12 | /**
13 | * @author ccheng
14 | * @Date 3/18/14
15 | */
16 | public class JustifyTextView extends TextView {
17 |
18 | private int mLineY;
19 | private int mViewWidth;
20 | public static final String TWO_CHINESE_BLANK = " ";
21 |
22 | public JustifyTextView(Context context, AttributeSet attrs) {
23 | super(context, attrs);
24 | }
25 |
26 | @Override
27 | protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
28 | super.onLayout(changed, left, top, right, bottom);
29 | }
30 |
31 | @Override
32 | protected void onDraw(Canvas canvas) {
33 | TextPaint paint = getPaint();
34 | paint.setColor(getCurrentTextColor());
35 | paint.drawableState = getDrawableState();
36 | mViewWidth = getMeasuredWidth();
37 | String text = getText().toString();
38 | mLineY = 0;
39 | mLineY += getTextSize();
40 | Layout layout = getLayout();
41 |
42 | // layout.getLayout()在4.4.3出现NullPointerException
43 | if (layout == null) {
44 | return;
45 | }
46 |
47 | Paint.FontMetrics fm = paint.getFontMetrics();
48 |
49 | int textHeight = (int) (Math.ceil(fm.descent - fm.ascent));
50 | textHeight = (int) (textHeight * layout.getSpacingMultiplier() + layout.getSpacingAdd());
51 |
52 | for (int i = 0; i < layout.getLineCount(); i++) {
53 | int lineStart = layout.getLineStart(i);
54 | int lineEnd = layout.getLineEnd(i);
55 | float width = StaticLayout.getDesiredWidth(text, lineStart, lineEnd, getPaint());
56 | String line = text.substring(lineStart, lineEnd);
57 | if (needScale(line) && i < layout.getLineCount() -1) {
58 | drawScaledText(canvas, lineStart, line, width);
59 | } else {
60 | canvas.drawText(line, 0, mLineY, paint);
61 | }
62 | mLineY += textHeight;
63 | }
64 | }
65 |
66 | private void drawScaledText(Canvas canvas, int lineStart, String line, float lineWidth) {
67 | float x = 0;
68 | if (isFirstLineOfParagraph(lineStart, line)) {
69 | String blanks = " ";
70 | canvas.drawText(blanks, x, mLineY, getPaint());
71 | float bw = StaticLayout.getDesiredWidth(blanks, getPaint());
72 | x += bw;
73 |
74 | line = line.substring(3);
75 | }
76 |
77 | int gapCount = line.length() - 1;
78 | int i = 0;
79 | if (line.length() > 2 && line.charAt(0) == 12288 && line.charAt(1) == 12288) {
80 | String substring = line.substring(0, 2);
81 | float cw = StaticLayout.getDesiredWidth(substring, getPaint());
82 | canvas.drawText(substring, x, mLineY, getPaint());
83 | x += cw;
84 | i += 2;
85 | }
86 |
87 | float d = (mViewWidth - lineWidth) / gapCount;
88 | for (; i < line.length(); i++) {
89 | String c = String.valueOf(line.charAt(i));
90 | float cw = StaticLayout.getDesiredWidth(c, getPaint());
91 | canvas.drawText(c, x, mLineY, getPaint());
92 | x += cw + d;
93 | }
94 | }
95 |
96 | private boolean isFirstLineOfParagraph(int lineStart, String line) {
97 | return line.length() > 3 && line.charAt(0) == ' ' && line.charAt(1) == ' ';
98 | }
99 |
100 | private boolean needScale(String line) {
101 | if (line == null || line.length() == 0) {
102 | return false;
103 | } else {
104 | return line.charAt(line.length() - 1) != '\n';
105 | }
106 | }
107 |
108 | }
109 |
--------------------------------------------------------------------------------
/justifytext-library/proguard-rules.txt:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # By default, the flags in this file are appended to flags specified
3 | # in /Users/ccheng/program/adt-bundle-mac-x86_64-20130219/sdk/tools/proguard/proguard-android.txt
4 | # You can edit the include path and order by changing the ProGuard
5 | # include property in project.properties.
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 | #}
--------------------------------------------------------------------------------
/justifytext-library/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/justifytext-library/src/main/java/me/biubiubiu/justifytext/library/JustifyTextView.java:
--------------------------------------------------------------------------------
1 | package me.biubiubiu.justifytext.library;
2 |
3 | import android.content.Context;
4 | import android.graphics.Canvas;
5 | import android.text.Layout;
6 | import android.text.StaticLayout;
7 | import android.text.TextPaint;
8 | import android.util.AttributeSet;
9 | import android.widget.TextView;
10 |
11 | /**
12 | * Created by ccheng on 3/18/14.
13 | */
14 | public class JustifyTextView extends TextView {
15 |
16 | private int mLineY;
17 | private int mViewWidth;
18 |
19 | public JustifyTextView(Context context, AttributeSet attrs) {
20 | super(context, attrs);
21 | }
22 |
23 | @Override
24 | protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
25 | super.onLayout(changed, left, top, right, bottom);
26 | }
27 |
28 | @Override
29 | protected void onDraw(Canvas canvas) {
30 | TextPaint paint = getPaint();
31 | paint.setColor(getCurrentTextColor());
32 | paint.drawableState = getDrawableState();
33 | mViewWidth = getMeasuredWidth();
34 | String text = (String) getText();
35 | mLineY = 0;
36 | mLineY += getTextSize() * 1.5;
37 | Layout layout = getLayout();
38 | for (int i = 0; i < layout.getLineCount(); i++) {
39 | int lineStart = layout.getLineStart(i);
40 | int lineEnd = layout.getLineEnd(i);
41 | String line = text.substring(lineStart, lineEnd);
42 |
43 | float width = StaticLayout.getDesiredWidth(text, lineStart, lineEnd, getPaint());
44 | if (needScale(line) && i < layout.getLineCount() -1) {
45 | drawScaledText(canvas, lineStart, line, width);
46 | } else {
47 | canvas.drawText(line, 0, mLineY, paint);
48 | }
49 |
50 | mLineY += getLineHeight();
51 | }
52 | }
53 |
54 | private void drawScaledText(Canvas canvas, int lineStart, String line, float lineWidth) {
55 | float x = 0;
56 | if (isFirstLineOfParagraph(lineStart, line)) {
57 | String blanks = " ";
58 | canvas.drawText(blanks, x, mLineY, getPaint());
59 | float bw = StaticLayout.getDesiredWidth(blanks, getPaint());
60 | x += bw;
61 |
62 | line = line.substring(3);
63 | }
64 |
65 | float d = (mViewWidth - lineWidth) / line.length() - 1;
66 | for (int i = 0; i < line.length(); i++) {
67 | String c = String.valueOf(line.charAt(i));
68 | float cw = StaticLayout.getDesiredWidth(c, getPaint());
69 | canvas.drawText(c, x, mLineY, getPaint());
70 | x += cw + d;
71 | }
72 | }
73 |
74 | private boolean isFirstLineOfParagraph(int lineStart, String line) {
75 | return line.length() > 3 && line.charAt(0) == ' ' && line.charAt(1) == ' ';
76 | }
77 |
78 | private boolean needScale(String line) {
79 | if (line.length() == 0) {
80 | return false;
81 | } else {
82 | return line.charAt(line.length() - 1) != '\n';
83 | }
84 | }
85 |
86 | }
87 |
--------------------------------------------------------------------------------
/sample/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/sample/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'android'
2 |
3 | android {
4 | compileSdkVersion 21
5 | buildToolsVersion "19.1.0"
6 |
7 | defaultConfig {
8 | minSdkVersion 8
9 | targetSdkVersion 21
10 | versionCode 1
11 | versionName "1.0"
12 | }
13 |
14 | /*signingConfigs {*/
15 | /*release {*/
16 | /*storeFile file("/Applications/Android Studio.app/bin/me-biubiubiu")*/
17 | /*storePassword "12345678"*/
18 | /*keyAlias "me-biubiubiu-alias"*/
19 | /*keyPassword "12345678"*/
20 | /*}*/
21 | /*}*/
22 |
23 | lintOptions {
24 | checkReleaseBuilds false
25 | // Or, if you prefer, you can continue to check for errors in release builds,
26 | // but continue the build even when errors are found:
27 | abortOnError false
28 | }
29 |
30 | buildTypes {
31 | release {
32 | minifyEnabled true
33 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.txt'
34 | /*signingConfig signingConfigs.release*/
35 | }
36 | }
37 | }
38 |
39 | dependencies {
40 | compile 'me.biubiubiu.justifytext:library:1.1'
41 | compile fileTree(dir: 'libs', include: ['*.jar'])
42 | compile 'com.android.support:appcompat-v7:21.0.3'
43 | }
44 |
--------------------------------------------------------------------------------
/sample/proguard-rules.txt:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # By default, the flags in this file are appended to flags specified
3 | # in /Users/ccheng/program/adt-bundle-mac-x86_64-20130219/sdk/tools/proguard/proguard-android.txt
4 | # You can edit the include path and order by changing the ProGuard
5 | # include property in project.properties.
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 | #}
--------------------------------------------------------------------------------
/sample/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
24 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
--------------------------------------------------------------------------------
/sample/src/main/assets/1.txt:
--------------------------------------------------------------------------------
1 | 太阳穴那里却又是一阵钻心的疼,宿醉后的反应让她有些懵了,低头看着自己一、丝、不、挂、吻痕遍及的身子,再掀开被子看了看自己身下的那抹刺眼的血红。
2 | ‘轰’——爆炸的声音在脑海里响起,然后她的大脑一片空白,几乎是如机械似的穿上已经整齐叠放在身边的衣服,然后拉开门想要立刻离开这里。
3 | 刚打开门却看到一个人站在门口,带着惊讶的表情看着她,“小小小、小姐,你要的醒酒汤。”
4 | 服务员端着一碗刚刚熬制出来的醒酒汤,手间颤了一下,似乎是被她过激的动作吓到。
5 | 叶一涵根本来不及顾及那些,猛地上前揪住那个服务员的衣领,脸色阴鸷的问他,“我问你,开这间房的人是谁?”
6 | 服务员没有想到她直接过来揪住他,手中的醒酒汤也来不及。
7 |
--------------------------------------------------------------------------------
/sample/src/main/assets/1en.txt:
--------------------------------------------------------------------------------
1 | Via NASA: “This movie shows a coronal mass ejection (CME) on the sun from July 22, 2012 at 10:00 p.m. EDT until 2 a.m. on July 23 as captured by NASA’s Solar Terrestrial RElations Observatory-Ahead (STEREO-A). Because the CME headed in STEREO-A’s direction, it appears like a giant halo around the sun. NOTE: This video loops 3 times.” Credit: NASA/STEREO
2 | Fortunately, the blast site of the CMEs was not directed at Earth. Had this event occurred a week earlier when the point of eruption was Earth-facing, a potentially disastrous outcome would have unfolded.
3 | “I have come away from our recent studies more convinced than ever that Earth and its inhabitants were incredibly fortunate that the 2012 eruption happened when it did,” Baker tells NASA. “If the eruption had occurred only one week earlier, Earth would have been in the line of fire.”
--------------------------------------------------------------------------------
/sample/src/main/java/me/biubiubiu/justifytext/DemoActivity.java:
--------------------------------------------------------------------------------
1 | package me.biubiubiu.justifytext;
2 |
3 | import android.app.Activity;
4 | import android.content.Intent;
5 | import android.os.Bundle;
6 | import android.view.View;
7 |
8 | /**
9 | * Created by ccheng on 7/24/14.
10 | */
11 | public class DemoActivity extends Activity {
12 |
13 | @Override
14 | protected void onCreate(Bundle savedInstanceState) {
15 | super.onCreate(savedInstanceState);
16 | setContentView(R.layout.activity_demo);
17 | }
18 |
19 | public void onDisplayChinese(View view) {
20 | Intent intent = new Intent(this, TextActivity.class);
21 | intent.putExtra(TextActivity.KEY_FILE_NAME, "1.txt");
22 | startActivity(intent);
23 | }
24 |
25 | public void onDisplayEnglish(View view) {
26 | Intent intent = new Intent(this, TextActivity.class);
27 | intent.putExtra(TextActivity.KEY_FILE_NAME, "1en.txt");
28 | startActivity(intent);
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/sample/src/main/java/me/biubiubiu/justifytext/MyScrollView.java:
--------------------------------------------------------------------------------
1 | package me.biubiubiu.justifytext;
2 |
3 | import android.content.Context;
4 | import android.util.AttributeSet;
5 | import android.widget.ScrollView;
6 |
7 | /**
8 | * Created by ccheng on 7/24/14.
9 | */
10 | public class MyScrollView extends ScrollView {
11 |
12 | private ScrollView mAlternativeScrollView;
13 |
14 | public MyScrollView(Context context, AttributeSet attrs) {
15 | super(context, attrs);
16 | }
17 |
18 |
19 | public void setAlternativeScrollView(ScrollView alternativeScrollView) {
20 | mAlternativeScrollView = alternativeScrollView;
21 | }
22 |
23 | @Override
24 | protected void onScrollChanged(int l, int t, int oldl, int oldt) {
25 | super.onScrollChanged(l, t, oldl, oldt);
26 | if (mAlternativeScrollView != null) {
27 | mAlternativeScrollView.scrollTo(l, t);
28 | }
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/sample/src/main/java/me/biubiubiu/justifytext/TextActivity.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2013 Google Inc.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package me.biubiubiu.justifytext;
18 |
19 | import android.app.Activity;
20 | import android.os.Bundle;
21 | import android.widget.TextView;
22 |
23 | import java.io.BufferedReader;
24 | import java.io.IOException;
25 | import java.io.InputStreamReader;
26 |
27 | public class TextActivity extends Activity {
28 |
29 | public static final String KEY_FILE_NAME = "KEY_FILE_NAME";
30 | private TextView mJustifiedText;
31 | private TextView mText;
32 | private MyScrollView mScroller;
33 | private MyScrollView mJustifiedScroller;
34 |
35 | protected void onCreate(Bundle savedInstanceState) {
36 | super.onCreate(savedInstanceState);
37 | setContentView(R.layout.activity_text);
38 |
39 | mText = (TextView)findViewById(R.id.text);
40 | mJustifiedText = (TextView)findViewById(R.id.justified_text);
41 | String text = "";
42 |
43 | BufferedReader br = null;
44 | try {
45 | String fileName = getIntent().getStringExtra(KEY_FILE_NAME);
46 | br = new BufferedReader(new InputStreamReader(getAssets().open(fileName)));
47 | String line;
48 | StringBuffer sb = new StringBuffer();
49 | while ((line = br.readLine()) != null) {
50 | sb.append(line + "\n");
51 | }
52 | text = sb.toString();
53 | } catch (IOException e) {
54 | e.printStackTrace();
55 | } finally {
56 | try {
57 | br.close();
58 | } catch (IOException e) {
59 | e.printStackTrace();
60 | }
61 | }
62 |
63 | mJustifiedText.setText(text);
64 | mText.setText(text);
65 |
66 | mScroller = (MyScrollView)findViewById(R.id.scroller);
67 | mJustifiedScroller = (MyScrollView)findViewById(R.id.justified_scroller);
68 | mScroller.setAlternativeScrollView(mJustifiedScroller);
69 | mJustifiedScroller.setAlternativeScrollView(mScroller);
70 | }
71 | }
72 |
--------------------------------------------------------------------------------
/sample/src/main/res/drawable-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ufo22940268/android-justifiedtextview/9329c7f68a79f78c0eabed35f6dc8d030b322d0d/sample/src/main/res/drawable-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/sample/src/main/res/drawable-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ufo22940268/android-justifiedtextview/9329c7f68a79f78c0eabed35f6dc8d030b322d0d/sample/src/main/res/drawable-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/sample/src/main/res/drawable-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ufo22940268/android-justifiedtextview/9329c7f68a79f78c0eabed35f6dc8d030b322d0d/sample/src/main/res/drawable-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/sample/src/main/res/drawable-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ufo22940268/android-justifiedtextview/9329c7f68a79f78c0eabed35f6dc8d030b322d0d/sample/src/main/res/drawable-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/sample/src/main/res/layout/activity_demo.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
8 |
9 |
15 |
16 |
22 |
23 |
24 |
--------------------------------------------------------------------------------
/sample/src/main/res/layout/activity_main.xml:
--------------------------------------------------------------------------------
1 |
8 |
9 |
--------------------------------------------------------------------------------
/sample/src/main/res/layout/activity_text.xml:
--------------------------------------------------------------------------------
1 |
16 |
17 |
21 |
22 |
31 |
32 |
40 |
41 |
49 |
50 |
51 |
52 |
61 |
62 |
70 |
71 |
79 |
80 |
81 |
82 |
83 |
--------------------------------------------------------------------------------
/sample/src/main/res/menu/main.xml:
--------------------------------------------------------------------------------
1 |
11 |
--------------------------------------------------------------------------------
/sample/src/main/res/values-w820dp/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 | 64dp
6 |
7 |
--------------------------------------------------------------------------------
/sample/src/main/res/values/attrs.xml:
--------------------------------------------------------------------------------
1 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/sample/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #f00
4 |
--------------------------------------------------------------------------------
/sample/src/main/res/values/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 16dp
4 | 16dp
5 |
6 |
7 |
--------------------------------------------------------------------------------
/sample/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | justifytextview
5 | Hello world!
6 | Settings
7 |
8 | Tests whether this string matches the given regularExpression. This method returns true only if the regular expression matches the entire input string. A common mistake is to assume that this method behaves like contains(CharSequence); if you want to match anywhere within the input string, you need to add .* to the beginning and end of your regular expression. See matches(String, CharSequence).
9 |
10 | 预览版目前已经包括Android Wear最新API,这些新API可以让开发者以语音的方式定制和扩展他们的通知。其提供的仿真器可用于预览包括方形和圆形可穿戴设备上出现的通知方式,与此同时,谷歌也希望在未来几个月内继续为Android Wear设备提供新的API和功能。
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/sample/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/settings.gradle:
--------------------------------------------------------------------------------
1 | include 'justifytext-library', 'sample'
2 |
--------------------------------------------------------------------------------
/texttest.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/zh.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ufo22940268/android-justifiedtextview/9329c7f68a79f78c0eabed35f6dc8d030b322d0d/zh.png
--------------------------------------------------------------------------------