├── app
├── .gitignore
├── src
│ └── main
│ │ ├── res
│ │ ├── values
│ │ │ ├── strings.xml
│ │ │ ├── colors.xml
│ │ │ ├── dimens.xml
│ │ │ └── styles.xml
│ │ ├── mipmap-hdpi
│ │ │ └── ic_launcher.png
│ │ ├── mipmap-mdpi
│ │ │ └── ic_launcher.png
│ │ ├── mipmap-xxxhdpi
│ │ │ ├── ic_face.jpg
│ │ │ ├── ic_test.jpg
│ │ │ └── ic_launcher.png
│ │ ├── mipmap-xhdpi
│ │ │ └── ic_launcher.png
│ │ ├── mipmap-xxhdpi
│ │ │ └── ic_launcher.png
│ │ ├── values-w820dp
│ │ │ └── dimens.xml
│ │ └── layout
│ │ │ └── activity_main.xml
│ │ ├── assets
│ │ └── fonts
│ │ │ └── Aileron-Light.otf
│ │ ├── AndroidManifest.xml
│ │ └── java
│ │ └── com
│ │ └── zrq
│ │ └── spanbuilderdemo
│ │ └── MainActivity.java
├── proguard-rules.pro
└── build.gradle
├── spanbuilder
├── .gitignore
├── src
│ └── main
│ │ ├── res
│ │ └── values
│ │ │ └── strings.xml
│ │ ├── AndroidManifest.xml
│ │ └── java
│ │ └── com
│ │ └── zrq
│ │ └── spanbuilder
│ │ ├── CustomTypefaceSpan.kt
│ │ ├── Span.kt
│ │ └── Spans.kt
├── proguard-rules.pro
└── build.gradle
├── settings.gradle
├── screenshots
├── all.jpg
├── common.jpg
├── qrcode.png
└── app-debug.apk
├── gradle
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── .gitignore
├── gradle.properties
├── README.md
├── gradlew.bat
└── gradlew
/app/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/spanbuilder/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':app', ':spanbuilder'
2 |
--------------------------------------------------------------------------------
/screenshots/all.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zrq1060/SpanBuilder/HEAD/screenshots/all.jpg
--------------------------------------------------------------------------------
/screenshots/common.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zrq1060/SpanBuilder/HEAD/screenshots/common.jpg
--------------------------------------------------------------------------------
/screenshots/qrcode.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zrq1060/SpanBuilder/HEAD/screenshots/qrcode.png
--------------------------------------------------------------------------------
/screenshots/app-debug.apk:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zrq1060/SpanBuilder/HEAD/screenshots/app-debug.apk
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zrq1060/SpanBuilder/HEAD/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/app/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | SpanBuilderDemo
3 |
4 |
--------------------------------------------------------------------------------
/spanbuilder/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | SpanBuilder
3 |
4 |
--------------------------------------------------------------------------------
/app/src/main/assets/fonts/Aileron-Light.otf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zrq1060/SpanBuilder/HEAD/app/src/main/assets/fonts/Aileron-Light.otf
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zrq1060/SpanBuilder/HEAD/app/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zrq1060/SpanBuilder/HEAD/app/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_face.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zrq1060/SpanBuilder/HEAD/app/src/main/res/mipmap-xxxhdpi/ic_face.jpg
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_test.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zrq1060/SpanBuilder/HEAD/app/src/main/res/mipmap-xxxhdpi/ic_test.jpg
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zrq1060/SpanBuilder/HEAD/app/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zrq1060/SpanBuilder/HEAD/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zrq1060/SpanBuilder/HEAD/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | *.iml
2 | .gradle
3 | /local.properties
4 | /.idea/workspace.xml
5 | /.idea/libraries
6 | .DS_Store
7 | /build
8 | /captures
9 | .externalNativeBuild
10 |
--------------------------------------------------------------------------------
/app/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #3F51B5
4 | #303F9F
5 | #FF4081
6 |
7 |
--------------------------------------------------------------------------------
/app/src/main/res/values/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 16dp
4 | 16dp
5 |
6 |
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Fri Sep 25 16:21:01 CST 2020
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-6.6.1-all.zip
7 |
--------------------------------------------------------------------------------
/spanbuilder/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/app/src/main/res/values-w820dp/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 | 64dp
6 |
7 |
--------------------------------------------------------------------------------
/app/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
10 |
11 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/app/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # By default, the flags in this file are appended to flags specified
3 | # in E:\Android-SDK/tools/proguard/proguard-android.txt
4 | # You can edit the include path and order by changing the proguardFiles
5 | # directive in build.gradle.
6 | #
7 | # For more details, see
8 | # http://developer.android.com/guide/developing/tools/proguard.html
9 |
10 | # Add any project specific keep options here:
11 |
12 | # If your project uses WebView with JS, uncomment the following
13 | # and specify the fully qualified class name to the JavaScript interface
14 | # class:
15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
16 | # public *;
17 | #}
18 |
--------------------------------------------------------------------------------
/spanbuilder/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # By default, the flags in this file are appended to flags specified
3 | # in E:\Android-SDK/tools/proguard/proguard-android.txt
4 | # You can edit the include path and order by changing the proguardFiles
5 | # directive in build.gradle.
6 | #
7 | # For more details, see
8 | # http://developer.android.com/guide/developing/tools/proguard.html
9 |
10 | # Add any project specific keep options here:
11 |
12 | # If your project uses WebView with JS, uncomment the following
13 | # and specify the fully qualified class name to the JavaScript interface
14 | # class:
15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
16 | # public *;
17 | #}
18 |
--------------------------------------------------------------------------------
/spanbuilder/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.library'
2 | apply plugin: 'kotlin-android'
3 | apply plugin: "com.vanniktech.maven.publish"
4 |
5 | android {
6 | compileSdkVersion 30
7 |
8 | defaultConfig {
9 | minSdkVersion 14
10 | targetSdkVersion 30
11 | versionCode 1
12 | versionName "1.0.0"
13 | }
14 |
15 | buildTypes {
16 | release {
17 | minifyEnabled false
18 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
19 | }
20 | }
21 |
22 | lintOptions {
23 | abortOnError false
24 | }
25 | }
26 |
27 | dependencies {
28 | implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
29 | }
--------------------------------------------------------------------------------
/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/app/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.application'
2 |
3 | android {
4 | compileSdkVersion 27
5 | defaultConfig {
6 | applicationId "com.zrq.spanbuilderdemo"
7 | minSdkVersion 16
8 | targetSdkVersion 27
9 | versionCode 1
10 | versionName "1.0"
11 | }
12 | buildTypes {
13 | release {
14 | minifyEnabled false
15 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
16 | }
17 | }
18 | }
19 |
20 | dependencies {
21 | implementation fileTree(include: ['*.jar'], dir: 'libs')
22 | implementation 'com.android.support:appcompat-v7:27.1.1'
23 | // implementation project(':spanbuilder')
24 | // implementation 'com.zrq:spans:1.1.0'
25 | implementation 'io.github.zrq1060:spans:1.1.0'
26 | }
27 |
--------------------------------------------------------------------------------
/spanbuilder/src/main/java/com/zrq/spanbuilder/CustomTypefaceSpan.kt:
--------------------------------------------------------------------------------
1 | package com.zrq.spanbuilder
2 |
3 | import android.graphics.Paint
4 | import android.graphics.Typeface
5 | import android.text.TextPaint
6 | import android.text.style.MetricAffectingSpan
7 |
8 | /**
9 | * 描述:可设置自定义Typeface的span
10 | *
11 | * @author zhangrq
12 | * 2016/10/13 18:03
13 | */
14 | class CustomTypefaceSpan(private val typeface: Typeface) : MetricAffectingSpan() {
15 | override fun updateDrawState(drawState: TextPaint) {
16 | apply(drawState)
17 | }
18 |
19 | override fun updateMeasureState(paint: TextPaint) {
20 | apply(paint)
21 | }
22 |
23 | private fun apply(paint: Paint) {
24 | val oldTypeface = paint.typeface
25 | val oldStyle = oldTypeface?.style ?: 0
26 | val fakeStyle = oldStyle and typeface.style.inv()
27 | if (fakeStyle and Typeface.BOLD != 0) {
28 | paint.isFakeBoldText = true
29 | }
30 | if (fakeStyle and Typeface.ITALIC != 0) {
31 | paint.textSkewX = -0.25f
32 | }
33 | paint.typeface = typeface
34 | }
35 | }
--------------------------------------------------------------------------------
/gradle.properties:
--------------------------------------------------------------------------------
1 | # Project-wide Gradle settings.
2 |
3 | # IDE (e.g. Android Studio) users:
4 | # Gradle settings configured through the IDE *will override*
5 | # any settings specified in this file.
6 |
7 | # For more details on how to configure your build environment visit
8 | # http://www.gradle.org/docs/current/userguide/build_environment.html
9 |
10 | # Specifies the JVM arguments used for the daemon process.
11 | # The setting is particularly useful for tweaking memory settings.
12 | org.gradle.jvmargs=-Xmx1536m
13 |
14 | # When configured, Gradle will run in incubating parallel mode.
15 | # This option should only be used with decoupled projects. More details, visit
16 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
17 | # org.gradle.parallel=true
18 | GROUP=io.github.zrq1060
19 | POM_ARTIFACT_ID=spans
20 | VERSION_NAME=1.1.0
21 |
22 | POM_NAME=spans
23 | POM_DESCRIPTION=android span builder
24 | POM_INCEPTION_YEAR=2021
25 | POM_URL=https://github.com/zrq1060/SpanBuilder/
26 |
27 | POM_LICENCE_NAME=The Apache Software License, Version 2.0
28 | POM_LICENCE_URL=https://www.apache.org/licenses/LICENSE-2.0.txt
29 | POM_LICENCE_DIST=repo
30 |
31 | POM_SCM_URL=https://github.com/zrq1060/SpanBuilder/
32 | POM_SCM_CONNECTION=scm:git:git://github.com/zrq1060/SpanBuilder.git
33 | POM_SCM_DEV_CONNECTION=scm:git:ssh://git@github.com/zrq1060/SpanBuilder.git
34 |
35 | POM_DEVELOPER_ID=zrq1060
36 | POM_DEVELOPER_NAME=zrq1060
37 | POM_DEVELOPER_URL=https://github.com/zrq1060/
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_main.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
11 |
12 |
19 |
20 |
27 |
28 |
35 |
36 |
44 |
45 |
53 |
54 |
62 |
63 |
64 |
65 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # SpanBuilder
2 |
3 | ## 特点
4 |
5 | * **内置大量常用样式**
6 | * **支持自定义样式**
7 |
8 | ## Demo
9 |
10 | ### 下载
11 | 
12 |
13 | ### 项目演示
14 |
15 | #### 常用效果
16 |
17 |
18 | #### 全部效果
19 |
20 |
21 | ## 简单使用
22 |
23 | ### 引入
24 |
25 | project **build.gradle**
26 |
27 | ```groovy
28 | allprojects {
29 | repositories {
30 | mavenCentral()
31 | }
32 | }
33 | ```
34 |
35 | app **build.gradle**
36 |
37 | ```groovy
38 | implementation 'io.github.zrq1060:spans:1.1.0'
39 | ```
40 |
41 | ### 使用
42 | ```java
43 | textView.setText(Spans.builder()
44 | .text("8")
45 | .text(".88").size(28).color(Color.RED)
46 | .text("%").size(16).color(Color.BLACK)
47 | .build());
48 | ```
49 |
50 | ## 方法说明
51 | | 方法 | 说明 |
52 | | :-------- | :--------|
53 | | text() | 设置拼接内容,下面的方法全部作用于此text |
54 | | size() | 设置字体大小,单位sp |
55 | | sizePx() | 设置字体大小,单位px |
56 | | color() | 设置字体颜色 |
57 | | style() | 设置字体样式 (Typeface NORMAL:正常,BOLD:粗体,ITALIC:斜体,BOLD_ITALIC:粗斜体) |
58 | | backgroundColor() | 设置字体背景 |
59 | | typeface() | 设置自定义的ttf、otf字体 |
60 | | appearance() | 设置字体外貌 |
61 | | fontFamily() | 设置字体Family (可设置"monospace", "serif", and "sans-serif"等) |
62 | | strikeThrough() | 设置删除线 |
63 | | underLine() | 设置下划线 |
64 | | quote() | 设置引用线颜色 |
65 | | alignment() | 设置对齐方式(ALIGN_NORMAL、ALIGN_OPPOSITE、ALIGN_CENTER) |
66 | | relativeSize() | 设置X倍字体 |
67 | | scaleX() | 设置X轴缩放x倍 |
68 | | superscript() | 设置为上标 |
69 | | subscript() | 设置为下标 |
70 | | click() | 设置可点击 |
71 | | url() | 设置Url,点击会打开网页 |
72 | | drawableMargin() | 可设置Drawable,不会替换内容,顶部对齐,文字在图片右边 |
73 | | iconMargin() | 可设置Bitmap,不会替换内容,顶部对齐,文字在图片右边 |
74 | | dynamicDrawable() | 可设置Drawable,会替换内容,底部对齐,文字环绕嵌入型 |
75 | | image() | 可设置Drawable、Bitmap、Uri、resourceId,会替换内容,基线对齐,文字环绕嵌入型 |
76 | | bullet() | 设置子弹,可设置gapWidth、color |
77 | | leadingMargin() | 设置行前空隙 |
78 | | tabStop() | 设置制表符位置 |
79 | | blurMask() | 设置字体模糊 |
80 | | setSpanAll() | 设置自定义样式,作用于当前text()全部内容 |
81 | | setSpanPart() | 设置自定义样式,作用于当前text()部分内容 |
82 | | build() | |
83 |
84 |
85 | ## 联系我
86 | QQ:273902141
87 |
88 | 邮箱:zrq1060@163.com
89 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 | # Attempt to set APP_HOME
46 | # Resolve links: $0 may be a link
47 | PRG="$0"
48 | # Need this for relative symlinks.
49 | while [ -h "$PRG" ] ; do
50 | ls=`ls -ld "$PRG"`
51 | link=`expr "$ls" : '.*-> \(.*\)$'`
52 | if expr "$link" : '/.*' > /dev/null; then
53 | PRG="$link"
54 | else
55 | PRG=`dirname "$PRG"`"/$link"
56 | fi
57 | done
58 | SAVED="`pwd`"
59 | cd "`dirname \"$PRG\"`/" >/dev/null
60 | APP_HOME="`pwd -P`"
61 | cd "$SAVED" >/dev/null
62 |
63 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
64 |
65 | # Determine the Java command to use to start the JVM.
66 | if [ -n "$JAVA_HOME" ] ; then
67 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
68 | # IBM's JDK on AIX uses strange locations for the executables
69 | JAVACMD="$JAVA_HOME/jre/sh/java"
70 | else
71 | JAVACMD="$JAVA_HOME/bin/java"
72 | fi
73 | if [ ! -x "$JAVACMD" ] ; then
74 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
75 |
76 | Please set the JAVA_HOME variable in your environment to match the
77 | location of your Java installation."
78 | fi
79 | else
80 | JAVACMD="java"
81 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
82 |
83 | Please set the JAVA_HOME variable in your environment to match the
84 | location of your Java installation."
85 | fi
86 |
87 | # Increase the maximum file descriptors if we can.
88 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
89 | MAX_FD_LIMIT=`ulimit -H -n`
90 | if [ $? -eq 0 ] ; then
91 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
92 | MAX_FD="$MAX_FD_LIMIT"
93 | fi
94 | ulimit -n $MAX_FD
95 | if [ $? -ne 0 ] ; then
96 | warn "Could not set maximum file descriptor limit: $MAX_FD"
97 | fi
98 | else
99 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
100 | fi
101 | fi
102 |
103 | # For Darwin, add options to specify how the application appears in the dock
104 | if $darwin; then
105 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
106 | fi
107 |
108 | # For Cygwin, switch paths to Windows format before running java
109 | if $cygwin ; then
110 | APP_HOME=`cygpath --path --mixed "$APP_HOME"`
111 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
112 | JAVACMD=`cygpath --unix "$JAVACMD"`
113 |
114 | # We build the pattern for arguments to be converted via cygpath
115 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
116 | SEP=""
117 | for dir in $ROOTDIRSRAW ; do
118 | ROOTDIRS="$ROOTDIRS$SEP$dir"
119 | SEP="|"
120 | done
121 | OURCYGPATTERN="(^($ROOTDIRS))"
122 | # Add a user-defined pattern to the cygpath arguments
123 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then
124 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
125 | fi
126 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
127 | i=0
128 | for arg in "$@" ; do
129 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
130 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
131 |
132 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
133 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
134 | else
135 | eval `echo args$i`="\"$arg\""
136 | fi
137 | i=$((i+1))
138 | done
139 | case $i in
140 | (0) set -- ;;
141 | (1) set -- "$args0" ;;
142 | (2) set -- "$args0" "$args1" ;;
143 | (3) set -- "$args0" "$args1" "$args2" ;;
144 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
145 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
146 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
147 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
148 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
149 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
150 | esac
151 | fi
152 |
153 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
154 | function splitJvmOpts() {
155 | JVM_OPTS=("$@")
156 | }
157 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
158 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
159 |
160 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
161 |
--------------------------------------------------------------------------------
/app/src/main/java/com/zrq/spanbuilderdemo/MainActivity.java:
--------------------------------------------------------------------------------
1 | package com.zrq.spanbuilderdemo;
2 |
3 | import android.graphics.BitmapFactory;
4 | import android.graphics.BlurMaskFilter;
5 | import android.graphics.Color;
6 | import android.graphics.Typeface;
7 | import android.graphics.drawable.Drawable;
8 | import android.os.Bundle;
9 | import android.support.annotation.NonNull;
10 | import android.support.v7.app.AppCompatActivity;
11 | import android.text.Layout;
12 | import android.text.TextPaint;
13 | import android.text.style.ClickableSpan;
14 | import android.text.style.DynamicDrawableSpan;
15 | import android.text.style.URLSpan;
16 | import android.view.View;
17 | import android.widget.TextView;
18 | import android.widget.Toast;
19 |
20 | import com.zrq.spanbuilder.Spans;
21 |
22 | public class MainActivity extends AppCompatActivity {
23 |
24 | @Override
25 | protected void onCreate(Bundle savedInstanceState) {
26 | super.onCreate(savedInstanceState);
27 | setContentView(R.layout.activity_main);
28 | TextView textView1 = findViewById(R.id.textView1);
29 | TextView textView2 = findViewById(R.id.textView2);
30 | TextView textView3 = findViewById(R.id.textView3);
31 | TextView textView4 = findViewById(R.id.textView4);
32 | TextView textView5 = findViewById(R.id.textView5);
33 | TextView textViewAll = findViewById(R.id.textViewAll);
34 | final Drawable drawable = getResources().getDrawable(R.mipmap.ic_test);
35 | // 获取颜色的方法:
36 | // 1.Color.RED;
37 | // 2.Color.parseColor("#123456");
38 | // 3.ContextCompat.getColor(getApplicationContext(), R.color.colorPrimary);
39 | // 4.getResources().getColor(R.color.colorPrimary)
40 |
41 | // Spans的常用样式
42 | textView1.setText(Spans.builder()
43 | .text("8")
44 | .text(".88").size(28).color(Color.RED)
45 | .text("%").size(16).color(Color.BLACK)
46 | .build());
47 |
48 | textView2.setText(Spans.builder()
49 | .text("10").size(50).color(Color.RED).style(Typeface.BOLD)
50 | .text("元")
51 | .build());
52 |
53 | textView3.setText(Spans.builder()
54 | .text("¥149").size(24).color(Color.RED)
55 | .text(".9 ").size(16).color(Color.RED)
56 | .text("¥259.00").size(20).color(Color.BLACK).strikeThrough()
57 | .text(" 4738").size(20).color(Color.RED).style(Typeface.BOLD_ITALIC)
58 | .text("件已售").size(20).color(Color.BLACK)
59 | .build());
60 |
61 | textView4.setText(Spans.builder()
62 | .text("请阅读")
63 | .text("《隐私权政策》").click(textView4, new ClickableSpan() {
64 | @Override
65 | public void onClick(@NonNull View widget) {
66 | Toast.makeText(getApplicationContext(), "隐私权政策", Toast.LENGTH_SHORT).show();
67 | }
68 |
69 | @Override
70 | public void updateDrawState(@NonNull TextPaint ds) {
71 | ds.setColor(Color.RED);
72 | ds.setUnderlineText(false);
73 | }
74 | })
75 | .text("和")
76 | .text("《用户协议》").click(textView4, new ClickableSpan() {
77 | @Override
78 | public void onClick(@NonNull View widget) {
79 | Toast.makeText(getApplicationContext(), "用户协议", Toast.LENGTH_SHORT).show();
80 | }
81 |
82 | @Override
83 | public void updateDrawState(@NonNull TextPaint ds) {
84 | ds.setColor(Color.RED);
85 | ds.setUnderlineText(false);
86 | }
87 | })
88 | .text(" ")
89 | .build());
90 |
91 | textView5.setText(Spans.builder()
92 | .text("表情前")
93 | .text("表情内容").image(getApplicationContext(), R.mipmap.ic_face)
94 | .text("表情后")
95 | .build());
96 |
97 | // Spans的全部样式
98 | textViewAll.setText(Spans.builder()
99 | .text("--!!下面内容全部是在一个TextView上--").backgroundColor(Color.RED)
100 | .text("\n字体大小:").text("24SP大小 ").size(24).text("24PX大小").sizePx(24)
101 | .text("\n字体颜色:").text("颜色").color(Color.RED)
102 | .text("\n字体样式:").text("粗体 ").style(Typeface.BOLD).text("斜体 ").style(Typeface.ITALIC).text("粗斜体").style(Typeface.BOLD_ITALIC)
103 | .text("\n------------------------------------------------------------")
104 | .text("\n字体背景:").text("背景").backgroundColor(Color.RED)
105 | .text("\n自定义字体:Hello Word ").text("Hello Word").typeface(Typeface.createFromAsset(getAssets(), "fonts/Aileron-Light.otf"))
106 | .text("\n字体外貌:").text("外貌").appearance(this, android.R.style.TextAppearance_Small)
107 | .text("\n字体Family:").text("字体Family").fontFamily("monospace")
108 | .text("\n------------------------------------------------------------")
109 | .text("\n删除线:").text("删除线").strikeThrough()
110 | .text("\n下划线:").text("下划线").underLine()
111 | .text("\n引用线").quote(Color.BLUE)
112 | .text("\n------------------------------------------------------------")
113 | .text("\n对齐方式:")
114 | .text("\n正常对齐").alignment(Layout.Alignment.ALIGN_NORMAL)
115 | .text("\n反向对齐").alignment(Layout.Alignment.ALIGN_OPPOSITE)
116 | .text("\n居中对齐").alignment(Layout.Alignment.ALIGN_CENTER)
117 | .text("\n------------------------------------------------------------")
118 | .text("\nX倍字体:").text("2倍字体").relativeSize(2)
119 | .text("\nX轴缩放:").text("X轴缩0.5倍").scaleX(0.5f)
120 | .text("\n------------------------------------------------------------")
121 | .text("\n上标:").text("X").text("2").superscript()
122 | .text("\n下标:").text("X").text("2").subscript()
123 | .text("\n------------------------------------------------------------")
124 | .text("\n字体点击:").text("协议").click(textViewAll, new ClickableSpan() {
125 | @Override
126 | public void onClick(@NonNull View widget) {
127 | Toast.makeText(getApplicationContext(), "协议", Toast.LENGTH_SHORT).show();
128 | }
129 | }).text(" ")// 点击协议后面也会触发点击事件,解决办法协议后面需有不可点击内容
130 | .text("\nURL点击:").text("百度").url(textViewAll, new URLSpan("http://www.baidu.com")).text(" ")// 点击百度后面也会触发点击事件,解决办法百度后面需有不可点击内容
131 | .text("\n------------------------------------------------------------")
132 | .text("\n设置图片:\n")
133 | .text("图片内容,").drawableMargin(drawable).text("可设置Drawable,不会替换内容,顶部对齐,文字在图片右边\n")// 设置 Drawable,不会替换内容
134 | .text("图片内容,").iconMargin(BitmapFactory.decodeResource(getResources(), R.mipmap.ic_test)).text("可设置Bitmap,不会替换内容,顶部对齐,文字在图片右边\n")// 设置 Bitmap,不会替换内容
135 | .text("前前前前").text("图片内容").dynamicDrawable(new DynamicDrawableSpan(DynamicDrawableSpan.ALIGN_BOTTOM) {
136 | @Override
137 | public Drawable getDrawable() {
138 | drawable.setBounds(0, 0, drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight());
139 | return drawable;// 需要设置setBounds,会替换内容
140 | }
141 | }).text("后后后后,").text("可设置Drawable,会替换内容,底部对齐,文字环绕嵌入型\n")
142 | .text("前前前前").text("图片内容").image(drawable, DynamicDrawableSpan.ALIGN_BASELINE).text("后后后后,").text("可设置Drawable、Bitmap、Uri、resourceId,会替换内容,基线对齐,文字环绕嵌入型")// 设置 Bitmap,会替换内容
143 | .text("\n------------------------------------------------------------")
144 | .text("\n子弹:看不出效果,需前面无内容").bullet(2, Color.RED)// 前面不能设置内容
145 | .text("\n行前空隙:内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容").leadingMargin(100, 30)
146 | .text("\n制表符位置:内容\t内容内容内容内容内容内容内容内\t容内容内容内容内容内容内容").tabStop(900)
147 | .text("\n字体模糊:").text("容内容内容内容内容内容内容内容内容内容内容").blurMask(30, BlurMaskFilter.Blur.OUTER)
148 | .build());
149 |
150 | // -------------------方法介绍:----------------------
151 | // .text() // 设置拼接内容,下面的方法全部作用于此text
152 | // .size() // 设置字体大小,单位sp
153 | // .sizePx() // 设置字体大小,单位px
154 | // .color() // 设置字体颜色
155 | // .style() // 设置字体样式 (Typeface NORMAL:正常,BOLD:粗体,ITALIC:斜体,BOLD_ITALIC:粗斜体)
156 |
157 | // .backgroundColor() // 设置字体背景
158 | // .typeface() // 设置自定义的ttf、otf字体
159 | // .appearance() // 设置字体外貌
160 | // .fontFamily() // 设置字体Family (可设置"monospace", "serif", and "sans-serif"等)
161 |
162 | // .strikeThrough() // 设置删除线
163 | // .underLine() // 设置下划线
164 | // .quote() // 设置引用线颜色
165 |
166 | // .alignment() // 设置对齐方式(ALIGN_NORMAL、ALIGN_OPPOSITE、ALIGN_CENTER)
167 |
168 | // .relativeSize() // 设置X倍字体
169 | // .scaleX() // 设置X轴缩放x倍
170 |
171 | // .superscript() // 设置为上标
172 | // .subscript() // 设置为下标
173 |
174 | // .click() // 设置可点击
175 | // .url() // 设置Url,点击会打开网页
176 |
177 | // .drawableMargin() // 可设置Drawable,不会替换内容,顶部对齐,文字在图片右边
178 | // .iconMargin() // 可设置Bitmap,不会替换内容,顶部对齐,文字在图片右边
179 | // .dynamicDrawable() // 可设置Drawable,会替换内容,底部对齐,文字环绕嵌入型
180 | // .image() // 可设置Drawable、Bitmap、Uri、resourceId,会替换内容,基线对齐,文字环绕嵌入型
181 |
182 | // .bullet() // 设置子弹,可设置gapWidth、color
183 | // .leadingMargin() // 设置行前空隙
184 | // .tabStop() // 设置制表符位置
185 | // .blurMask() // 设置字体模糊
186 |
187 |
188 | // .setSpanAll() // 设置自定义样式,作用于当前text()全部内容
189 | // .setSpanPart() // 设置自定义样式,作用于当前text()部分内容
190 | // .build()
191 |
192 |
193 | }
194 | }
195 |
--------------------------------------------------------------------------------
/spanbuilder/src/main/java/com/zrq/spanbuilder/Span.kt:
--------------------------------------------------------------------------------
1 | package com.zrq.spanbuilder
2 |
3 | import android.content.Context
4 | import android.graphics.Bitmap
5 | import android.graphics.BlurMaskFilter
6 | import android.graphics.Color
7 | import android.graphics.Typeface
8 | import android.graphics.drawable.Drawable
9 | import android.net.Uri
10 | import android.text.Layout
11 | import android.text.SpannableString
12 | import android.text.Spanned.*
13 | import android.text.method.LinkMovementMethod
14 | import android.text.style.*
15 | import android.widget.TextView
16 |
17 | /**
18 | * 描述: Span构建者,内部封装了常用的样式
19 | *
20 | *
21 | * 邮箱:zrq1060@163.com
22 | *
23 | * @author zhangrq
24 | * 2016/9/30 11:50
25 | */
26 | class Span(source: CharSequence?) : SpannableString(source ?: "") {
27 | /**
28 | * 设置字体大小
29 | *
30 | * [size] 设置字体大小(单位sp)
31 | */
32 | fun setTextSize(size: Int): Span {
33 | setOneSpanAll(AbsoluteSizeSpan(size, true))
34 | return this
35 | }
36 |
37 | /**
38 | * 设置字体大小
39 | *
40 | * [size] 设置字体大小(单位px)
41 | */
42 | fun setTextSizePx(size: Int): Span {
43 | setOneSpanAll(AbsoluteSizeSpan(size, false))
44 | return this
45 | }
46 |
47 | /**
48 | * 设置字体颜色
49 | *
50 | * [color] 字体颜色
51 | */
52 | fun setTextColor(color: Int): Span {
53 | setOneSpanAll(ForegroundColorSpan(color))
54 | return this
55 | }
56 |
57 | /**
58 | * 设置背景颜色
59 | *
60 | * [color] 背景颜色
61 | */
62 | fun setBackgroundColor(color: Int): Span {
63 | setOneSpanAll(BackgroundColorSpan(color))
64 | return this
65 | }
66 |
67 | /**
68 | * 设置字体类型
69 | *
70 | * [style] 字体类型 [android.graphics.Typeface] NORMAL:正常,BOLD:粗体,ITALIC:斜体,BOLD_ITALIC:粗斜体
71 | */
72 | fun setTextStyle(style: Int): Span {
73 | setOneSpanAll(StyleSpan(style))
74 | return this
75 | }
76 |
77 | /**
78 | * 设置自定义的字体类型
79 | *
80 | * [typeface] 设置字体类型
81 | */
82 | fun setTypeface(typeface: Typeface): Span {
83 | setOneSpanAll(CustomTypefaceSpan(typeface))
84 | return this
85 | }
86 |
87 | /**
88 | * 设置字体style样式的外观
89 | *
90 | * [styleId] 字体style样式的id
91 | */
92 | fun setTextAppearance(context: Context, styleId: Int): Span {
93 | setOneSpanAll(TextAppearanceSpan(context, styleId))
94 | return this
95 | }
96 |
97 | /**
98 | * 设置系统自带字体类型
99 | *
100 | * [family] 设置字体 "monospace", "serif", and "sans-serif"等
101 | */
102 | fun setFontFamily(family: String?): Span {
103 | setOneSpanAll(TypefaceSpan(family))
104 | return this
105 | }
106 |
107 | /**
108 | * 设置点击
109 | * (默认是有下划线和字体颜色的,如需更改,则覆盖clickableSpan对象的updateDrawState方法 )设置如下:
110 | * public void updateDrawState(TextPaint ds) {
111 | * ds.setColor(ds.linkColor);
112 | * ds.setUnderlineText(false);
113 | * }
114 | *
115 | * [textView] 设置可点击的textView
116 | * [clickableSpan] 点击的span
117 | */
118 | fun setClick(textView: TextView?, clickableSpan: ClickableSpan?): Span {
119 | if (textView != null) {
120 | textView.movementMethod = LinkMovementMethod.getInstance()
121 | textView.highlightColor = Color.TRANSPARENT
122 | }
123 | setOneSpanAll(clickableSpan)
124 | return this
125 | }
126 |
127 | /**
128 | * 设置删除线的样式
129 | */
130 | fun setStrikeThrough(): Span {
131 | setOneSpanAll(StrikethroughSpan())
132 | return this
133 | }
134 |
135 | /**
136 | * 设置下划线的样式
137 | */
138 | fun setUnderLine(): Span {
139 | setOneSpanAll(UnderlineSpan())
140 | return this
141 | }
142 |
143 | /**
144 | * 设置垂直的引用线
145 | *
146 | * [color] 设置垂直的引用线的颜色
147 | */
148 | fun setQuote(color: Int): Span {
149 | setOneSpanAll(QuoteSpan(color))
150 | return this
151 | }
152 |
153 | /**
154 | * 设置字体内容的对其方式
155 | *
156 | * [align] 设置对其方式 ALIGN_NORMAL, ALIGN_OPPOSITE, ALIGN_CENTER,
157 | */
158 | fun setAlignment(align: Layout.Alignment): Span {
159 | setOneSpanAll(AlignmentSpan.Standard(align))
160 | return this
161 | }
162 |
163 | /**
164 | * 设置字体的相对大小
165 | *
166 | * [proportion] 设置相对textSize的比例大小
167 | */
168 | fun setRelativeSize(proportion: Float): Span {
169 | setOneSpanAll(RelativeSizeSpan(proportion))
170 | return this
171 | }
172 |
173 | /**
174 | * 设置字体为上标的样式
175 | */
176 | fun setSuperscript(): Span {
177 | setOneSpanAll(SuperscriptSpan())
178 | return this
179 | }
180 |
181 | /**
182 | * 设置字体为下标的样式
183 | */
184 | fun setSubscript(): Span {
185 | setOneSpanAll(SubscriptSpan())
186 | return this
187 | }
188 |
189 | /**
190 | * 设置字体X轴缩放
191 | *
192 | * [proportion] 缩放的倍数
193 | */
194 | fun setScaleX(proportion: Float): Span {
195 | setOneSpanAll(ScaleXSpan(proportion))
196 | return this
197 | }
198 |
199 | /**
200 | * 设置URL,点击会打开浏览器展示
201 | *
202 | * [urlSpan] 如需修改字体颜色等,请参考[setClick]方法
203 | */
204 | fun setURL(textView: TextView?, urlSpan: URLSpan): Span {
205 | if (textView != null) {
206 | textView.movementMethod = LinkMovementMethod.getInstance()
207 | textView.highlightColor = Color.TRANSPARENT
208 | }
209 | setOneSpanAll(urlSpan)
210 | return this
211 | }
212 |
213 | /**
214 | * 设置图片-Drawable,不需要设置setBounds,不会替换内容
215 | *
216 | * [drawable] 图片
217 | * [pad] 距离
218 | */
219 | @JvmOverloads
220 | fun setDrawableMargin(drawable: Drawable, pad: Int = 0): Span {
221 | setOneSpanAll(DrawableMarginSpan(drawable, pad))
222 | return this
223 | }
224 |
225 | /**
226 | * 设置图片-Bitmap,不会替换内容
227 | *
228 | * [bitmap] 图片
229 | * [pad] 距离
230 | */
231 | @JvmOverloads
232 | fun setIconMargin(bitmap: Bitmap, pad: Int = 0): Span {
233 | setOneSpanAll(IconMarginSpan(bitmap, pad))
234 | return this
235 | }
236 |
237 | /**
238 | * 设置图片-Drawable,需要设置setBounds,会替换内容
239 | *
240 | * [dynamicDrawableSpan] 提供Drawable的span
241 | */
242 | fun setDynamicDrawable(dynamicDrawableSpan: DynamicDrawableSpan): Span {
243 | setOneSpanAll(dynamicDrawableSpan)
244 | return this
245 | }
246 |
247 | /**
248 | * 设置图片-Bitmap,会替换内容
249 | *
250 | * [verticalAlignment] 垂直对齐方式:ALIGN_BOTTOM、ALIGN_BASELINE
251 | */
252 | @JvmOverloads
253 | fun setImage(context: Context, bitmap: Bitmap, verticalAlignment: Int = DynamicDrawableSpan.ALIGN_BOTTOM): Span {
254 | setOneSpanAll(ImageSpan(context, bitmap, verticalAlignment))
255 | return this
256 | }
257 |
258 | /**
259 | * 设置图片-Drawable,会替换内容。Drawable需设置setBounds。
260 | *
261 | * [verticalAlignment] 垂直对齐方式:ALIGN_BOTTOM、ALIGN_BASELINE
262 | */
263 | @JvmOverloads
264 | fun setImage(drawable: Drawable, source: String? = null, verticalAlignment: Int = DynamicDrawableSpan.ALIGN_BOTTOM): Span {
265 | setOneSpanAll(if (source == null) ImageSpan(drawable, verticalAlignment) else ImageSpan(drawable, source, verticalAlignment))
266 | return this
267 | }
268 |
269 | /**
270 | * 解决上述方法JvmOverloads后无此参数情况
271 | */
272 | fun setImage(drawable: Drawable, verticalAlignment: Int) = setImage(drawable, null, verticalAlignment)
273 |
274 | /**
275 | * 设置图片-Uri,会替换内容。
276 | *
277 | * [verticalAlignment] 垂直对齐方式:ALIGN_BOTTOM、ALIGN_BASELINE
278 | */
279 | @JvmOverloads
280 | fun setImage(context: Context, uri: Uri, verticalAlignment: Int = DynamicDrawableSpan.ALIGN_BOTTOM): Span {
281 | setOneSpanAll(ImageSpan(context, uri, verticalAlignment))
282 | return this
283 | }
284 |
285 | /**
286 | * 设置图片-resourceId,会替换内容。
287 | *
288 | * [verticalAlignment] 垂直对齐方式:ALIGN_BOTTOM、ALIGN_BASELINE
289 | */
290 | @JvmOverloads
291 | fun setImage(context: Context, resourceId: Int, verticalAlignment: Int = DynamicDrawableSpan.ALIGN_BOTTOM): Span {
292 | setOneSpanAll(ImageSpan(context, resourceId, verticalAlignment))
293 | return this
294 | }
295 |
296 | /**
297 | * 设置子弹
298 | */
299 | @JvmOverloads
300 | fun setBullet(gapWidth: Int = BulletSpan.STANDARD_GAP_WIDTH, color: Int? = null): Span {
301 | setOneSpanAll(if (color == null) BulletSpan(gapWidth) else BulletSpan(gapWidth, color))
302 | return this
303 | }
304 |
305 | /**
306 | * 设置行缩进
307 | * [first] 段落第一行的缩进距离
308 | * [rest] 段落其余行缩进距离
309 | */
310 | @JvmOverloads
311 | fun setLeadingMargin(first: Int, rest: Int = first): Span {
312 | setOneSpanAll(LeadingMarginSpan.Standard(first, rest))
313 | return this
314 | }
315 |
316 | /**
317 | * 设置一行上制表符位置
318 | * [where] 制表符与行前距离的偏移量
319 | */
320 | fun setTabStop(where: Int): Span {
321 | setOneSpanAll(TabStopSpan.Standard(where))
322 | return this
323 | }
324 |
325 | /**
326 | * 设置模糊
327 | * [radius] 模糊的半径
328 | * [style] 模糊的样式
329 | */
330 | fun setBlurMask(radius: Float, style: BlurMaskFilter.Blur): Span {
331 | setOneSpanAll(MaskFilterSpan(BlurMaskFilter(radius, style)))
332 | return this
333 | }
334 |
335 | /**
336 | * 设置一个样式-作用于text全部
337 | * [span] 一个span样式
338 | */
339 | private fun setOneSpanAll(span: Any?): Span {
340 | return setSpanPart(0, length, SPAN_EXCLUSIVE_EXCLUSIVE, span)
341 | }
342 |
343 | /**
344 | * 设置样式-作用于text全部
345 | * [flags] 默认:[SPAN_EXCLUSIVE_EXCLUSIVE]。常用:[SPAN_INCLUSIVE_INCLUSIVE]、[SPAN_EXCLUSIVE_EXCLUSIVE]、[SPAN_INCLUSIVE_EXCLUSIVE]、[SPAN_EXCLUSIVE_INCLUSIVE],详情请看:https://www.jianshu.com/p/1956e15c9a27
346 | * [spans] 多个span样式
347 | */
348 | @JvmOverloads
349 | fun setSpanAll(flags: Int = SPAN_EXCLUSIVE_EXCLUSIVE, vararg spans: Any?): Span {
350 | return setSpanPart(0, length, flags, *spans)
351 | }
352 |
353 | /**
354 | * 设置样式-作用于text文本start到end的位置
355 | *
356 | * [start] 从此位置开始设置样式
357 | * [end] 从此位置结束设置样式
358 | * [flags] 默认:[SPAN_EXCLUSIVE_EXCLUSIVE]。常用:[SPAN_INCLUSIVE_INCLUSIVE]、[SPAN_EXCLUSIVE_EXCLUSIVE]、[SPAN_INCLUSIVE_EXCLUSIVE]、[SPAN_EXCLUSIVE_INCLUSIVE],详情请看:https://www.jianshu.com/p/1956e15c9a27
359 | * [spans] 多个span样式
360 | */
361 | @JvmOverloads
362 | fun setSpanPart(start: Int, end: Int, flags: Int = SPAN_EXCLUSIVE_EXCLUSIVE, vararg spans: Any?): Span {
363 | if (start > end || spans.isEmpty()) return this
364 | for (span in spans) {
365 | if (span == null) continue
366 | super.setSpan(span, start, end, flags)
367 | }
368 | return this
369 | }
370 | }
--------------------------------------------------------------------------------
/spanbuilder/src/main/java/com/zrq/spanbuilder/Spans.kt:
--------------------------------------------------------------------------------
1 | package com.zrq.spanbuilder
2 |
3 | import android.content.Context
4 | import android.graphics.Bitmap
5 | import android.graphics.BlurMaskFilter
6 | import android.graphics.Typeface
7 | import android.graphics.drawable.Drawable
8 | import android.net.Uri
9 | import android.text.Layout
10 | import android.text.SpannableString.*
11 | import android.text.SpannableStringBuilder
12 | import android.text.style.*
13 | import android.widget.TextView
14 |
15 | /**
16 | * 描述:
17 | *
18 | * @author zhangrq
19 | * 2016/11/5 21:14
20 | */
21 | class Spans : SpannableStringBuilder() {
22 |
23 | /**
24 | * 构建者模式拼接Spans
25 | */
26 | class Builder {
27 | private var span = Span("")
28 | private var spans = Spans()
29 |
30 | /**
31 | * 设置文本内容
32 | * [text] 文本
33 | */
34 | fun text(text: CharSequence?): Builder {
35 | // 新文本起始设置,拼接上一个,创建新的Span
36 | appendOld() // 拼接上一个
37 | span = Span(text)
38 | return this
39 | }
40 |
41 | /**
42 | * 生成拼接好的Spans
43 | */
44 | fun build(): Spans {
45 | // 文本结束设置,拼接上一个,并返回结果
46 | appendOld() // 拼接上一个
47 | return spans
48 | }
49 |
50 | /**
51 | * 拼接上一个span
52 | */
53 | private fun appendOld() {
54 | if (span.length != 0) {
55 | // 无内容即无样式,不增加;有内容,增加,增加的是前一个的样式
56 | spans.append(span)
57 | }
58 | }
59 |
60 | /**
61 | * 设置字体大小
62 | *
63 | * [textSize] 设置字体大小(单位sp)
64 | */
65 | fun size(textSize: Int): Builder {
66 | span.setTextSize(textSize)
67 | return this
68 | }
69 |
70 | /**
71 | * 设置字体大小
72 | *
73 | * [textSize] 设置字体大小(单位Px)
74 | */
75 | fun sizePx(textSize: Int): Builder {
76 | span.setTextSizePx(textSize)
77 | return this
78 | }
79 |
80 | /**
81 | * 设置字体颜色
82 | *
83 | * [textColor] 字体颜色
84 | */
85 | fun color(textColor: Int): Builder {
86 | span.setTextColor(textColor)
87 | return this
88 | }
89 |
90 | /**
91 | * 设置背景颜色
92 | *
93 | * [color] 背景颜色
94 | */
95 | fun backgroundColor(color: Int): Builder {
96 | span.setBackgroundColor(color)
97 | return this
98 | }
99 |
100 | /**
101 | * 设置字体类型
102 | *
103 | * [style] 字体类型 [android.graphics.Typeface] NORMAL:正常,BOLD:粗体,ITALIC:斜体,BOLD_ITALIC:粗斜体
104 | */
105 | fun style(style: Int): Builder {
106 | span.setTextStyle(style)
107 | return this
108 | }
109 |
110 | /**
111 | * 设置自定义的字体类型
112 | *
113 | * [typeface] 设置字体类型
114 | */
115 | fun typeface(typeface: Typeface): Builder {
116 | span.setTypeface(typeface)
117 | return this
118 | }
119 |
120 | /**
121 | * 设置字体style样式的外观
122 | *
123 | * [styleId] 字体style样式的id
124 | */
125 | fun appearance(context: Context, styleId: Int): Builder {
126 | span.setTextAppearance(context, styleId)
127 | return this
128 | }
129 |
130 | /**
131 | * 设置系统自带字体类型
132 | *
133 | * [family] 设置字体 "monospace", "serif", and "sans-serif"等
134 | */
135 | fun fontFamily(family: String?): Builder {
136 | span.setFontFamily(family)
137 | return this
138 | }
139 |
140 | /**
141 | * 设置点击
142 | * (默认是有下划线和字体颜色的,如需更改,则覆盖clickableSpan对象的updateDrawState方法 )设置如下:
143 | * public void updateDrawState(TextPaint ds) {
144 | * ds.setColor(ds.linkColor);
145 | * ds.setUnderlineText(false);
146 | * }
147 | *
148 | * [textView] 设置可点击的textView
149 | * [clickableSpan] 点击的span
150 | */
151 | fun click(textView: TextView?, clickableSpan: ClickableSpan?): Builder {
152 | span.setClick(textView, clickableSpan)
153 | return this
154 | }
155 |
156 | /**
157 | * 设置删除线的样式
158 | */
159 | fun strikeThrough(): Builder {
160 | span.setStrikeThrough()
161 | return this
162 | }
163 |
164 | /**
165 | * 设置下划线的样式
166 | */
167 | fun underLine(): Builder {
168 | span.setUnderLine()
169 | return this
170 | }
171 |
172 | /**
173 | * 设置垂直的引用线
174 | *
175 | * [color] 设置垂直的引用线的颜色
176 | */
177 | fun quote(color: Int): Builder {
178 | span.setQuote(color)
179 | return this
180 | }
181 |
182 | /**
183 | * 设置字体内容的对其方式
184 | *
185 | * [align] 设置对其方式 ALIGN_NORMAL, ALIGN_OPPOSITE, ALIGN_CENTER,
186 | */
187 | fun alignment(align: Layout.Alignment): Builder {
188 | span.setAlignment(align)
189 | return this
190 | }
191 |
192 | /**
193 | * 设置字体的相对大小
194 | *
195 | * [proportion] 设置相对textSize的比例大小
196 | */
197 | fun relativeSize(proportion: Float): Builder {
198 | span.setRelativeSize(proportion)
199 | return this
200 | }
201 |
202 | /**
203 | * 设置字体为上标的样式
204 | */
205 | fun superscript(): Builder {
206 | span.setSuperscript()
207 | return this
208 | }
209 |
210 | /**
211 | * 设置字体为下标的样式
212 | */
213 | fun subscript(): Builder {
214 | span.setSubscript()
215 | return this
216 | }
217 |
218 | /**
219 | * 设置字体X轴缩放
220 | *
221 | * [proportion] 缩放的倍数
222 | */
223 | fun scaleX(proportion: Float): Builder {
224 | span.setScaleX(proportion)
225 | return this
226 | }
227 |
228 | /**
229 | * 设置URL,点击会打开浏览器展示
230 | *
231 | * [urlSpan] 如需修改字体颜色等,请参考[click]方法
232 | */
233 | fun url(textView: TextView?, urlSpan: URLSpan): Builder {
234 | span.setURL(textView,urlSpan)
235 | return this
236 | }
237 |
238 | /**
239 | * 设置图片-Drawable,不需要设置setBounds,不会替换内容
240 | *
241 | * [drawable] 图片
242 | * [pad] 距离
243 | */
244 | @JvmOverloads
245 | fun drawableMargin(drawable: Drawable, pad: Int = 0): Builder {
246 | span.setDrawableMargin(drawable, pad)
247 | return this
248 | }
249 |
250 | /**
251 | * 设置图片-Bitmap,不会替换内容
252 | *
253 | * [bitmap] 图片
254 | * [pad] 距离
255 | */
256 | @JvmOverloads
257 | fun iconMargin(bitmap: Bitmap, pad: Int = 0): Builder {
258 | span.setIconMargin(bitmap, pad)
259 | return this
260 | }
261 |
262 | /**
263 | * 设置图片-Drawable,需要设置setBounds,会替换内容
264 | *
265 | * [dynamicDrawableSpan] 提供Drawable的span
266 | */
267 | fun dynamicDrawable(dynamicDrawableSpan: DynamicDrawableSpan): Builder {
268 | span.setDynamicDrawable(dynamicDrawableSpan)
269 | return this
270 | }
271 |
272 | /**
273 | * 设置图片-Bitmap,会替换内容
274 | *
275 | * [verticalAlignment] 垂直对齐方式:ALIGN_BOTTOM、ALIGN_BASELINE
276 | */
277 | @JvmOverloads
278 | fun image(context: Context, bitmap: Bitmap, verticalAlignment: Int = DynamicDrawableSpan.ALIGN_BOTTOM): Builder {
279 | span.setImage(context, bitmap, verticalAlignment)
280 | return this
281 | }
282 |
283 | /**
284 | * 设置图片-Drawable,会替换内容。Drawable需设置setBounds。
285 | *
286 | * [verticalAlignment] 垂直对齐方式:ALIGN_BOTTOM、ALIGN_BASELINE
287 | */
288 | @JvmOverloads
289 | fun image(drawable: Drawable, source: String? = null, verticalAlignment: Int = DynamicDrawableSpan.ALIGN_BOTTOM): Builder {
290 | span.setImage(drawable, source, verticalAlignment)
291 | return this
292 | }
293 |
294 | /**
295 | * 解决上述方法JvmOverloads后无此参数情况
296 | */
297 | fun image(drawable: Drawable, verticalAlignment: Int) = image(drawable, null, verticalAlignment)
298 |
299 | /**
300 | * 设置图片-Uri,会替换内容。
301 | *
302 | * [verticalAlignment] 垂直对齐方式:ALIGN_BOTTOM、ALIGN_BASELINE
303 | */
304 | @JvmOverloads
305 | fun image(context: Context, uri: Uri, verticalAlignment: Int = DynamicDrawableSpan.ALIGN_BOTTOM): Builder {
306 | span.setImage(context, uri, verticalAlignment)
307 | return this
308 | }
309 |
310 | /**
311 | * 设置图片-resourceId,会替换内容。
312 | *
313 | * [verticalAlignment] 垂直对齐方式:ALIGN_BOTTOM、ALIGN_BASELINE
314 | */
315 | @JvmOverloads
316 | fun image(context: Context, resourceId: Int, verticalAlignment: Int = DynamicDrawableSpan.ALIGN_BOTTOM): Builder {
317 | span.setImage(context, resourceId, verticalAlignment)
318 | return this
319 | }
320 |
321 | /**
322 | * 设置子弹
323 | *
324 | */
325 | @JvmOverloads
326 | fun bullet(gapWidth: Int = BulletSpan.STANDARD_GAP_WIDTH, color: Int? = null): Builder {
327 | span.setBullet(gapWidth, color)
328 | return this
329 | }
330 |
331 | /**
332 | * 设置行缩进
333 | * [first] 段落第一行的缩进距离
334 | * [rest] 段落其余行缩进距离
335 | */
336 | @JvmOverloads
337 | fun leadingMargin(first: Int, rest: Int = first): Builder {
338 | span.setLeadingMargin(first, rest)
339 | return this
340 | }
341 |
342 | /**
343 | * 设置一行上制表符位置
344 | * [where] 制表符与行前距离的偏移量
345 | */
346 | fun tabStop(where: Int): Builder {
347 | span.setTabStop(where)
348 | return this
349 | }
350 |
351 | /**
352 | * 设置模糊
353 | * [radius] 模糊的半径
354 | * [style] 模糊的样式
355 | */
356 | fun blurMask(radius: Float, style: BlurMaskFilter.Blur): Builder {
357 | span.setBlurMask(radius, style)
358 | return this
359 | }
360 |
361 | /**
362 | * 设置样式-作用于text全部
363 | * [flags] 默认:[SPAN_EXCLUSIVE_EXCLUSIVE]。常用:[SPAN_INCLUSIVE_INCLUSIVE]、[SPAN_EXCLUSIVE_EXCLUSIVE]、[SPAN_INCLUSIVE_EXCLUSIVE]、[SPAN_EXCLUSIVE_INCLUSIVE],详情请看:https://www.jianshu.com/p/1956e15c9a27
364 | * [spans] 多个span样式
365 | */
366 | @JvmOverloads
367 | fun setSpanAll(flags: Int = SPAN_EXCLUSIVE_EXCLUSIVE, vararg spans: Any?): Builder {
368 | span.setSpanAll(flags, *spans)
369 | return this
370 | }
371 |
372 | /**
373 | * 设置样式-作用于text文本start到end的位置
374 | *
375 | * [start] 从此位置开始设置样式
376 | * [end] 从此位置结束设置样式
377 | * [flags] 默认:[SPAN_EXCLUSIVE_EXCLUSIVE]。常用:[SPAN_INCLUSIVE_INCLUSIVE]、[SPAN_EXCLUSIVE_EXCLUSIVE]、[SPAN_INCLUSIVE_EXCLUSIVE]、[SPAN_EXCLUSIVE_INCLUSIVE],详情请看:https://www.jianshu.com/p/1956e15c9a27
378 | * [spans] 多个span样式
379 | */
380 | @JvmOverloads
381 | fun setSpanPart(start: Int, end: Int, flags: Int = SPAN_EXCLUSIVE_EXCLUSIVE, vararg spans: Any?): Builder {
382 | span.setSpanPart(start, end, flags, *spans)
383 | return this
384 | }
385 | }
386 |
387 | companion object {
388 | /**
389 | * 构建者模式拼接Spans
390 | */
391 | @JvmStatic
392 | fun builder(): Builder {
393 | return Builder()
394 | }
395 | }
396 | }
--------------------------------------------------------------------------------