├── app
├── .gitignore
├── src
│ └── main
│ │ ├── res
│ │ ├── drawable
│ │ │ ├── tag.png
│ │ │ ├── label.png
│ │ │ ├── test_img.jpg
│ │ │ ├── ic_bulletin.png
│ │ │ ├── timeline_card_small_web.png
│ │ │ ├── timeline_card_small_video.png
│ │ │ └── timeline_card_small_article.png
│ │ ├── values
│ │ │ ├── strings.xml
│ │ │ ├── colors.xml
│ │ │ ├── dimens.xml
│ │ │ └── styles.xml
│ │ ├── mipmap-hdpi
│ │ │ └── ic_launcher.png
│ │ ├── mipmap-mdpi
│ │ │ └── ic_launcher.png
│ │ ├── mipmap-xhdpi
│ │ │ └── ic_launcher.png
│ │ ├── mipmap-xxhdpi
│ │ │ └── ic_launcher.png
│ │ ├── mipmap-xxxhdpi
│ │ │ └── ic_launcher.png
│ │ ├── values-w820dp
│ │ │ └── dimens.xml
│ │ └── layout
│ │ │ └── activity_main.xml
│ │ ├── AndroidManifest.xml
│ │ └── java
│ │ └── cn
│ │ └── iwgang
│ │ └── simplifyspandemo
│ │ └── MainActivity.java
├── proguard-rules.pro
└── build.gradle
├── library
├── .gitignore
├── src
│ └── main
│ │ ├── res
│ │ └── values
│ │ │ └── strings.xml
│ │ ├── AndroidManifest.xml
│ │ └── java
│ │ └── cn
│ │ └── iwgang
│ │ └── simplifyspan
│ │ ├── other
│ │ ├── OnClickStateChangeListener.java
│ │ ├── SpecialGravity.java
│ │ ├── SpecialConvertMode.java
│ │ ├── OnClickableSpanListener.java
│ │ └── CustomLinkMovementMethod.java
│ │ ├── unit
│ │ ├── SpecialRawSpanUnit.java
│ │ ├── BaseSpecialUnit.java
│ │ ├── SpecialClickableUnit.java
│ │ ├── SpecialImageUnit.java
│ │ ├── SpecialTextUnit.java
│ │ └── SpecialLabelUnit.java
│ │ ├── customspan
│ │ ├── CustomAbsoluteSizeSpan.java
│ │ ├── CustomClickableSpan.java
│ │ ├── CustomImageSpan.java
│ │ └── CustomLabelSpan.java
│ │ └── SimplifySpanBuild.java
├── build.gradle
├── gradle.properties
├── proguard-rules.pro
└── build_push_maven.gradle
├── settings.gradle
├── demo.apk
├── screenshot
├── s1.png
├── s2.gif
└── s3.gif
├── gradle
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── .gitignore
├── gradle.properties
├── LICENSE
├── gradlew.bat
├── README.md
└── gradlew
/app/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/library/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':app', ':library'
2 |
--------------------------------------------------------------------------------
/demo.apk:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/iwgang/SimplifySpan/HEAD/demo.apk
--------------------------------------------------------------------------------
/screenshot/s1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/iwgang/SimplifySpan/HEAD/screenshot/s1.png
--------------------------------------------------------------------------------
/screenshot/s2.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/iwgang/SimplifySpan/HEAD/screenshot/s2.gif
--------------------------------------------------------------------------------
/screenshot/s3.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/iwgang/SimplifySpan/HEAD/screenshot/s3.gif
--------------------------------------------------------------------------------
/app/src/main/res/drawable/tag.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/iwgang/SimplifySpan/HEAD/app/src/main/res/drawable/tag.png
--------------------------------------------------------------------------------
/app/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | SimplifySpan
3 |
4 |
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/iwgang/SimplifySpan/HEAD/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/library/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | Library
3 |
4 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/label.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/iwgang/SimplifySpan/HEAD/app/src/main/res/drawable/label.png
--------------------------------------------------------------------------------
/library/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/test_img.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/iwgang/SimplifySpan/HEAD/app/src/main/res/drawable/test_img.jpg
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_bulletin.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/iwgang/SimplifySpan/HEAD/app/src/main/res/drawable/ic_bulletin.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/iwgang/SimplifySpan/HEAD/app/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/iwgang/SimplifySpan/HEAD/app/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/iwgang/SimplifySpan/HEAD/app/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/iwgang/SimplifySpan/HEAD/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/iwgang/SimplifySpan/HEAD/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable/timeline_card_small_web.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/iwgang/SimplifySpan/HEAD/app/src/main/res/drawable/timeline_card_small_web.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable/timeline_card_small_video.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/iwgang/SimplifySpan/HEAD/app/src/main/res/drawable/timeline_card_small_video.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable/timeline_card_small_article.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/iwgang/SimplifySpan/HEAD/app/src/main/res/drawable/timeline_card_small_article.png
--------------------------------------------------------------------------------
/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 | #Sat Nov 21 15:36:38 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.1.1-all.zip
7 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # generated files
2 | bin/
3 | gen/
4 | *target/
5 | *build/
6 | .gradle/
7 | .idea/
8 |
9 | # built application files
10 | *.ap_
11 |
12 | # files for the dex VM
13 | *.dex
14 |
15 | # Java class files
16 | *.class
17 |
18 | # Eclipse project files
19 | .project
20 | .classpath
21 | .settings/
22 |
23 | # Other
24 | .DS_Store
25 | /local.properties
26 | *.iml
--------------------------------------------------------------------------------
/library/src/main/java/cn/iwgang/simplifyspan/other/OnClickStateChangeListener.java:
--------------------------------------------------------------------------------
1 | package cn.iwgang.simplifyspan.other;
2 |
3 | /**
4 | * Click State Change Listener
5 | * Created by iWgang on 15/12/19.
6 | * https://github.com/iwgang/SimplifySpan
7 | */
8 | public interface OnClickStateChangeListener {
9 | void onStateChange(boolean isSelected, int pressBgColor);
10 | }
11 |
--------------------------------------------------------------------------------
/app/src/main/res/values-w820dp/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 | 64dp
6 |
7 |
--------------------------------------------------------------------------------
/library/src/main/java/cn/iwgang/simplifyspan/other/SpecialGravity.java:
--------------------------------------------------------------------------------
1 | package cn.iwgang.simplifyspan.other;
2 |
3 | /**
4 | * Special Gravity
5 | * Created by iWgang on 15/12/3.
6 | * https://github.com/iwgang/SimplifySpan
7 | */
8 | public class SpecialGravity {
9 | public static final int TOP = 1;
10 | public static final int CENTER = 2;
11 | public static final int BOTTOM = 3;
12 | }
13 |
--------------------------------------------------------------------------------
/library/src/main/java/cn/iwgang/simplifyspan/other/SpecialConvertMode.java:
--------------------------------------------------------------------------------
1 | package cn.iwgang.simplifyspan.other;
2 |
3 | /**
4 | * Special Convert Mode
5 | * Created by iWgang on 15/12/3.
6 | * https://github.com/iwgang/SimplifySpan
7 | */
8 | public class SpecialConvertMode {
9 | public static final int ONLY_FIRST = 1;
10 | public static final int ONLY_LAST = 2;
11 | public static final int ALL = 3;
12 | }
13 |
--------------------------------------------------------------------------------
/app/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/library/src/main/java/cn/iwgang/simplifyspan/other/OnClickableSpanListener.java:
--------------------------------------------------------------------------------
1 | package cn.iwgang.simplifyspan.other;
2 |
3 | import android.text.style.ClickableSpan;
4 | import android.widget.TextView;
5 |
6 | import cn.iwgang.simplifyspan.customspan.CustomClickableSpan;
7 |
8 | /**
9 | * ClickableSpan Listener
10 | * Created by iWgang on 15/12/3.
11 | * https://github.com/iwgang/SimplifySpan
12 | */
13 | public interface OnClickableSpanListener {
14 |
15 | void onClick(TextView tv, CustomClickableSpan clickableSpan);
16 |
17 | }
18 |
--------------------------------------------------------------------------------
/library/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.library'
2 |
3 | android {
4 | compileSdkVersion 25
5 | buildToolsVersion "25.0.3"
6 |
7 | defaultConfig {
8 | minSdkVersion 10
9 | targetSdkVersion 25
10 | versionCode 1
11 | versionName "1.0"
12 | }
13 | buildTypes {
14 | release {
15 | minifyEnabled false
16 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
17 | }
18 | }
19 | }
20 |
21 | dependencies {
22 | implementation fileTree(dir: 'libs', include: ['*.jar'])
23 | }
24 |
25 | apply from: './build_push_maven.gradle'
--------------------------------------------------------------------------------
/library/gradle.properties:
--------------------------------------------------------------------------------
1 | POM_GROUP_ID=com.github.iwgang
2 | POM_ARTIFACT_ID=simplifyspan
3 | POM_DESCRIPTION=A simplify to use and powerful Spannable library.
4 | POM_URL=https://github.com/iwgang/SimplifySpan
5 |
6 | POM_SCM_URL=https://github.com/iwgang/SimplifySpan
7 | POM_SCM_CONNECTION=scm:git@github.com:iwgang/SimplifySpan.git
8 | POM_SCM_DEVELOPER_CONNECTION=scm:git@github.com:iwgang/SimplifySpan.git
9 |
10 | POM_LICENCE_NAME=The MIT License (MIT)
11 | POM_LICENCE_URL=http://opensource.org/licenses/MIT
12 | POM_LICENCE_DISTRIBUTION=repo
13 |
14 | POM_DEVELOPER_ID=iwgang
15 | POM_DEVELOPER_NAME=iWgang
16 | POM_DEVELOPER_EMAIL=iwgang@163.com
17 |
18 |
19 | VERSION_NAME=2.2
--------------------------------------------------------------------------------
/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 /Users/iwgang/Dev/Android/AndroidSdk/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 |
--------------------------------------------------------------------------------
/library/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 /Users/iwgang/Dev/Android/AndroidSdk/tools/proguard/proguard-android.txt
4 | # You can edit the include path and order by changing the proguardFiles
5 | # directive in build.gradle.
6 | #
7 | # For more details, see
8 | # http://developer.android.com/guide/developing/tools/proguard.html
9 |
10 | # Add any project specific keep options here:
11 |
12 | # If your project uses WebView with JS, uncomment the following
13 | # and specify the fully qualified class name to the JavaScript interface
14 | # class:
15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
16 | # public *;
17 | #}
18 |
--------------------------------------------------------------------------------
/app/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.application'
2 |
3 | android {
4 | compileSdkVersion 25
5 | buildToolsVersion "25.0.3"
6 |
7 | defaultConfig {
8 | applicationId "cn.iwgang.simplifyspandemo"
9 | minSdkVersion 10
10 | targetSdkVersion 25
11 | versionCode 1
12 | versionName "1.0"
13 | }
14 | buildTypes {
15 | release {
16 | minifyEnabled false
17 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
18 | }
19 | }
20 | }
21 |
22 | dependencies {
23 | implementation fileTree(dir: 'libs', include: ['*.jar'])
24 | implementation 'com.android.support:appcompat-v7:25.4.0'
25 | implementation project(':library')
26 | }
27 |
--------------------------------------------------------------------------------
/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/gradle.properties:
--------------------------------------------------------------------------------
1 | # Project-wide Gradle settings.
2 |
3 | # IDE (e.g. Android Studio) users:
4 | # Gradle settings configured through the IDE *will override*
5 | # any settings specified in this file.
6 |
7 | # For more details on how to configure your build environment visit
8 | # http://www.gradle.org/docs/current/userguide/build_environment.html
9 |
10 | # Specifies the JVM arguments used for the daemon process.
11 | # The setting is particularly useful for tweaking memory settings.
12 | # Default value: -Xmx10248m -XX:MaxPermSize=256m
13 | # org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8
14 |
15 | # When configured, Gradle will run in incubating parallel mode.
16 | # This option should only be used with decoupled projects. More details, visit
17 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
18 | # org.gradle.parallel=true
--------------------------------------------------------------------------------
/library/src/main/java/cn/iwgang/simplifyspan/unit/SpecialRawSpanUnit.java:
--------------------------------------------------------------------------------
1 | package cn.iwgang.simplifyspan.unit;
2 |
3 | import android.text.Spanned;
4 |
5 | /**
6 | * Special Raw Span Unit
7 | * Created by iWgang on 15/12/3.
8 | * https://github.com/iwgang/SimplifySpan
9 | */
10 | public class SpecialRawSpanUnit extends BaseSpecialUnit {
11 | private Object spanObj;
12 | private int flags;
13 |
14 | public SpecialRawSpanUnit(String text, Object spanObj) {
15 | super(text);
16 | this.spanObj = spanObj;
17 | this.flags = Spanned.SPAN_EXCLUSIVE_EXCLUSIVE;
18 | }
19 |
20 | public SpecialRawSpanUnit(String text, Object spanObj, int flags) {
21 | super(text);
22 | this.spanObj = spanObj;
23 | this.flags = flags;
24 | }
25 |
26 | public Object getSpanObj() {
27 | return spanObj;
28 | }
29 |
30 | public int getFlags() {
31 | return flags;
32 | }
33 |
34 | }
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2015 iWgang
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
--------------------------------------------------------------------------------
/library/src/main/java/cn/iwgang/simplifyspan/unit/BaseSpecialUnit.java:
--------------------------------------------------------------------------------
1 | package cn.iwgang.simplifyspan.unit;
2 |
3 | import cn.iwgang.simplifyspan.other.SpecialConvertMode;
4 | import cn.iwgang.simplifyspan.other.SpecialGravity;
5 |
6 | /**
7 | * Base Special Unit
8 | * Created by iWgang on 15/12/3.
9 | * https://github.com/iwgang/SimplifySpan
10 | */
11 | public class BaseSpecialUnit {
12 | protected String text;
13 | private int[] startPoss;
14 | protected int gravity = SpecialGravity.BOTTOM;
15 | protected int convertMode = SpecialConvertMode.ONLY_FIRST;
16 |
17 | public BaseSpecialUnit(String text) {
18 | this.text = text;
19 | }
20 |
21 | public String getText() {
22 | return text;
23 | }
24 |
25 | public int getGravity() {
26 | return gravity;
27 | }
28 |
29 | public int getConvertMode() {
30 | return convertMode;
31 | }
32 |
33 | /**
34 | * Use only in SimplifySpanBuild
35 | * @param startPoss start poss
36 | */
37 | public void setStartPoss(int[] startPoss) {
38 | this.startPoss = startPoss;
39 | }
40 |
41 | public int[] getStartPoss() {
42 | return startPoss;
43 | }
44 |
45 | }
--------------------------------------------------------------------------------
/library/src/main/java/cn/iwgang/simplifyspan/customspan/CustomAbsoluteSizeSpan.java:
--------------------------------------------------------------------------------
1 | package cn.iwgang.simplifyspan.customspan;
2 |
3 | import android.graphics.Rect;
4 | import android.text.TextPaint;
5 | import android.text.style.AbsoluteSizeSpan;
6 | import android.widget.TextView;
7 |
8 | import cn.iwgang.simplifyspan.other.SpecialGravity;
9 |
10 | /**
11 | * Custom AbsoluteSizeSpan
12 | * Created by iWgang on 15/12/5.
13 | * https://github.com/iwgang/SimplifySpan
14 | */
15 | public class CustomAbsoluteSizeSpan extends AbsoluteSizeSpan {
16 | private TextPaint mTextPaint;
17 | private int mGravity;
18 | private int mOffsetBaselineShift;
19 | private String mText;
20 | private Rect mTextViewRect = new Rect();
21 | private Rect mTextRect = new Rect();
22 | private String mNormalSizeText;
23 |
24 | public CustomAbsoluteSizeSpan(String normalSizeText, String text, int size, TextPaint textView, int gravity) {
25 | super(size, true);
26 | mText = text;
27 | mTextPaint = textView;
28 | mGravity = gravity;
29 | mNormalSizeText = normalSizeText;
30 | }
31 |
32 | @Override
33 | public void updateDrawState(TextPaint ds) {
34 | super.updateDrawState(ds);
35 |
36 | if (mGravity == SpecialGravity.BOTTOM) return;
37 |
38 | mTextPaint.getTextBounds(mNormalSizeText, 0, mNormalSizeText.length(), mTextViewRect);
39 | ds.getTextBounds(mText, 0, mText.length(), mTextRect);
40 | int mMainTextHeight = mTextViewRect.height();
41 |
42 | int offset = mTextViewRect.bottom - mTextRect.bottom;
43 | switch (mGravity) {
44 | case SpecialGravity.TOP:
45 | mOffsetBaselineShift = mMainTextHeight - mTextRect.height() - offset;
46 | break;
47 | case SpecialGravity.CENTER:
48 | mOffsetBaselineShift = mMainTextHeight / 2 - mTextRect.height() / 2 - offset;
49 | break;
50 | }
51 |
52 | ds.baselineShift -= mOffsetBaselineShift;
53 | }
54 |
55 | @Override
56 | public void updateMeasureState(TextPaint ds) {
57 | super.updateMeasureState(ds);
58 | ds.baselineShift -= mOffsetBaselineShift;
59 | }
60 | }
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/library/src/main/java/cn/iwgang/simplifyspan/other/CustomLinkMovementMethod.java:
--------------------------------------------------------------------------------
1 | package cn.iwgang.simplifyspan.other;
2 |
3 | import android.text.Layout;
4 | import android.text.Selection;
5 | import android.text.Spannable;
6 | import android.text.method.LinkMovementMethod;
7 | import android.view.MotionEvent;
8 | import android.widget.TextView;
9 |
10 | import cn.iwgang.simplifyspan.customspan.CustomClickableSpan;
11 |
12 | /**
13 | * Custom LinkMovementMethod
14 | * Created by iWgang on 15/12/8.
15 | * https://github.com/iwgang/SimplifySpan
16 | * Ref: http://stackoverflow.com/a/20905824
17 | */
18 | public class CustomLinkMovementMethod extends LinkMovementMethod {
19 | private static CustomLinkMovementMethod sInstance;
20 | private CustomClickableSpan mCustomClickableSpan;
21 |
22 | public static CustomLinkMovementMethod getInstance() {
23 | if (sInstance == null)
24 | sInstance = new CustomLinkMovementMethod();
25 |
26 | return sInstance;
27 | }
28 |
29 | @Override
30 | public boolean onTouchEvent(TextView textView, Spannable spannable, MotionEvent event) {
31 | if (event.getAction() == MotionEvent.ACTION_DOWN) {
32 | mCustomClickableSpan = getPressedSpan(textView, spannable, event);
33 | if (mCustomClickableSpan != null) {
34 | mCustomClickableSpan.setPressed(true);
35 | Selection.setSelection(spannable, spannable.getSpanStart(mCustomClickableSpan), spannable.getSpanEnd(mCustomClickableSpan));
36 | }
37 | } else if (event.getAction() == MotionEvent.ACTION_MOVE) {
38 | CustomClickableSpan touchedSpan = getPressedSpan(textView, spannable, event);
39 | if (mCustomClickableSpan != null && touchedSpan != mCustomClickableSpan) {
40 | mCustomClickableSpan.setPressed(false);
41 | mCustomClickableSpan = null;
42 | Selection.removeSelection(spannable);
43 | }
44 | } else {
45 | if (mCustomClickableSpan != null) {
46 | mCustomClickableSpan.setPressed(false);
47 | super.onTouchEvent(textView, spannable, event);
48 | }
49 | mCustomClickableSpan = null;
50 | Selection.removeSelection(spannable);
51 | }
52 | return true;
53 | }
54 |
55 | private CustomClickableSpan getPressedSpan(TextView textView, Spannable spannable, MotionEvent event) {
56 | int x = (int) event.getX();
57 | int y = (int) event.getY();
58 |
59 | x -= textView.getTotalPaddingLeft();
60 | y -= textView.getTotalPaddingTop();
61 |
62 | x += textView.getScrollX();
63 | y += textView.getScrollY();
64 |
65 | Layout layout = textView.getLayout();
66 | int line = layout.getLineForVertical(y);
67 | int off = layout.getOffsetForHorizontal(line, x);
68 |
69 | CustomClickableSpan[] link = spannable.getSpans(off, off, CustomClickableSpan.class);
70 | CustomClickableSpan touchedSpan = null;
71 | if (link.length > 0) {
72 | touchedSpan = link[0];
73 | }
74 | return touchedSpan;
75 | }
76 |
77 | }
--------------------------------------------------------------------------------
/library/build_push_maven.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'maven'
2 | apply plugin: 'signing'
3 |
4 | def isReleaseBuild() {
5 | return VERSION_NAME.contains("SNAPSHOT") == false
6 | }
7 |
8 | def getReleaseRepositoryUrl() {
9 | return hasProperty('RELEASE_REPOSITORY_URL') ? RELEASE_REPOSITORY_URL
10 | : "https://oss.sonatype.org/service/local/staging/deploy/maven2/"
11 | }
12 |
13 | def getSnapshotRepositoryUrl() {
14 | return hasProperty('SNAPSHOT_REPOSITORY_URL') ? SNAPSHOT_REPOSITORY_URL
15 | : "https://oss.sonatype.org/content/repositories/snapshots/"
16 | }
17 |
18 | def getRepositoryUsername() {
19 | return hasProperty('NEXUS_USERNAME') ? NEXUS_USERNAME : ''
20 | }
21 |
22 | def getRepositoryPassword() {
23 | return hasProperty('NEXUS_PASSWORD') ? NEXUS_PASSWORD : ''
24 | }
25 |
26 | afterEvaluate { project ->
27 | uploadArchives {
28 | repositories {
29 | mavenDeployer {
30 | //为Pom文件做数字签名
31 | beforeDeployment { deployment -> signing.signPom(deployment) }
32 |
33 | pom.groupId = POM_GROUP_ID
34 | pom.artifactId = POM_ARTIFACT_ID
35 | pom.version = VERSION_NAME
36 |
37 | repository(url: getReleaseRepositoryUrl()) {
38 | authentication(userName: getRepositoryUsername(), password: getRepositoryPassword())
39 | }
40 | snapshotRepository(url: getSnapshotRepositoryUrl()) {
41 | authentication(userName: getRepositoryUsername(), password: getRepositoryPassword())
42 | }
43 |
44 | pom.project {
45 | name project.name
46 | packaging 'aar'
47 | description POM_DESCRIPTION
48 | url POM_URL
49 |
50 | scm {
51 | url POM_SCM_URL
52 | connection POM_SCM_CONNECTION
53 | developerConnection POM_SCM_DEVELOPER_CONNECTION
54 | }
55 |
56 | licenses {
57 | license {
58 | name POM_LICENCE_NAME
59 | url POM_LICENCE_URL
60 | distribution POM_LICENCE_DISTRIBUTION
61 | }
62 | }
63 |
64 | developers {
65 | developer {
66 | id POM_DEVELOPER_ID
67 | name POM_DEVELOPER_NAME
68 | email POM_DEVELOPER_EMAIL
69 | }
70 | }
71 | }
72 | }
73 | }
74 | }
75 |
76 | signing {
77 | required { isReleaseBuild() && gradle.taskGraph.hasTask("uploadArchives") }
78 | sign configurations.archives
79 | }
80 |
81 | task androidJavadocs(type: Javadoc) {
82 | source = android.sourceSets.main.java.srcDirs
83 | classpath += project.files(android.getBootClasspath().join(File.pathSeparator))
84 | }
85 |
86 | task androidJavadocsJar(type: Jar, dependsOn: androidJavadocs) {
87 | classifier = 'javadoc'
88 | from androidJavadocs.destinationDir
89 | }
90 |
91 | task androidSourcesJar(type: Jar) {
92 | classifier = 'sources'
93 | from android.sourceSets.main.java.srcDirs
94 | }
95 |
96 | artifacts {
97 | archives androidSourcesJar
98 | archives androidJavadocsJar
99 | }
100 |
101 | }
--------------------------------------------------------------------------------
/library/src/main/java/cn/iwgang/simplifyspan/unit/SpecialClickableUnit.java:
--------------------------------------------------------------------------------
1 | package cn.iwgang.simplifyspan.unit;
2 |
3 | import android.widget.TextView;
4 |
5 | import java.util.List;
6 |
7 | import cn.iwgang.simplifyspan.other.OnClickStateChangeListener;
8 | import cn.iwgang.simplifyspan.other.OnClickableSpanListener;
9 |
10 | /**
11 | * Clickable Special Unit
12 | * Created by iWgang on 15/12/7.
13 | * https://github.com/iwgang/SimplifySpan
14 | */
15 | public class SpecialClickableUnit extends BaseSpecialUnit {
16 | private List onClickStateChangeListeners;
17 | private OnClickableSpanListener onClickListener;
18 | private TextView curTextView;
19 | private int normalBgColor;
20 | private int pressBgColor;
21 | // only text
22 | private boolean isShowUnderline;
23 | private int normalTextColor;
24 | private int pressTextColor;
25 | private Object tag;
26 |
27 | public SpecialClickableUnit(TextView curTextView, OnClickableSpanListener onClickListener) {
28 | super(null);
29 | this.curTextView = curTextView;
30 | this.onClickListener = onClickListener;
31 | }
32 |
33 | public SpecialClickableUnit showUnderline() {
34 | this.isShowUnderline = true;
35 | return this;
36 | }
37 |
38 | public SpecialClickableUnit setPressBgColor(int pressBgColor) {
39 | this.pressBgColor = pressBgColor;
40 | return this;
41 | }
42 |
43 | public SpecialClickableUnit setPressTextColor(int pressTextColor) {
44 | this.pressTextColor = pressTextColor;
45 | return this;
46 | }
47 |
48 | public SpecialClickableUnit setNormalBgColor(int normalBgColor) {
49 | this.normalBgColor = normalBgColor;
50 | return this;
51 | }
52 |
53 | public SpecialClickableUnit setNormalTextColor(int normalTextColor) {
54 | this.normalTextColor = normalTextColor;
55 | return this;
56 | }
57 |
58 | /**
59 | * Do not call !!! (Use only in SimplifySpanBuild)
60 | *
61 | * @param onClickStateChangeListeners OnClickStateChangeListener list
62 | */
63 | public void setOnClickStateChangeListeners(List onClickStateChangeListeners) {
64 | this.onClickStateChangeListeners = onClickStateChangeListeners;
65 | }
66 |
67 | public OnClickableSpanListener getOnClickListener() {
68 | return onClickListener;
69 | }
70 |
71 | public int getPressTextColor() {
72 | return pressTextColor;
73 | }
74 |
75 | public int getPressBgColor() {
76 | return pressBgColor;
77 | }
78 |
79 | public int getNormalBgColor() {
80 | return normalBgColor;
81 | }
82 |
83 | public int getNormalTextColor() {
84 | return normalTextColor;
85 | }
86 |
87 | public TextView getCurTextView() {
88 | return curTextView;
89 | }
90 |
91 | public List getOnClickStateChangeListeners() {
92 | return onClickStateChangeListeners;
93 | }
94 |
95 | public boolean isShowUnderline() {
96 | return isShowUnderline;
97 | }
98 |
99 | /**
100 | * Do not call !!! (Use only in SimplifySpanBuild)
101 | *
102 | * @param text text
103 | */
104 | public void setText(String text) {
105 | this.text = text;
106 | }
107 |
108 | public Object getTag() {
109 | return tag;
110 | }
111 |
112 | public SpecialClickableUnit setTag(Object tag) {
113 | this.tag = tag;
114 | return this;
115 | }
116 | }
--------------------------------------------------------------------------------
/library/src/main/java/cn/iwgang/simplifyspan/customspan/CustomClickableSpan.java:
--------------------------------------------------------------------------------
1 | package cn.iwgang.simplifyspan.customspan;
2 |
3 | import android.graphics.Color;
4 | import android.text.Spanned;
5 | import android.text.TextPaint;
6 | import android.text.style.ClickableSpan;
7 | import android.view.View;
8 | import android.widget.TextView;
9 |
10 | import java.util.List;
11 |
12 | import cn.iwgang.simplifyspan.other.OnClickStateChangeListener;
13 | import cn.iwgang.simplifyspan.other.OnClickableSpanListener;
14 | import cn.iwgang.simplifyspan.unit.SpecialClickableUnit;
15 |
16 | /**
17 | * Custom ClickableSpan
18 | * Created by iWgang on 15/12/8.
19 | * https://github.com/iwgang/SimplifySpan
20 | */
21 | public class CustomClickableSpan extends ClickableSpan {
22 | private List mOnClickStateChangeListeners;
23 | private OnClickableSpanListener mOnClickableSpanListener;
24 | private boolean isPressed;
25 | private boolean isShowUnderline;
26 | private int mTextColorNor;
27 | private int mTextColorPre;
28 | private int mBgColorNor;
29 | private int mBgColorPre;
30 | private Object mTag;
31 | private String mClickText;
32 | private int mStartSpanIndex;
33 | private int mEndSpanIndex;
34 |
35 | public CustomClickableSpan(SpecialClickableUnit specialClickableUnit) {
36 | mTag = specialClickableUnit.getTag();
37 | mTextColorNor = specialClickableUnit.getNormalTextColor();
38 | mTextColorPre = specialClickableUnit.getPressTextColor();
39 | mBgColorNor = specialClickableUnit.getNormalBgColor();
40 | mBgColorPre = specialClickableUnit.getPressBgColor();
41 | isShowUnderline = specialClickableUnit.isShowUnderline();
42 | mOnClickableSpanListener = specialClickableUnit.getOnClickListener();
43 | mOnClickStateChangeListeners = specialClickableUnit.getOnClickStateChangeListeners();
44 | }
45 |
46 | @Override
47 | public void onClick(View widget) {
48 | if (null != mOnClickableSpanListener) {
49 | TextView tv = (TextView) widget;
50 | Spanned spanned = (Spanned) tv.getText();
51 | mStartSpanIndex = spanned.getSpanStart(this);
52 | mEndSpanIndex = spanned.getSpanEnd(this);
53 | mClickText = spanned.subSequence(mStartSpanIndex, mEndSpanIndex).toString();
54 | mOnClickableSpanListener.onClick(tv, this);
55 | }
56 | }
57 |
58 | public void setPressed(boolean isSelected) {
59 | if (null != mOnClickStateChangeListeners && !mOnClickStateChangeListeners.isEmpty()) {
60 | for (OnClickStateChangeListener csl : mOnClickStateChangeListeners) {
61 | csl.onStateChange(isSelected, mBgColorPre);
62 | }
63 | }
64 | isPressed = isSelected;
65 | }
66 |
67 | @Override
68 | public void updateDrawState(TextPaint ds) {
69 | super.updateDrawState(ds);
70 |
71 | // set text color And press status color
72 | if (mTextColorNor != 0) {
73 | if (mTextColorPre != 0) {
74 | ds.setColor(isPressed ? mTextColorPre : mTextColorNor);
75 | } else {
76 | ds.setColor(mTextColorNor);
77 | }
78 | }
79 |
80 | // set background color And press status color
81 | if (mBgColorPre != 0) {
82 | ds.bgColor = isPressed ? mBgColorPre : mBgColorNor == 0 ? Color.TRANSPARENT : mBgColorNor;
83 | } else if (mBgColorNor != 0) {
84 | ds.bgColor = mBgColorNor;
85 | }
86 |
87 | if (!isShowUnderline) {
88 | // clear underline
89 | ds.setUnderlineText(false);
90 | }
91 | }
92 |
93 | /**
94 | * get tag
95 | * @return tag object
96 | */
97 | public Object getTag() {
98 | return mTag;
99 | }
100 |
101 | /**
102 | * get current click text
103 | * @return click text
104 | */
105 | public String getClickText() {
106 | return mClickText;
107 | }
108 |
109 | /**
110 | * get current click text span start index in total
111 | * @return start span index
112 | */
113 | public int getStartSpanIndex() {
114 | return mStartSpanIndex;
115 | }
116 |
117 | /**
118 | * get current click text span start index in end
119 | * @return end span index
120 | */
121 | public int getEndSpanIndex() {
122 | return mEndSpanIndex;
123 | }
124 |
125 | }
126 |
--------------------------------------------------------------------------------
/library/src/main/java/cn/iwgang/simplifyspan/customspan/CustomImageSpan.java:
--------------------------------------------------------------------------------
1 | package cn.iwgang.simplifyspan.customspan;
2 |
3 | import android.graphics.Canvas;
4 | import android.graphics.Paint;
5 | import android.graphics.Rect;
6 | import android.graphics.drawable.Drawable;
7 | import android.text.style.ImageSpan;
8 |
9 | import java.lang.ref.WeakReference;
10 |
11 | import cn.iwgang.simplifyspan.other.OnClickStateChangeListener;
12 | import cn.iwgang.simplifyspan.other.SpecialGravity;
13 | import cn.iwgang.simplifyspan.unit.SpecialImageUnit;
14 |
15 | /**
16 | * Custom ImageSpan
17 | * Created by iWgang on 15/12/3.
18 | * https://github.com/iwgang/SimplifySpan
19 | */
20 | public class CustomImageSpan extends ImageSpan implements OnClickStateChangeListener {
21 | private WeakReference mDrawableRef;
22 | private int gravity;
23 | private Rect mRect = new Rect();
24 | private String mNormalSizeText;
25 | private int mBgColor;
26 | private boolean isSelected;
27 | private boolean isClickable;
28 | private int pressBgColor;
29 |
30 | private int mLineTextHeight;
31 | private int mLineTextBaselineOffset;
32 |
33 | public CustomImageSpan(String normalSizeText, SpecialImageUnit specialImageUnit) {
34 | super(specialImageUnit.getContext(), specialImageUnit.getBitmap(), ALIGN_BASELINE);
35 | this.gravity = specialImageUnit.getGravity();
36 | this.mNormalSizeText = normalSizeText;
37 | this.mBgColor = specialImageUnit.getBgColor();
38 | this.isClickable = specialImageUnit.isClickable();
39 | }
40 |
41 | @Override
42 | public void onStateChange(boolean isSelected, int pressBgColor) {
43 | this.isSelected = isSelected;
44 | this.pressBgColor = pressBgColor;
45 | }
46 |
47 | @Override
48 | public void draw(Canvas canvas, CharSequence text, int start, int end, float x, int top, int y, int bottom, Paint paint) {
49 | Drawable b = getCachedDrawable();
50 | int drawableHeight = b.getIntrinsicHeight();
51 | int drawableWidth = b.getIntrinsicWidth();
52 | paint.getTextBounds(mNormalSizeText, 0, mNormalSizeText.length(), mRect);
53 |
54 | float finalUnitHeight = bottom - top;
55 | float bgTop = bottom - finalUnitHeight;
56 |
57 | if (isClickable && isSelected && pressBgColor != 0) {
58 | // click background
59 | paint.setColor(pressBgColor);
60 | canvas.drawRect(x, bgTop, x + drawableWidth, bgTop + finalUnitHeight, paint);
61 | } else {
62 | // normal background
63 | if (mBgColor != 0) {
64 | paint.setColor(mBgColor);
65 | canvas.drawRect(x, bgTop, x + drawableWidth, bgTop + finalUnitHeight, paint);
66 | }
67 | }
68 |
69 | int textHeight = mRect.height();
70 | if (drawableHeight > textHeight) {
71 | super.draw(canvas, text, start, end, x, top, y, bottom, paint);
72 | return;
73 | }
74 | canvas.save();
75 |
76 | if (mLineTextHeight <= 0) {
77 | Rect specialTextRect = new Rect();
78 | paint.getTextBounds(mNormalSizeText, 0, mNormalSizeText.length(), specialTextRect);
79 | mLineTextHeight = specialTextRect.height();
80 | mLineTextBaselineOffset = specialTextRect.bottom;
81 | }
82 |
83 | float newStartY = y;
84 | switch (gravity) {
85 | case SpecialGravity.TOP: {
86 | newStartY -= mLineTextHeight - mLineTextBaselineOffset;
87 | canvas.translate(x, newStartY);
88 | break;
89 | }
90 | case SpecialGravity.CENTER: {
91 | newStartY -= (mLineTextHeight / 2 + drawableHeight / 2 - mLineTextBaselineOffset);
92 | canvas.translate(x, newStartY);
93 | break;
94 | }
95 | case SpecialGravity.BOTTOM: {
96 | newStartY -= drawableHeight - mLineTextBaselineOffset;
97 | canvas.translate(x, newStartY);
98 | break;
99 | }
100 | }
101 | b.draw(canvas);
102 | canvas.restore();
103 | }
104 |
105 | private Drawable getCachedDrawable() {
106 | Drawable drawable = null;
107 |
108 | if (mDrawableRef != null) {
109 | drawable = mDrawableRef.get();
110 | }
111 |
112 | if (drawable == null) {
113 | drawable = getDrawable();
114 | mDrawableRef = new WeakReference<>(drawable);
115 | }
116 |
117 | return drawable;
118 | }
119 |
120 | }
--------------------------------------------------------------------------------
/library/src/main/java/cn/iwgang/simplifyspan/unit/SpecialImageUnit.java:
--------------------------------------------------------------------------------
1 | package cn.iwgang.simplifyspan.unit;
2 |
3 | import android.content.Context;
4 | import android.graphics.Bitmap;
5 |
6 | /**
7 | * Image Special Unit
8 | * Created by iWgang on 15/12/3.
9 | * https://github.com/iwgang/SimplifySpan
10 | */
11 | public class SpecialImageUnit extends BaseSpecialUnit {
12 | private static final String DEF_IMG_PLACEHOLDER = "img";
13 |
14 | private Context context;
15 | private Bitmap bitmap;
16 | private int bgColor;
17 | private boolean isClickable;
18 | private int width; // px
19 | private int height; // px
20 |
21 | /**
22 | * @param context Context
23 | * @param bitmap Bitmap
24 | */
25 | public SpecialImageUnit(Context context, Bitmap bitmap) {
26 | this(context, DEF_IMG_PLACEHOLDER, bitmap);
27 | }
28 |
29 | /**
30 | * @param context Context
31 | * @param specialText Special Text
32 | * @param bitmap Bitmap
33 | */
34 | public SpecialImageUnit(Context context, String specialText, Bitmap bitmap) {
35 | super(specialText);
36 | this.context = context;
37 | this.bitmap = bitmap;
38 | }
39 |
40 | /**
41 | * @param context Context
42 | * @param bitmap Bitmap
43 | * @param width Width
44 | * @param height Height
45 | */
46 | public SpecialImageUnit(Context context, Bitmap bitmap, int width, int height) {
47 | this(context, DEF_IMG_PLACEHOLDER, bitmap, width, height);
48 | }
49 |
50 | /**
51 | * @param context Context
52 | * @param specialText Special Text
53 | * @param bitmap Bitmap
54 | * @param width Width
55 | * @param height Height
56 | */
57 | public SpecialImageUnit(Context context, String specialText, Bitmap bitmap, int width, int height) {
58 | super(specialText);
59 | this.context = context;
60 | this.bitmap = bitmap;
61 | this.width = width;
62 | this.height = height;
63 | }
64 |
65 | /**
66 | * @param context Context
67 | * @param bitmap Bitmap
68 | * @param width Width
69 | * @param height Height
70 | * @param gravity Use SpecialGravity.xx
71 | */
72 | public SpecialImageUnit(Context context, Bitmap bitmap, int width, int height, int gravity) {
73 | this(context, DEF_IMG_PLACEHOLDER, bitmap, width, height, gravity);
74 | }
75 |
76 | /**
77 | * @param context Context
78 | * @param specialText Special Text
79 | * @param bitmap Bitmap
80 | * @param width Width
81 | * @param height Height
82 | * @param gravity Use SpecialGravity.xx
83 | */
84 | public SpecialImageUnit(Context context, String specialText, Bitmap bitmap, int width, int height, int gravity) {
85 | super(specialText);
86 | this.context = context;
87 | this.bitmap = bitmap;
88 | this.width = width;
89 | this.height = height;
90 | this.gravity = gravity;
91 | }
92 |
93 | /**
94 | * Set Background Color
95 | * @param bgColor background color
96 | * @return this
97 | */
98 | public SpecialImageUnit setBgColor(int bgColor) {
99 | this.bgColor = bgColor;
100 | return this;
101 | }
102 |
103 | /**
104 | * Set Gravity
105 | * @param gravity use SpecialGravity.xx
106 | * @return this
107 | */
108 | public SpecialImageUnit setGravity(int gravity) {
109 | this.gravity = gravity;
110 | return this;
111 | }
112 |
113 | /**
114 | * Set Convert Mode
115 | * @param convertMode use SpecialConvertMode.xx
116 | * @return this
117 | */
118 | public SpecialImageUnit setConvertMode(int convertMode) {
119 | this.convertMode = convertMode;
120 | return this;
121 | }
122 |
123 | public boolean isClickable() {
124 | return isClickable;
125 | }
126 |
127 | /**
128 | * Use only in SimplifySpanBuild
129 | * @param clickable clickable
130 | */
131 | public void setClickable(boolean clickable) {
132 | isClickable = clickable;
133 | }
134 |
135 | public void setBitmap(Bitmap bitmap) {
136 | this.bitmap = bitmap;
137 | }
138 |
139 | public Bitmap getBitmap() {
140 | return bitmap;
141 | }
142 |
143 | public int getWidth() {
144 | return width;
145 | }
146 |
147 | public int getHeight() {
148 | return height;
149 | }
150 |
151 | public int getBgColor() {
152 | return bgColor;
153 | }
154 |
155 | public Context getContext() {
156 | return context;
157 | }
158 | }
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | [](http://www.android-gems.com/lib/iwgang/SimplifySpan)
2 | [](http://android-arsenal.com/details/1/2907)
3 | [](http://weibo.com/iwgang)
4 |
5 | # SimplifySpan
6 | A easy-to-use and powerful Spannable library
7 |
8 | ### screenshot
9 | 
10 | 
11 |
12 | ### gradle
13 | implementation 'com.github.iwgang:simplifyspan:2.2'
14 |
15 | ### Support Units
16 | * SpecialTextUnit
17 | * **text** (Constructor | String)
18 | * **gravity** (setGravity(gravity | int)) SpecialGravity.TOP, SpecialGravity.CENTER, SpecialGravity.BOTTOM
19 | * **convertMode** (setConvertMode(convertMode | int)) SpecialConvertMode.ONLY_FIRST, SpecialConvertMode.ALL, SpecialConvertMode.ONLY_LAST
20 | * **textColor** (Constructor Or setTextColor(int color))
21 | * **textSize** (Constructor Or setTextSize(float size)) ps
22 | * **backgroundColor** (setTextBackgroundColor(int color))
23 | * **showUnderline** (showUnderline())
24 | * **showStrikeThrough** (showStrikeThrough())
25 | * **textBold** (useTextBold())
26 | * **textItalic** (useTextItalic())
27 | * **textStyle** (setTextStyle(Typeface style))
28 | * **clickableUnit** (setClickableUnit(SpecialClickableUnit))
29 | * SpecialLabelUnit
30 | * **text** (Constructor | String)
31 | * **gravity** (setGravity(gravity | int)) SpecialGravity.TOP, SpecialGravity.CENTER, SpecialGravity.BOTTOM
32 | * **convertMode** (setConvertMode(convertMode | int)) SpecialConvertMode.ONLY_FIRST, SpecialConvertMode.ALL, SpecialConvertMode.ONLY_LAST
33 | * **labelTextColor** (Constructor | int color)
34 | * **labelTextSize** (Constructor | int color) sp
35 | * **labelBgColor** (Constructor | int color)
36 | * **bitmap** (Constructor | Bitmap)
37 | * **labelBgRadius** (setLabelBgRadius(float radius)) Only support labelBgColor
38 | * **labelBgWidth** And **labelBgHeight** (Constructor Or setLabelBgSize(int width, int height)) px
39 | * **padding** (setPadding(int padding)) px
40 | * **paddingLeft** (setPaddingLeft(int padding)) px
41 | * **paddingRight** (setPaddingRight(int padding)) px
42 | * **labelBgBorderColor** And **borderSize** (showBorder(int labelBgBorderColor, float borderSize | px))
43 | * **textBold** (useTextBold())
44 | * **textItalic** (useTextItalic())
45 | * **clickable** See SimplifySpanBuild.appendMultiClickable() Or SimplifySpanBuild.appendMultiClickableToFirst()
46 | * SpecialImageUnit
47 | * **text** (Constructor | String)
48 | * **gravity** (setGravity(gravity | int)) SpecialGravity.TOP, SpecialGravity.CENTER, SpecialGravity.BOTTOM
49 | * **convertMode** (setConvertMode(convertMode | int)) SpecialConvertMode.ONLY_FIRST, SpecialConvertMode.ALL, SpecialConvertMode.ONLY_LAST
50 | * **bitmap** (Constructor)
51 | * **width** And **height** (Constructor) px
52 | * **clickable** See SimplifySpanBuild.appendMultiClickable() Or SimplifySpanBuild.appendMultiClickableToFirst()
53 | * SpecialClickableUnit
54 | * **curTextView** (Constructor | TextView)
55 | * **onClickListener** (Constructor | OnClickableSpanListener)
56 | * **isShowUnderline** (showUnderline())
57 | * **pressTextColor** (setPressTextColor(int color))
58 | * **pressBgColor** (setPressBgColor(int color))
59 | * **normalTextColor** (setNormalTextColor(int color))
60 | * **normalBgColor** (setNormalBgColor(int color))
61 | * SpecialRawSpanUnit
62 | * **text** (Constructor | String)
63 | * **spanObj** (Constructor) Spannable Object
64 | * **flags** (Constructor | int) Spannable flags
65 |
66 | ### Support Methods
67 | * SimplifySpanBuild
68 | * **append** (string | Units)
69 | * **appendToFirst** (string | Units)
70 | * **appendMultiClickable** ()
71 | * **appendMultiClickableToFirst** ()
72 | * **build** ()
73 |
74 | ### how to use ?
75 | [Sample Code](https://github.com/iwgang/SimplifySpan/blob/master/app/src/main/java/cn/iwgang/simplifyspandemo/MainActivity.java)
76 | ```
77 | // sample 1
78 | tvText.setText(new SimplifySpanBuild("距离您:").append(new SpecialTextUnit("385", Color.BLUE)).append(" 米").build());
79 |
80 | // sample 2
81 | CharSequence spannableString = new SimplifySpanBuild(" 艾客优品雷霆Dock 2 雷电转USB3.0/火线/esata 扩展HUB")
82 | .appendToFirst(new SpecialLabelUnit("1212", Color.WHITE, sp2px(8), Color.RED, 70, 35).useTextBold().setGravity(SpecialGravity.CENTER))
83 | .appendToFirst(new SpecialLabelUnit("天猫", Color.WHITE, sp2px(8), 0xFFFF5000, 60, 35).setGravity(SpecialGravity.CENTER))
84 | .build();
85 | tvText.setText(spannableString);
86 | ```
87 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_main.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
12 |
13 |
20 |
21 |
30 |
31 |
39 |
40 |
46 |
47 |
56 |
57 |
65 |
66 |
74 |
75 |
84 |
85 |
93 |
94 |
103 |
104 |
114 |
115 |
125 |
126 |
127 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/library/src/main/java/cn/iwgang/simplifyspan/unit/SpecialTextUnit.java:
--------------------------------------------------------------------------------
1 | package cn.iwgang.simplifyspan.unit;
2 |
3 | import android.graphics.Typeface;
4 | import android.text.TextPaint;
5 | import android.widget.TextView;
6 |
7 | /**
8 | * Text Special Unit
9 | * Created by iWgang on 15/12/3.
10 | * https://github.com/iwgang/SimplifySpan
11 | */
12 | public class SpecialTextUnit extends BaseSpecialUnit {
13 | private int textStyle = Typeface.NORMAL;
14 | private int textColor;
15 | private int textBackgroundColor;
16 | private float textSize; // sp
17 | private boolean isShowUnderline;
18 | private boolean isShowStrikeThrough;
19 | private boolean isTextBold;
20 | private boolean isTextItalic;
21 | private SpecialClickableUnit specialClickableUnit;
22 | private TextView curTextView;
23 | private TextPaint curTextPaint;
24 |
25 | /**
26 | * @param text text
27 | */
28 | public SpecialTextUnit(String text) {
29 | super(text);
30 | }
31 |
32 | /**
33 | * @param text text
34 | * @param textColor Text Color
35 | */
36 | public SpecialTextUnit(String text, int textColor) {
37 | this(text);
38 | this.textColor = textColor;
39 | }
40 |
41 | /**
42 | * @param text text
43 | * @param textColor text color
44 | * @param textSize text size (unit:sp)
45 | */
46 | public SpecialTextUnit(String text, int textColor, float textSize) {
47 | this(text);
48 | this.textColor = textColor;
49 | this.textSize = textSize;
50 | }
51 |
52 | /**
53 | * @param text text
54 | * @param textColor text color
55 | * @param textSize text size (unit:sp)
56 | * @param gravity use SpecialGravity.xx
57 | * @param curTextView current TextView
58 | */
59 | public SpecialTextUnit(String text, int textColor, float textSize, int gravity, TextView curTextView) {
60 | this(text);
61 | this.textColor = textColor;
62 | this.textSize = textSize;
63 | this.gravity = gravity;
64 | this.curTextView = curTextView;
65 | if (curTextView != null) {
66 | this.curTextPaint = curTextView.getPaint();
67 | }
68 | }
69 |
70 | /**
71 | * Show StrikeThrough
72 | *
73 | * @return SpecialTextUnit
74 | */
75 | public SpecialTextUnit showStrikeThrough() {
76 | isShowStrikeThrough = true;
77 | return this;
78 | }
79 |
80 | /**
81 | * Show Underline
82 | *
83 | * @return SpecialTextUnit
84 | */
85 | public SpecialTextUnit showUnderline() {
86 | isShowUnderline = true;
87 | return this;
88 | }
89 |
90 | /**
91 | * Use Text Bold
92 | *
93 | * @return SpecialTextUnit
94 | */
95 | public SpecialTextUnit useTextBold() {
96 | isTextBold = true;
97 | return this;
98 | }
99 |
100 | /**
101 | * Use Text Italic
102 | *
103 | * @return SpecialTextUnit
104 | */
105 | public SpecialTextUnit useTextItalic() {
106 | isTextItalic = true;
107 | return this;
108 | }
109 |
110 | /**
111 | * Use TextView support textStyle
112 | *
113 | * @param textStyle please see {@link Typeface}
114 | * @return SpecialTextUnit
115 | */
116 | public SpecialTextUnit setTextStyle(int textStyle) {
117 | this.textStyle = textStyle;
118 | return this;
119 | }
120 |
121 | /**
122 | * Set Background Color
123 | *
124 | * @param textBackgroundColor color
125 | * @return SpecialTextUnit
126 | */
127 | public SpecialTextUnit setTextBackgroundColor(int textBackgroundColor) {
128 | this.textBackgroundColor = textBackgroundColor;
129 | return this;
130 | }
131 |
132 | /**
133 | * Set Special Text Color
134 | *
135 | * @param textColor color
136 | * @return SpecialTextUnit
137 | */
138 | public SpecialTextUnit setTextColor(int textColor) {
139 | this.textColor = textColor;
140 | return this;
141 | }
142 |
143 | /**
144 | * Set Special Text Size
145 | *
146 | * @param textSize size (sp)
147 | * @return SpecialTextUnit
148 | */
149 | public SpecialTextUnit setTextSize(float textSize) {
150 | this.textSize = textSize;
151 | return this;
152 | }
153 |
154 | /**
155 | * Set SpecialClickableUnit
156 | *
157 | * @param specialClickableUnit SpecialClickableUnit
158 | * @return SpecialTextUnit
159 | */
160 | public SpecialTextUnit setClickableUnit(SpecialClickableUnit specialClickableUnit) {
161 | this.specialClickableUnit = specialClickableUnit;
162 | return this;
163 | }
164 |
165 | /**
166 | * Set Gravity
167 | *
168 | * @param curTextView current TextView
169 | * @param gravity use SpecialGravity.xx
170 | * @return SpecialTextUnit
171 | */
172 | public SpecialTextUnit setGravity(TextView curTextView, int gravity) {
173 | this.curTextView = curTextView;
174 | if (curTextView != null) {
175 | this.curTextPaint = curTextView.getPaint();
176 | }
177 | this.gravity = gravity;
178 | return this;
179 | }
180 |
181 | /**
182 | * Set Gravity
183 | *
184 | * @param curTextPaint current TextView.getTextPaint
185 | * @param gravity use SpecialGravity.xx
186 | * @return SpecialTextUnit
187 | */
188 | public SpecialTextUnit setGravity(TextPaint curTextPaint, int gravity) {
189 | this.curTextPaint = curTextPaint;
190 | this.gravity = gravity;
191 | return this;
192 | }
193 |
194 | /**
195 | * Set Convert Mode
196 | *
197 | * @param convertMode use SpecialConvertMode.xx
198 | * @return SpecialTextUnit
199 | */
200 | public SpecialTextUnit setConvertMode(int convertMode) {
201 | this.convertMode = convertMode;
202 | return this;
203 | }
204 |
205 | public int getTextColor() {
206 | return textColor;
207 | }
208 |
209 | public int getTextBackgroundColor() {
210 | return textBackgroundColor;
211 | }
212 |
213 | public float getTextSize() {
214 | return textSize;
215 | }
216 |
217 | public boolean isShowUnderline() {
218 | return isShowUnderline;
219 | }
220 |
221 | public boolean isShowStrikeThrough() {
222 | return isShowStrikeThrough;
223 | }
224 |
225 | public boolean isTextBold() {
226 | return isTextBold;
227 | }
228 |
229 | public boolean isTextItalic() {
230 | return isTextItalic;
231 | }
232 |
233 | public int getTextStyle() {
234 | return textStyle;
235 | }
236 |
237 | public TextView getCurTextView() {
238 | return curTextView;
239 | }
240 |
241 | public TextPaint getCurTextPaint() {
242 | return curTextPaint;
243 | }
244 |
245 | public SpecialClickableUnit getSpecialClickableUnit() {
246 | return specialClickableUnit;
247 | }
248 |
249 | }
--------------------------------------------------------------------------------
/library/src/main/java/cn/iwgang/simplifyspan/unit/SpecialLabelUnit.java:
--------------------------------------------------------------------------------
1 | package cn.iwgang.simplifyspan.unit;
2 |
3 | import android.graphics.Bitmap;
4 | import android.graphics.Typeface;
5 |
6 | /**
7 | * Label Special Unit
8 | * Created by iWgang on 15/12/7.
9 | * https://github.com/iwgang/SimplifySpan
10 | */
11 | public class SpecialLabelUnit extends BaseSpecialUnit {
12 | private int textStyle = Typeface.NORMAL;
13 | private int labelTextColor;
14 | private float labelTextSize; // sp
15 | private Bitmap mBitmap;
16 | private int labelBgColor;
17 | private float labelBgRadius;
18 | private int labelBgWidth; // px
19 | private int labelBgHeight; // px
20 | private int padding; // px
21 | private int paddingLeft; // px
22 | private int paddingRight; // px
23 | private float borderSize; // px
24 | private int labelBgBorderColor;
25 | private boolean isShowBorder;
26 | private boolean isTextBold;
27 | private boolean isTextItalic;
28 | private boolean isClickable;
29 | private int bgColor;
30 |
31 | /**
32 | * @param labelText label text
33 | * @param labelTextColor label text color
34 | * @param labelTextSize label text size (unit:xp)
35 | * @param labelBgColor label background color
36 | */
37 | public SpecialLabelUnit(String labelText, int labelTextColor, float labelTextSize, int labelBgColor) {
38 | super(labelText);
39 | this.labelTextSize = labelTextSize;
40 | this.labelTextColor = labelTextColor;
41 | this.labelBgColor = labelBgColor;
42 | }
43 |
44 | /**
45 | * @param labelText label text
46 | * @param labelTextColor label text color
47 | * @param labelTextSize label text size (unit:xp)
48 | * @param labelBgColor label background color
49 | * @param labelBgWidth label background width
50 | * @param labelBgHeight label background height
51 | */
52 | public SpecialLabelUnit(String labelText, int labelTextColor, float labelTextSize, int labelBgColor, int labelBgWidth, int labelBgHeight) {
53 | super(labelText);
54 | this.labelTextSize = labelTextSize;
55 | this.labelTextColor = labelTextColor;
56 | this.labelBgColor = labelBgColor;
57 | this.labelBgWidth = labelBgWidth;
58 | this.labelBgHeight = labelBgHeight;
59 | }
60 |
61 | /**
62 | * @param labelText label text
63 | * @param labelTextColor label text color
64 | * @param labelTextSize label text size (unit:xp)
65 | * @param bitmap label background image bitmap
66 | */
67 | public SpecialLabelUnit(String labelText, int labelTextColor, float labelTextSize, Bitmap bitmap) {
68 | super(labelText);
69 | this.labelTextSize = labelTextSize;
70 | this.labelTextColor = labelTextColor;
71 | this.mBitmap = bitmap;
72 | }
73 |
74 | /**
75 | * @param labelText label text
76 | * @param labelTextColor label text color
77 | * @param labelTextSize label text size (unit:xp)
78 | * @param bitmap label background image bitmap
79 | * @param labelBgWidth label background width
80 | * @param labelBgHeight label background height
81 | */
82 | public SpecialLabelUnit(String labelText, int labelTextColor, float labelTextSize, Bitmap bitmap, int labelBgWidth, int labelBgHeight) {
83 | super(labelText);
84 | this.labelTextSize = labelTextSize;
85 | this.labelTextColor = labelTextColor;
86 | this.mBitmap = bitmap;
87 | this.labelBgWidth = labelBgWidth;
88 | this.labelBgHeight = labelBgHeight;
89 | }
90 |
91 | public SpecialLabelUnit setLabelBgSize(int labelBgWidth, int labelBgHeight) {
92 | this.labelBgWidth = labelBgWidth;
93 | this.labelBgHeight = labelBgHeight;
94 | return this;
95 | }
96 |
97 | public SpecialLabelUnit setBitmap(Bitmap mBitmap) {
98 | this.mBitmap = mBitmap;
99 | return this;
100 | }
101 |
102 | public SpecialLabelUnit setPadding(int padding) {
103 | this.padding = padding;
104 | return this;
105 | }
106 |
107 | public SpecialLabelUnit setPaddingLeft(int paddingLeft) {
108 | this.paddingLeft = paddingLeft;
109 | return this;
110 | }
111 |
112 | public SpecialLabelUnit setPaddingRight(int paddingRight) {
113 | this.paddingRight = paddingRight;
114 | return this;
115 | }
116 |
117 | public SpecialLabelUnit setLabelBgRadius(float labelBgRadius) {
118 | this.labelBgRadius = labelBgRadius;
119 | return this;
120 | }
121 |
122 | public SpecialLabelUnit showBorder(int labelBgBorderColor, float borderSize) {
123 | isShowBorder = true;
124 | this.labelBgBorderColor = labelBgBorderColor;
125 | this.borderSize = borderSize;
126 | return this;
127 | }
128 |
129 | public SpecialLabelUnit useTextBold() {
130 | isTextBold = true;
131 | return this;
132 | }
133 |
134 | public SpecialLabelUnit useTextItalic() {
135 | isTextItalic = true;
136 | return this;
137 | }
138 |
139 | /**
140 | * Use TextView support textStyle
141 | *
142 | * @param textStyle please see {@link Typeface}
143 | * @return SpecialTextUnit
144 | */
145 | public SpecialLabelUnit setTextStyle(int textStyle) {
146 | this.textStyle = textStyle;
147 | return this;
148 | }
149 |
150 | public SpecialLabelUnit setBgColor(int bgColor) {
151 | this.bgColor = bgColor;
152 | return this;
153 | }
154 |
155 | /**
156 | * Set Gravity
157 | *
158 | * @param gravity use SpecialGravity.xx
159 | * @return this
160 | */
161 | public SpecialLabelUnit setGravity(int gravity) {
162 | this.gravity = gravity;
163 | return this;
164 | }
165 |
166 | /**
167 | * Set Convert Mode
168 | *
169 | * @param convertMode use SpecialConvertMode.xx
170 | * @return this
171 | */
172 | public SpecialLabelUnit setConvertMode(int convertMode) {
173 | this.convertMode = convertMode;
174 | return this;
175 | }
176 |
177 | public int getLabelBgColor() {
178 | return labelBgColor;
179 | }
180 |
181 | public int getLabelBgHeight() {
182 | return labelBgHeight;
183 | }
184 |
185 | public int getLabelBgWidth() {
186 | return labelBgWidth;
187 | }
188 |
189 | public int getLabelTextColor() {
190 | return labelTextColor;
191 | }
192 |
193 | /**
194 | * label Text Size
195 | *
196 | * @return px
197 | */
198 | public float getLabelTextSize() {
199 | return labelTextSize;
200 | }
201 |
202 | public int getPadding() {
203 | return padding;
204 | }
205 |
206 | public int getPaddingLeft() {
207 | return paddingLeft;
208 | }
209 |
210 | public int getPaddingRight() {
211 | return paddingRight;
212 | }
213 |
214 | public float getLabelBgRadius() {
215 | return labelBgRadius;
216 | }
217 |
218 | public boolean isShowBorder() {
219 | return isShowBorder;
220 | }
221 |
222 | /**
223 | * Use only in SimplifySpanBuild
224 | *
225 | * @param clickable set clickable
226 | */
227 | public void setClickable(boolean clickable) {
228 | isClickable = clickable;
229 | }
230 |
231 | public float getBorderSize() {
232 | return borderSize;
233 | }
234 |
235 | public int getLabelBgBorderColor() {
236 | return labelBgBorderColor;
237 | }
238 |
239 | public boolean isTextBold() {
240 | return isTextBold;
241 | }
242 |
243 | public Bitmap getBitmap() {
244 | return mBitmap;
245 | }
246 |
247 | public boolean isClickable() {
248 | return isClickable;
249 | }
250 |
251 | public int getBgColor() {
252 | return bgColor;
253 | }
254 |
255 | public boolean isTextItalic() {
256 | return isTextItalic;
257 | }
258 |
259 | public int getTextStyle() {
260 | return textStyle;
261 | }
262 |
263 |
264 | }
--------------------------------------------------------------------------------
/library/src/main/java/cn/iwgang/simplifyspan/customspan/CustomLabelSpan.java:
--------------------------------------------------------------------------------
1 | package cn.iwgang.simplifyspan.customspan;
2 |
3 | import android.graphics.Bitmap;
4 | import android.graphics.Canvas;
5 | import android.graphics.Paint;
6 | import android.graphics.Rect;
7 | import android.graphics.RectF;
8 | import android.graphics.Typeface;
9 | import android.media.ThumbnailUtils;
10 | import android.text.style.ReplacementSpan;
11 |
12 | import cn.iwgang.simplifyspan.other.OnClickStateChangeListener;
13 | import cn.iwgang.simplifyspan.other.SpecialGravity;
14 | import cn.iwgang.simplifyspan.unit.SpecialLabelUnit;
15 |
16 | /**
17 | * Custom Label Span
18 | * Created by iWgang on 15/12/3.
19 | * https://github.com/iwgang/SimplifySpan
20 | */
21 | public class CustomLabelSpan extends ReplacementSpan implements OnClickStateChangeListener {
22 | private SpecialLabelUnit mSpecialLabelUnit;
23 | private String mSpecialText;
24 | private float mFinalWidth, mFinalHeight;
25 | private int mSpecialTextHeight = 0;
26 | private float mSpecialTextWidth = 0;
27 | private int mLineTextHeight = 0;
28 | private int mLineTextBaselineOffset = 0;
29 | private int mSpecialTextBaselineOffset = 0;
30 | private float mLabelBgRadius;
31 | private Bitmap mBitmap;
32 | private String mNormalSizeText;
33 |
34 | private int mPaddingTop = 0;
35 | private int mPaddingBottom = 0;
36 | private int mPaddingLeft = 0;
37 | private int mPaddingRight = 0;
38 | private boolean isLabelBgCenter = true;
39 | private boolean isDrawBitmap = false;
40 | private RectF bgRect;
41 | private boolean isInit = true;
42 |
43 | private int mBgColor;
44 | private boolean isSelected;
45 | private boolean isClickable;
46 | private int pressBgColor;
47 |
48 | public CustomLabelSpan(String normalSizeText, SpecialLabelUnit specialLabelUnit) {
49 | mSpecialLabelUnit = specialLabelUnit;
50 | mSpecialText = mSpecialLabelUnit.getText();
51 | mNormalSizeText = normalSizeText;
52 | isClickable = mSpecialLabelUnit.isClickable();
53 | mBgColor = mSpecialLabelUnit.getBgColor();
54 |
55 | mBitmap = mSpecialLabelUnit.getBitmap();
56 | if (null == mBitmap) {
57 | mLabelBgRadius = mSpecialLabelUnit.getLabelBgRadius();
58 | if (mLabelBgRadius > 0) {
59 | bgRect = new RectF();
60 | }
61 | } else {
62 | isDrawBitmap = true;
63 | }
64 | initPadding();
65 | }
66 |
67 | private void initPadding() {
68 | if (mSpecialLabelUnit.getLabelBgHeight() > 0 || mSpecialLabelUnit.getLabelBgWidth() > 0)
69 | return;
70 |
71 | int allPadding = mSpecialLabelUnit.getPadding();
72 |
73 | mPaddingTop = allPadding;
74 | mPaddingBottom = allPadding;
75 |
76 | int paddingLeft = mSpecialLabelUnit.getPaddingLeft();
77 | if (paddingLeft > 0) {
78 | mPaddingLeft = paddingLeft;
79 | } else {
80 | mPaddingLeft = allPadding;
81 | }
82 |
83 | int paddingRight = mSpecialLabelUnit.getPaddingRight();
84 | if (paddingRight > 0) {
85 | mPaddingRight = paddingRight;
86 | } else {
87 | mPaddingRight = allPadding;
88 | }
89 |
90 | if (mPaddingTop > 0 || mPaddingBottom > 0 || mPaddingLeft > 0 || mPaddingRight > 0)
91 | isLabelBgCenter = false;
92 | }
93 |
94 | @Override
95 | public void onStateChange(boolean isSelected, int pressBgColor) {
96 | this.isSelected = isSelected;
97 | this.pressBgColor = pressBgColor;
98 | }
99 |
100 | @Override
101 | public int getSize(Paint paint, CharSequence text, int start, int end, Paint.FontMetricsInt fm) {
102 | if (isInit) {
103 | isInit = false;
104 | initFinalHeight(paint);
105 | initFinalWidth(paint);
106 |
107 | // Re Create Bitmap
108 | if (isDrawBitmap) {
109 | Bitmap newBitmap = ThumbnailUtils.extractThumbnail(mBitmap, Math.round(mFinalWidth), Math.round(mFinalHeight));
110 | if (null != newBitmap) {
111 | mBitmap.recycle();
112 | mBitmap = newBitmap;
113 | }
114 | }
115 | }
116 |
117 | return Math.round(mFinalWidth);
118 | }
119 |
120 | @Override
121 | public void draw(Canvas canvas, CharSequence text, int start, int end, float x, int top, int y, int bottom, Paint paint) {
122 | float finalUnitHeight = bottom - top;
123 | float bgTop = bottom - finalUnitHeight;
124 | if (isClickable && isSelected && pressBgColor != 0) {
125 | // click background
126 | paint.setColor(pressBgColor);
127 | canvas.drawRect(x, bgTop, x + mFinalWidth, bgTop + finalUnitHeight, paint);
128 | } else {
129 | // normal background
130 | if (mBgColor != 0) {
131 | paint.setColor(mBgColor);
132 | canvas.drawRect(x, bgTop, x + mFinalWidth, bgTop + finalUnitHeight, paint);
133 | }
134 | }
135 |
136 | float labelTextSize = mSpecialLabelUnit.getLabelTextSize();
137 | if (labelTextSize > 0 && labelTextSize != paint.getTextSize()) {
138 | paint.setTextSize(labelTextSize);
139 | }
140 |
141 | float newStartY = y;
142 | int newTextY = y;
143 |
144 | switch (mSpecialLabelUnit.getGravity()) {
145 | case SpecialGravity.TOP:
146 | newStartY -= (mLineTextHeight - mLineTextBaselineOffset);
147 | newTextY -= (mLineTextHeight - mSpecialTextHeight - (mLineTextBaselineOffset - mSpecialTextBaselineOffset) - mPaddingTop);
148 | break;
149 | case SpecialGravity.CENTER:
150 | newStartY -= (mLineTextHeight / 2 + mFinalHeight / 2 - mLineTextBaselineOffset);
151 | newTextY -= (mLineTextHeight / 2 - mSpecialTextHeight / 2 - (mLineTextBaselineOffset - mSpecialTextBaselineOffset));
152 | break;
153 | case SpecialGravity.BOTTOM:
154 | newStartY -= mFinalHeight - mLineTextBaselineOffset;
155 | newTextY -= mPaddingBottom - (mLineTextBaselineOffset - mSpecialTextBaselineOffset);
156 | break;
157 | }
158 |
159 | if (isDrawBitmap) {
160 | canvas.drawBitmap(mBitmap, x, newStartY, paint);
161 | } else {
162 | paint.setColor(mSpecialLabelUnit.getLabelBgColor());
163 | if (mLabelBgRadius > 0) {
164 | bgRect.top = newStartY;
165 | bgRect.bottom = newStartY + mFinalHeight;
166 | bgRect.left = x;
167 | bgRect.right = x + mFinalWidth;
168 | if (mSpecialLabelUnit.isShowBorder()) {
169 | float borderSize = mSpecialLabelUnit.getBorderSize();
170 | // draw background
171 | canvas.drawRect(x, newStartY, x + mFinalWidth, newStartY + mFinalHeight, paint);
172 |
173 | // draw border
174 | paint.setColor(mSpecialLabelUnit.getLabelBgBorderColor());
175 | paint.setStyle(Paint.Style.STROKE);
176 | paint.setStrokeWidth(borderSize);
177 | canvas.drawRoundRect(bgRect, mLabelBgRadius, mLabelBgRadius, paint);
178 |
179 | // recover paint
180 | paint.setStyle(Paint.Style.FILL);
181 | } else {
182 | // draw background
183 | canvas.drawRoundRect(bgRect, mLabelBgRadius, mLabelBgRadius, paint);
184 | }
185 | } else {
186 | // draw background
187 | canvas.drawRect(x, newStartY, x + mFinalWidth, newStartY + mFinalHeight, paint);
188 |
189 | if (mSpecialLabelUnit.isShowBorder()) {
190 | // draw border
191 | paint.setColor(mSpecialLabelUnit.getLabelBgBorderColor());
192 | paint.setStyle(Paint.Style.STROKE);
193 | paint.setStrokeWidth(mSpecialLabelUnit.getBorderSize());
194 | canvas.drawRect(x, newStartY, x + mFinalWidth, newStartY + mFinalHeight, paint);
195 |
196 | // recover paint
197 | paint.setStyle(Paint.Style.FILL);
198 | }
199 | }
200 | }
201 |
202 | paint.setColor(mSpecialLabelUnit.getLabelTextColor());
203 | float newTextX;
204 | if (isLabelBgCenter) {
205 | newTextX = x + Math.round(mFinalWidth / 2f - mSpecialTextWidth / 2f);
206 | } else {
207 | newTextX = x + mPaddingLeft;
208 | }
209 |
210 | if (mSpecialLabelUnit.isTextBold())
211 | paint.setFakeBoldText(true);
212 |
213 | if (mSpecialLabelUnit.isTextItalic())
214 | paint.setTypeface(Typeface.create((String) null, Typeface.ITALIC));
215 |
216 | canvas.drawText(mSpecialText, newTextX, newTextY, paint);
217 | }
218 |
219 | private float initFinalWidth(Paint paint) {
220 | if (mFinalWidth <= 0) {
221 | float labelTextSize = mSpecialLabelUnit.getLabelTextSize();
222 | if (labelTextSize > 0 && labelTextSize != paint.getTextSize()) {
223 | paint.setTextSize(labelTextSize);
224 | }
225 |
226 | int labelBgWidth = mSpecialLabelUnit.getLabelBgWidth();
227 | mSpecialTextWidth = paint.measureText(mSpecialText, 0, mSpecialText.length());
228 | if (labelBgWidth > 0 && labelBgWidth > mSpecialTextWidth) {
229 | mFinalWidth = labelBgWidth;
230 | } else {
231 | mFinalWidth = mSpecialTextWidth + mPaddingLeft + mPaddingRight;
232 | }
233 | }
234 |
235 | return mFinalWidth;
236 | }
237 |
238 | private float initFinalHeight(Paint paint) {
239 | if (mFinalHeight <= 0) {
240 | int labelBgHeight = mSpecialLabelUnit.getLabelBgHeight();
241 |
242 | Rect specialTextRect = new Rect();
243 | paint.getTextBounds(mNormalSizeText, 0, mNormalSizeText.length(), specialTextRect);
244 | mLineTextHeight = specialTextRect.height();
245 | mLineTextBaselineOffset = specialTextRect.bottom;
246 |
247 | float labelTextSize = mSpecialLabelUnit.getLabelTextSize();
248 | if (labelTextSize > 0 && labelTextSize != paint.getTextSize()) {
249 | paint.setTextSize(labelTextSize);
250 | }
251 |
252 | paint.getTextBounds(mSpecialText, 0, mSpecialText.length(), specialTextRect);
253 | mSpecialTextHeight = specialTextRect.height();
254 | mSpecialTextBaselineOffset = specialTextRect.bottom;
255 |
256 | if (labelBgHeight > 0 && labelBgHeight > mSpecialTextHeight && labelBgHeight <= mLineTextHeight) {
257 | mFinalHeight = labelBgHeight;
258 | } else {
259 | mFinalHeight = mSpecialTextHeight + mPaddingTop + mPaddingBottom;
260 | }
261 |
262 | if (mFinalHeight > mLineTextHeight) {
263 | mFinalHeight = mLineTextHeight;
264 | }
265 | }
266 |
267 | return mFinalHeight;
268 | }
269 |
270 | }
--------------------------------------------------------------------------------
/app/src/main/java/cn/iwgang/simplifyspandemo/MainActivity.java:
--------------------------------------------------------------------------------
1 | package cn.iwgang.simplifyspandemo;
2 |
3 | import android.graphics.BitmapFactory;
4 | import android.graphics.Color;
5 | import android.graphics.Typeface;
6 | import android.os.Bundle;
7 | import android.support.v7.app.AppCompatActivity;
8 | import android.widget.TextView;
9 | import android.widget.Toast;
10 |
11 | import cn.iwgang.simplifyspan.SimplifySpanBuild;
12 | import cn.iwgang.simplifyspan.customspan.CustomClickableSpan;
13 | import cn.iwgang.simplifyspan.other.OnClickableSpanListener;
14 | import cn.iwgang.simplifyspan.other.SpecialConvertMode;
15 | import cn.iwgang.simplifyspan.other.SpecialGravity;
16 | import cn.iwgang.simplifyspan.unit.SpecialClickableUnit;
17 | import cn.iwgang.simplifyspan.unit.SpecialImageUnit;
18 | import cn.iwgang.simplifyspan.unit.SpecialLabelUnit;
19 | import cn.iwgang.simplifyspan.unit.SpecialTextUnit;
20 |
21 | public class MainActivity extends AppCompatActivity implements OnClickableSpanListener {
22 |
23 | @Override
24 | protected void onCreate(Bundle savedInstanceState) {
25 | super.onCreate(savedInstanceState);
26 | setContentView(R.layout.activity_main);
27 |
28 | TextView tvText1 = (TextView) findViewById(R.id.tv_text1);
29 | TextView tvText11 = (TextView) findViewById(R.id.tv_text11);
30 | TextView tvText2 = (TextView) findViewById(R.id.tv_text2);
31 | TextView tvText21 = (TextView) findViewById(R.id.tv_text21);
32 | TextView tvText3 = (TextView) findViewById(R.id.tv_text3);
33 | TextView tvText4 = (TextView) findViewById(R.id.tv_text4);
34 | TextView tvText41 = (TextView) findViewById(R.id.tv_text41);
35 |
36 | CharSequence tv1SpannableString = new SimplifySpanBuild(" 艾客优品雷霆Dock 2 雷电转USB3.0/火线/esata 扩展HUB")
37 | .appendToFirst(new SpecialLabelUnit("1212", Color.WHITE, sp2px(8), Color.RED, 70, 35).useTextBold().setGravity(SpecialGravity.CENTER))
38 | .appendToFirst(new SpecialLabelUnit("天猫", Color.WHITE, sp2px(8), 0xFFFF5000, 60, 35).setGravity(SpecialGravity.CENTER))
39 | .build();
40 | tvText1.setText(tv1SpannableString);
41 |
42 | // tvText1.setText(new SimplifySpanBuild("距离您:").append(new SpecialTextUnit("385", Color.BLUE)).append(" 米").build());
43 |
44 | SimplifySpanBuild simplifySpanBuild11 = new SimplifySpanBuild();
45 | simplifySpanBuild11.append(new SpecialLabelUnit("火", Color.RED, sp2px(7), BitmapFactory.decodeResource(getResources(), R.drawable.label)).setPadding(15).setGravity(SpecialGravity.CENTER))
46 | .append("正常")
47 | .append(new SpecialLabelUnit("火", Color.RED, sp2px(7), BitmapFactory.decodeResource(getResources(), R.drawable.label)).setPadding(15).setGravity(SpecialGravity.TOP))
48 | .append("正常")
49 | .append(new SpecialLabelUnit("火", Color.RED, sp2px(7), BitmapFactory.decodeResource(getResources(), R.drawable.label)).setPadding(15).setGravity(SpecialGravity.BOTTOM))
50 | .append("正常")
51 | .append(new SpecialLabelUnit("原创", Color.BLACK, sp2px(10), BitmapFactory.decodeResource(getResources(), R.drawable.tag)).setPadding(5).setPaddingLeft(15).setPaddingRight(30).setGravity(SpecialGravity.TOP))
52 | .append("正常")
53 | .append(new SpecialLabelUnit("原创", Color.BLACK, sp2px(10), BitmapFactory.decodeResource(getResources(), R.drawable.tag)).setPadding(5).setPaddingLeft(15).setPaddingRight(30).setGravity(SpecialGravity.CENTER))
54 | .append("正常")
55 | .append(new SpecialLabelUnit("原创", Color.BLACK, sp2px(10), BitmapFactory.decodeResource(getResources(), R.drawable.tag)).setPadding(5).setPaddingLeft(15).setPaddingRight(30).setGravity(SpecialGravity.BOTTOM))
56 | .append("正常")
57 | .append(new SpecialLabelUnit("原创", Color.WHITE, sp2px(10), 0xFFFF5000).setLabelBgRadius(5).setPadding(5).setPaddingLeft(10).setPaddingRight(10).setGravity(SpecialGravity.BOTTOM))
58 | .append("正常")
59 | .append(new SpecialLabelUnit("原创", Color.WHITE, sp2px(10), 0xFFFF5000).setLabelBgRadius(5).setPadding(5).setPaddingLeft(10).setPaddingRight(10).setGravity(SpecialGravity.TOP))
60 | .append("正常")
61 | .append(new SpecialLabelUnit("原创", Color.WHITE, sp2px(10), 0xFFFF5000).setLabelBgRadius(5).setPadding(5).setPaddingLeft(10).setPaddingRight(10).setGravity(SpecialGravity.CENTER))
62 | .append("正常")
63 | .append(new SpecialLabelUnit("原创", Color.WHITE, sp2px(10), Color.GRAY).setLabelBgRadius(5).showBorder(Color.BLACK, 2).setPadding(5).setPaddingLeft(10).setPaddingRight(10).setGravity(SpecialGravity.BOTTOM))
64 | .append("正常")
65 | .append(new SpecialLabelUnit("原创", Color.WHITE, sp2px(10), Color.GRAY).setLabelBgRadius(5).showBorder(Color.BLACK, 2).setPadding(5).setPaddingLeft(10).setPaddingRight(10).setGravity(SpecialGravity.TOP))
66 | .append("正常")
67 | .append(new SpecialLabelUnit("原创", Color.WHITE, sp2px(10), Color.GRAY).setLabelBgRadius(5).showBorder(Color.BLACK, 2).setPadding(5).setPaddingLeft(10).setPaddingRight(10).setGravity(SpecialGravity.CENTER))
68 | .append("正常")
69 | .append(new SpecialLabelUnit("原创", Color.RED, sp2px(10), Color.TRANSPARENT).showBorder(Color.BLACK, 2).setPadding(5).setPaddingLeft(10).setPaddingRight(10).setGravity(SpecialGravity.BOTTOM))
70 | .append("正常")
71 | .append(new SpecialLabelUnit("原创", Color.RED, sp2px(10), Color.TRANSPARENT).showBorder(Color.BLACK, 2).setPadding(5).setPaddingLeft(10).setPaddingRight(10).setGravity(SpecialGravity.TOP))
72 | .append("正常")
73 | .append(new SpecialLabelUnit("原创", Color.RED, sp2px(10), Color.TRANSPARENT).showBorder(Color.BLACK, 2).setPadding(5).setPaddingLeft(10).setPaddingRight(10).setGravity(SpecialGravity.CENTER));
74 | tvText11.setText(simplifySpanBuild11.build());
75 |
76 | SimplifySpanBuild simplifySpanBuild2 = new SimplifySpanBuild("替换所有张字的颜色及字体大小并加粗,张歆艺、张馨予、张嘉倪、张涵予、张含韵、张韶涵、张嘉译、张佳宁、大张伟", new SpecialTextUnit("张").useTextBold().setTextSize(20).setTextColor(0xFFFFA500).setConvertMode(SpecialConvertMode.ALL));
77 | tvText2.setText(simplifySpanBuild2.build());
78 |
79 | SimplifySpanBuild simplifySpanBuild21 = new SimplifySpanBuild();
80 | simplifySpanBuild21
81 | .append(new SpecialTextUnit("正常字体").setTextSize(18).setTextStyle(Typeface.NORMAL))
82 | .append(new SpecialTextUnit("粗体").setTextSize(18).useTextBold())
83 | .append(new SpecialTextUnit("斜体").setTextSize(18).useTextItalic())
84 | .append(new SpecialTextUnit("粗斜体").setTextSize(18).useTextBold().useTextItalic())
85 | .append(new SpecialTextUnit("黑体").setTextSize(18).setTextStyle(Typeface.DEFAULT_BOLD.getStyle()))
86 | .append(new SpecialTextUnit("等宽1字体").setTextSize(18).setTextStyle(Typeface.MONOSPACE.getStyle()))
87 | .append(new SpecialTextUnit("Sans Serif字体").setTextSize(18).setTextStyle(Typeface.SANS_SERIF.getStyle()))
88 | .append(new SpecialTextUnit("Serif字体").setTextSize(18).setTextStyle(Typeface.SERIF.getStyle()))
89 | .append("\n")
90 | .append(new SpecialTextUnit("居中").setTextSize(12).setGravity(tvText21.getPaint(), SpecialGravity.CENTER).setTextColor(Color.BLUE))
91 | .append("正常")
92 | .append(new SpecialTextUnit("顶部").setTextSize(12).setGravity(tvText21, SpecialGravity.TOP).setTextColor(0xFFFF5000))
93 | .append("正常")
94 | .append(new SpecialTextUnit("底部").setTextSize(12).setTextColor(0xFF8B658B));
95 | tvText21.setText(simplifySpanBuild21.build());
96 |
97 | SimplifySpanBuild simplifySpanBuild3 = new SimplifySpanBuild();
98 | simplifySpanBuild3.append(new SpecialImageUnit(this, BitmapFactory.decodeResource(getResources(), R.drawable.ic_bulletin), 50, 50).setGravity(SpecialGravity.CENTER))
99 | .append("正常")
100 | .append(new SpecialImageUnit(this, BitmapFactory.decodeResource(getResources(), R.drawable.test_img), 150, 150))
101 | .append("正常")
102 | .append(new SpecialImageUnit(this, BitmapFactory.decodeResource(getResources(), R.mipmap.ic_launcher), 50, 50).setGravity(SpecialGravity.CENTER))
103 | .append("正常")
104 | .append(new SpecialImageUnit(this, BitmapFactory.decodeResource(getResources(), R.mipmap.ic_launcher), 50, 50).setGravity(SpecialGravity.TOP))
105 | .append("正常")
106 | .append(new SpecialImageUnit(this, BitmapFactory.decodeResource(getResources(), R.mipmap.ic_launcher), 50, 50).setGravity(SpecialGravity.BOTTOM));
107 | tvText3.setText(simplifySpanBuild3.build());
108 |
109 |
110 | int linkNorTextColor = 0xFF483D8B;
111 | int linkPressBgColor = 0xFF87CEFA;
112 | SimplifySpanBuild simplifySpanBuild4 = new SimplifySpanBuild();
113 | simplifySpanBuild4.append(new SpecialTextUnit("@英雄联盟", linkNorTextColor).setClickableUnit(new SpecialClickableUnit(tvText4, this).setPressBgColor(linkPressBgColor)))
114 | .append(" : ")
115 | .append(new SpecialTextUnit("#LOG夜话#", linkNorTextColor).setClickableUnit(new SpecialClickableUnit(tvText4, this).setPressBgColor(linkPressBgColor)))
116 | .append(new SpecialTextUnit("#LOL明星召唤师#", linkNorTextColor).setClickableUnit(new SpecialClickableUnit(tvText4, this).setPressBgColor(linkPressBgColor)))
117 | .append("星光熠熠的各赛区阵容、精彩的1V1Solo赛、克隆/双人共玩等奇葩套路层出不穷的花样对抗,你最期待看到哪位选手、哪种模式的对决?");
118 | simplifySpanBuild4.appendMultiClickable(new SpecialClickableUnit(tvText4, this).setNormalTextColor(linkNorTextColor).setPressBgColor(linkPressBgColor),
119 | " ",
120 | new SpecialImageUnit(this, BitmapFactory.decodeResource(getResources(), R.drawable.timeline_card_small_video), 35, 35).setGravity(SpecialGravity.CENTER),
121 | new SpecialTextUnit("LOL新大战闻声识英雄 "));
122 | simplifySpanBuild4.append("完整文章见 ");
123 | simplifySpanBuild4.appendMultiClickable(new SpecialClickableUnit(tvText4, this).setNormalTextColor(linkNorTextColor).setPressBgColor(linkPressBgColor),
124 | new SpecialImageUnit(this, BitmapFactory.decodeResource(getResources(), R.drawable.timeline_card_small_article), 30, 30).setGravity(SpecialGravity.CENTER),
125 | new SpecialTextUnit("LOL超强攻略,不看绝对后悔 ").showUnderline());
126 | simplifySpanBuild4.append(" 更多好玩的内容请点击 ");
127 | simplifySpanBuild4.appendMultiClickable(new SpecialClickableUnit(tvText4, this).setNormalTextColor(linkNorTextColor).setPressBgColor(linkPressBgColor),
128 | new SpecialImageUnit(this, BitmapFactory.decodeResource(getResources(), R.drawable.timeline_card_small_web), 42, 42).setGravity(SpecialGravity.CENTER),
129 | new SpecialTextUnit("网页链接"));
130 | simplifySpanBuild4.append(" 已收录 ");
131 | simplifySpanBuild4.appendMultiClickable(new SpecialClickableUnit(tvText4, this).setNormalTextColor(linkNorTextColor).setPressBgColor(linkPressBgColor),
132 | " ",
133 | new SpecialLabelUnit("酷玩", 0xFF483D8B, sp2px(8), Color.TRANSPARENT).showBorder(0xFF483D8B, 2).setLabelBgRadius(8).setPadding(5).setPaddingLeft(8).setPaddingRight(10).setGravity(SpecialGravity.CENTER),
134 | new SpecialTextUnit(" LOL新闻库 "));
135 | simplifySpanBuild4.append(" 后面加的内容是为了凑字数的哈");
136 | tvText4.setText(simplifySpanBuild4.build());
137 |
138 |
139 | SimplifySpanBuild simplifySpanBuild41 = new SimplifySpanBuild();
140 | simplifySpanBuild41.append("无默认背景")
141 | .append(new SpecialTextUnit("点我点我1").setClickableUnit(new SpecialClickableUnit(tvText41, this).setTag("1").setPressBgColor(0xFFFF5000)).setTextColor(Color.BLUE))
142 | .append("无默认背景显示下划线")
143 | .append(new SpecialTextUnit("点我点我2").setClickableUnit(new SpecialClickableUnit(tvText41, this).setTag("2").showUnderline().setPressBgColor(0xFFFF5000).setPressTextColor(Color.WHITE)).setTextColor(0xFFFF5000))
144 | .append("有默认背景")
145 | .append(new SpecialTextUnit("点我点我3").setClickableUnit(new SpecialClickableUnit(tvText41, this).setTag("3").setPressBgColor(Color.BLUE).setPressTextColor(Color.WHITE)).setTextColor(0xFFFF5000).setTextBackgroundColor(0xFF87CEEB))
146 | .append("我只是个结尾");
147 | tvText41.setText(simplifySpanBuild41.build());
148 | }
149 |
150 | @Override
151 | public void onClick(TextView tv, CustomClickableSpan clickableSpan) {
152 | Toast.makeText(MainActivity.this, "Click Text: " + clickableSpan.getClickText()
153 | + " tag: " + clickableSpan.getTag()
154 | + " StartSpanIndex: " + clickableSpan.getStartSpanIndex()
155 | + " EndSpanIndex: " + clickableSpan.getEndSpanIndex()
156 | , Toast.LENGTH_SHORT).show();
157 | }
158 |
159 | private float sp2px(float spValue) {
160 | final float scale = getResources().getDisplayMetrics().scaledDensity;
161 | return spValue * scale;
162 | }
163 | }
164 |
--------------------------------------------------------------------------------
/library/src/main/java/cn/iwgang/simplifyspan/SimplifySpanBuild.java:
--------------------------------------------------------------------------------
1 | package cn.iwgang.simplifyspan;
2 |
3 | import android.graphics.Bitmap;
4 | import android.graphics.Typeface;
5 | import android.media.ThumbnailUtils;
6 | import android.text.SpannableStringBuilder;
7 | import android.text.Spanned;
8 | import android.text.TextPaint;
9 | import android.text.TextUtils;
10 | import android.text.style.AbsoluteSizeSpan;
11 | import android.text.style.BackgroundColorSpan;
12 | import android.text.style.ForegroundColorSpan;
13 | import android.text.style.StrikethroughSpan;
14 | import android.text.style.StyleSpan;
15 | import android.text.style.UnderlineSpan;
16 | import android.widget.TextView;
17 |
18 | import java.util.ArrayList;
19 | import java.util.Arrays;
20 | import java.util.HashMap;
21 | import java.util.List;
22 | import java.util.Map;
23 |
24 | import cn.iwgang.simplifyspan.customspan.CustomAbsoluteSizeSpan;
25 | import cn.iwgang.simplifyspan.customspan.CustomClickableSpan;
26 | import cn.iwgang.simplifyspan.customspan.CustomImageSpan;
27 | import cn.iwgang.simplifyspan.customspan.CustomLabelSpan;
28 | import cn.iwgang.simplifyspan.other.CustomLinkMovementMethod;
29 | import cn.iwgang.simplifyspan.other.OnClickStateChangeListener;
30 | import cn.iwgang.simplifyspan.other.SpecialConvertMode;
31 | import cn.iwgang.simplifyspan.other.SpecialGravity;
32 | import cn.iwgang.simplifyspan.unit.BaseSpecialUnit;
33 | import cn.iwgang.simplifyspan.unit.SpecialClickableUnit;
34 | import cn.iwgang.simplifyspan.unit.SpecialImageUnit;
35 | import cn.iwgang.simplifyspan.unit.SpecialLabelUnit;
36 | import cn.iwgang.simplifyspan.unit.SpecialRawSpanUnit;
37 | import cn.iwgang.simplifyspan.unit.SpecialTextUnit;
38 |
39 | /**
40 | * SimplifySpan Build
41 | * Created by iWgang on 15/12/2.
42 | * https://github.com/iwgang/SimplifySpan
43 | */
44 | public class SimplifySpanBuild {
45 | private List mFinalSpecialUnit;
46 | private List mBeforeSpecialUnit;
47 | private StringBuilder mStringBuilder;
48 | private StringBuilder mBeforeStringBuilder;
49 | private Map mBeforeCacheSpecialClickableUnitMap = new HashMap<>();
50 | private Map mCacheSpecialClickableUnitMap = new HashMap<>();
51 |
52 | private StringBuilder mNormalSizeText;
53 |
54 | public SimplifySpanBuild() {
55 | this(null);
56 | }
57 |
58 | public SimplifySpanBuild(String initializeNormalText) {
59 | init(initializeNormalText);
60 | }
61 |
62 | public SimplifySpanBuild(String initializeNormalText, BaseSpecialUnit... normalSpecialUnits) {
63 | init(initializeNormalText, normalSpecialUnits);
64 | }
65 |
66 | private void init(String initializeNormalText, BaseSpecialUnit... normalSpecialUnits) {
67 | mStringBuilder = new StringBuilder(TextUtils.isEmpty(initializeNormalText) ? "" : initializeNormalText);
68 | mBeforeStringBuilder = new StringBuilder("");
69 | mNormalSizeText = new StringBuilder("");
70 | mFinalSpecialUnit = new ArrayList<>();
71 | mBeforeSpecialUnit = new ArrayList<>();
72 |
73 | if (!TextUtils.isEmpty(initializeNormalText)) {
74 | if (null != normalSpecialUnits && normalSpecialUnits.length > 0) {
75 | buildNormalSpecialUnits(false, 0, initializeNormalText, normalSpecialUnits);
76 | } else {
77 | mNormalSizeText.append(initializeNormalText);
78 | }
79 | }
80 | }
81 |
82 | private void buildNormalSpecialUnits(boolean isAddToBeforeSpecialUnit, int initStartPos, String finalText, BaseSpecialUnit... normalSpecialUnits) {
83 | Map delTextTagMap = new HashMap<>();
84 | List otherSpecialTextStartPos;
85 | for (BaseSpecialUnit st : normalSpecialUnits) {
86 | String specialText = st.getText();
87 |
88 | if (TextUtils.isEmpty(specialText) || !finalText.contains(specialText)) continue;
89 |
90 | int specialTextLength = specialText.length();
91 | switch (st.getConvertMode()) {
92 | case SpecialConvertMode.ONLY_FIRST:
93 | st.setStartPoss(new int[]{initStartPos + finalText.indexOf(specialText)});
94 | break;
95 | case SpecialConvertMode.ONLY_LAST:
96 | st.setStartPoss(new int[]{initStartPos + finalText.lastIndexOf(specialText)});
97 | break;
98 | case SpecialConvertMode.ALL:
99 | int firstSpecialTextStartPos = finalText.indexOf(specialText);
100 | otherSpecialTextStartPos = new ArrayList<>();
101 | otherSpecialTextStartPos.add(firstSpecialTextStartPos);
102 | int previousSpecialTextStartPos = firstSpecialTextStartPos + specialTextLength;
103 | boolean hasNext = true;
104 | while (hasNext) {
105 | int nextSpecialTextStartPos = finalText.indexOf(specialText, previousSpecialTextStartPos);
106 | if (nextSpecialTextStartPos != -1) {
107 | otherSpecialTextStartPos.add(nextSpecialTextStartPos);
108 | previousSpecialTextStartPos = nextSpecialTextStartPos + specialTextLength;
109 | } else {
110 | hasNext = false;
111 | }
112 | }
113 |
114 | int[] startPos = new int[otherSpecialTextStartPos.size()];
115 | for (int i = 0; i < otherSpecialTextStartPos.size(); i++) {
116 | startPos[i] = initStartPos + otherSpecialTextStartPos.get(i);
117 | }
118 | st.setStartPoss(startPos);
119 | break;
120 | }
121 |
122 | int[] startPossTemp = st.getStartPoss();
123 | if (null == startPossTemp || startPossTemp.length == 0) continue;
124 |
125 | // excluding content
126 | if (st instanceof SpecialTextUnit) {
127 | SpecialTextUnit specialTextUnit = (SpecialTextUnit) st;
128 |
129 | if (specialTextUnit.getTextSize() > 0) {
130 | if (startPossTemp.length > 1) {
131 | delTextTagMap.put(specialText, true);
132 | } else {
133 | delTextTagMap.put(specialText, false);
134 | }
135 | }
136 | } else if (st instanceof SpecialImageUnit || st instanceof SpecialLabelUnit) {
137 | if (startPossTemp.length > 1) {
138 | delTextTagMap.put(specialText, true);
139 | } else {
140 | delTextTagMap.put(specialText, false);
141 | }
142 | }
143 | }
144 |
145 | if (!delTextTagMap.isEmpty()) {
146 | for (Map.Entry pm : delTextTagMap.entrySet()) {
147 | if (pm.getValue()) {
148 | // delete all
149 | finalText = finalText.replaceAll(pm.getKey(), "");
150 | } else {
151 | // del first
152 | finalText = finalText.replace(pm.getKey(), "");
153 | }
154 | }
155 | }
156 |
157 |
158 | if (isAddToBeforeSpecialUnit) {
159 | mNormalSizeText.insert(0, finalText);
160 | mBeforeSpecialUnit.addAll(Arrays.asList(normalSpecialUnits));
161 | } else {
162 | mNormalSizeText.append(finalText);
163 | mFinalSpecialUnit.addAll(Arrays.asList(normalSpecialUnits));
164 | }
165 | }
166 |
167 | /**
168 | * append SpecialUnit
169 | *
170 | * @param specialUnit SpecialUnit (Not support convertMode)
171 | * @return SimplifySpanBuild
172 | */
173 | public SimplifySpanBuild append(BaseSpecialUnit specialUnit) {
174 | if (null == specialUnit) return this;
175 |
176 | String specialText = specialUnit.getText();
177 | if (TextUtils.isEmpty(specialText)) return this;
178 |
179 | // process start pos
180 | specialUnit.setStartPoss(new int[]{mStringBuilder.length()});
181 | mStringBuilder.append(specialText);
182 | mFinalSpecialUnit.add(specialUnit);
183 | return this;
184 | }
185 |
186 | /**
187 | * append normal text
188 | *
189 | * @param text normal text
190 | * @return SimplifySpanBuild
191 | */
192 | public SimplifySpanBuild append(String text) {
193 | if (TextUtils.isEmpty(text)) return this;
194 |
195 | mNormalSizeText.append(text);
196 | mStringBuilder.append(text);
197 | return this;
198 | }
199 |
200 | /**
201 | * append SpecialUnit to first (Behind the existing BeforeContent)
202 | *
203 | * @param specialUnit (Not support convertMode)
204 | * @return SimplifySpanBuild
205 | */
206 | public SimplifySpanBuild appendToFirst(BaseSpecialUnit specialUnit) {
207 | if (null == specialUnit) return this;
208 |
209 | String specialText = specialUnit.getText();
210 | if (TextUtils.isEmpty(specialText)) return this;
211 |
212 | // process start pos
213 | int curBeforeFirstPos = mBeforeStringBuilder.length();
214 | specialUnit.setStartPoss(new int[]{curBeforeFirstPos});
215 | mBeforeStringBuilder.insert(curBeforeFirstPos, specialText);
216 | mBeforeSpecialUnit.add(specialUnit);
217 | return this;
218 | }
219 |
220 | /**
221 | * append normal text to first (Behind the existing BeforeContent)
222 | *
223 | * @param text normal text
224 | * @return SimplifySpanBuild
225 | */
226 | public SimplifySpanBuild appendToFirst(String text) {
227 | if (TextUtils.isEmpty(text)) return this;
228 |
229 | mNormalSizeText.append(text);
230 | mBeforeStringBuilder.append(text);
231 | return this;
232 | }
233 |
234 | /**
235 | * append multi clickable SpecialUnit or String
236 | *
237 | * @param specialClickableUnit SpecialClickableUnit
238 | * @param specialUnitOrStrings Unit Or String
239 | * @return SimplifySpanBuild
240 | */
241 | public SimplifySpanBuild appendMultiClickable(SpecialClickableUnit specialClickableUnit, Object... specialUnitOrStrings) {
242 | processMultiClickableSpecialUnit(false, specialClickableUnit, specialUnitOrStrings);
243 | return this;
244 | }
245 |
246 | /**
247 | * append multi clickable SpecialUnit or String to first (Behind the existing BeforeContent)
248 | *
249 | * @param specialClickableUnit SpecialClickableUnit
250 | * @param specialUnitOrStrings Unit Or String
251 | * @return SimplifySpanBuild
252 | */
253 | public SimplifySpanBuild appendMultiClickableToFirst(SpecialClickableUnit specialClickableUnit, Object... specialUnitOrStrings) {
254 | processMultiClickableSpecialUnit(true, specialClickableUnit, specialUnitOrStrings);
255 | return this;
256 | }
257 |
258 | /**
259 | * Build
260 | *
261 | * @return SpannableStringBuilder
262 | */
263 | public SpannableStringBuilder build() {
264 | if (mBeforeStringBuilder.length() > 0) {
265 | mStringBuilder.insert(0, mBeforeStringBuilder);
266 |
267 | // reset SpecialUnit start pos
268 | if (!mFinalSpecialUnit.isEmpty()) {
269 | for (BaseSpecialUnit specialUnit : mFinalSpecialUnit) {
270 | int[] tempStartPoss = specialUnit.getStartPoss();
271 |
272 | if (null == tempStartPoss || tempStartPoss.length == 0) continue;
273 |
274 | for (int i = 0; i < tempStartPoss.length; i++) {
275 | int oldStartPos = tempStartPoss[i];
276 | tempStartPoss[i] = oldStartPos + mBeforeStringBuilder.length();
277 | }
278 | }
279 | }
280 |
281 | // reset Cache SpecialClickableUnit start pos
282 | if (!mCacheSpecialClickableUnitMap.isEmpty()) {
283 | for (Map.Entry cm : mCacheSpecialClickableUnitMap.entrySet()) {
284 | cm.getValue().startPos += mBeforeStringBuilder.length();
285 | }
286 | }
287 | }
288 |
289 | if (!mBeforeCacheSpecialClickableUnitMap.isEmpty()) {
290 | mCacheSpecialClickableUnitMap.putAll(mBeforeCacheSpecialClickableUnitMap);
291 | }
292 |
293 | if (!mBeforeSpecialUnit.isEmpty()) {
294 | mFinalSpecialUnit.addAll(mBeforeSpecialUnit);
295 | }
296 |
297 | if (mStringBuilder.length() == 0) return null;
298 | if (mFinalSpecialUnit.isEmpty())
299 | return new SpannableStringBuilder(mStringBuilder.toString());
300 |
301 | if (mNormalSizeText.length() == 0) mNormalSizeText.append(mStringBuilder);
302 |
303 | String normalSizeText = mNormalSizeText.toString();
304 | SpannableStringBuilder spannableStringBuilder = new SpannableStringBuilder(mStringBuilder);
305 | boolean isInitClickListener = false;
306 | for (BaseSpecialUnit st : mFinalSpecialUnit) {
307 | String specialText = st.getText();
308 | int[] startPoss = st.getStartPoss();
309 |
310 | if (TextUtils.isEmpty(specialText) || null == startPoss || startPoss.length == 0)
311 | continue;
312 |
313 | int specialTextLength = specialText.length();
314 | if (st instanceof SpecialTextUnit) {
315 | // text span
316 | SpecialTextUnit specialTextUnit = (SpecialTextUnit) st;
317 |
318 | final SpecialClickableUnit internalSpecialClickableUnit = specialTextUnit.getSpecialClickableUnit();
319 | if (null != internalSpecialClickableUnit) {
320 | if (internalSpecialClickableUnit.getNormalTextColor() == 0) {
321 | internalSpecialClickableUnit.setNormalTextColor(specialTextUnit.getTextColor());
322 | }
323 | if (internalSpecialClickableUnit.getNormalBgColor() == 0) {
324 | internalSpecialClickableUnit.setNormalBgColor(specialTextUnit.getTextBackgroundColor());
325 | }
326 | }
327 | for (int startPos : startPoss) {
328 | // Set Text Color
329 | if (specialTextUnit.getTextColor() != 0) {
330 | spannableStringBuilder.setSpan(new ForegroundColorSpan(specialTextUnit.getTextColor()), startPos, startPos + specialTextLength, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
331 | }
332 |
333 | // Set Text Background Color
334 | if (specialTextUnit.getTextBackgroundColor() != 0 && null == internalSpecialClickableUnit) {
335 | spannableStringBuilder.setSpan(new BackgroundColorSpan(specialTextUnit.getTextBackgroundColor()), startPos, startPos + specialTextLength, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
336 | }
337 |
338 | // Add Underline
339 | if (specialTextUnit.isShowUnderline()) {
340 | spannableStringBuilder.setSpan(new UnderlineSpan(), startPos, startPos + specialTextLength, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
341 | }
342 |
343 | // Add StrikeThrough
344 | if (specialTextUnit.isShowStrikeThrough()) {
345 | spannableStringBuilder.setSpan(new StrikethroughSpan(), startPos, startPos + specialTextLength, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
346 | }
347 |
348 | // Set Text Bold
349 | if (specialTextUnit.isTextBold()) {
350 | spannableStringBuilder.setSpan(new StyleSpan(Typeface.BOLD), startPos, startPos + specialTextLength, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
351 | }
352 |
353 | // Set Text Italic
354 | if (specialTextUnit.isTextItalic()) {
355 | spannableStringBuilder.setSpan(new StyleSpan(Typeface.ITALIC), startPos, startPos + specialTextLength, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
356 | }
357 |
358 | // Set Text Style
359 | if (specialTextUnit.getTextStyle() != Typeface.NORMAL) {
360 | spannableStringBuilder.setSpan(new StyleSpan(specialTextUnit.getTextStyle()), startPos, startPos + specialTextLength, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
361 | }
362 |
363 | // Set Text Size
364 | if (specialTextUnit.getTextSize() > 0) {
365 | TextPaint curTextPaint = specialTextUnit.getCurTextPaint();
366 | int gravity = specialTextUnit.getGravity();
367 | if (gravity != SpecialGravity.BOTTOM && null != curTextPaint) {
368 | spannableStringBuilder.setSpan(new CustomAbsoluteSizeSpan(normalSizeText, specialTextUnit.getText(), Math.round(specialTextUnit.getTextSize()), curTextPaint, gravity), startPos, startPos + specialTextLength, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
369 | } else {
370 | spannableStringBuilder.setSpan(new AbsoluteSizeSpan(Math.round(specialTextUnit.getTextSize()), true), startPos, startPos + specialTextLength, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
371 | }
372 | }
373 |
374 | // set clickable
375 | if (null != internalSpecialClickableUnit) {
376 | if (!isInitClickListener) {
377 | isInitClickListener = true;
378 | TextView curTextView = internalSpecialClickableUnit.getCurTextView();
379 | if (null != curTextView) {
380 | curTextView.setMovementMethod(CustomLinkMovementMethod.getInstance());
381 | }
382 | }
383 | spannableStringBuilder.setSpan(new CustomClickableSpan(internalSpecialClickableUnit), startPos, startPos + specialTextLength, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
384 | }
385 | }
386 | } else if (st instanceof SpecialImageUnit) {
387 | // image Span
388 | SpecialImageUnit specialImageUnit = (SpecialImageUnit) st;
389 | Bitmap bitmap = specialImageUnit.getBitmap();
390 | int imgWidth = specialImageUnit.getWidth();
391 | int imgHeight = specialImageUnit.getHeight();
392 | if (imgWidth > 0 && imgHeight > 0) {
393 | int bitWidth = bitmap.getWidth();
394 | int bitHeight = bitmap.getHeight();
395 |
396 | if (imgWidth < bitWidth && imgHeight < bitHeight) {
397 | Bitmap newBitmap = ThumbnailUtils.extractThumbnail(bitmap, imgWidth, imgHeight);
398 | if (null != newBitmap) {
399 | bitmap.recycle();
400 | specialImageUnit.setBitmap(newBitmap);
401 | }
402 | }
403 | }
404 |
405 | for (int startPos : startPoss) {
406 | CustomImageSpan curCustomImageSpan = new CustomImageSpan(normalSizeText, specialImageUnit);
407 | spannableStringBuilder.setSpan(curCustomImageSpan, startPos, startPos + specialTextLength, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
408 |
409 | if (specialImageUnit.isClickable()) {
410 | addClickStateChangeListeners(startPos, startPos + specialTextLength, curCustomImageSpan);
411 | }
412 | }
413 | } else if (st instanceof SpecialLabelUnit) {
414 | // label span
415 | SpecialLabelUnit specialLabelUnit = (SpecialLabelUnit) st;
416 | for (int startPos : startPoss) {
417 | CustomLabelSpan curCustomLabelSpan = new CustomLabelSpan(normalSizeText, specialLabelUnit);
418 | spannableStringBuilder.setSpan(curCustomLabelSpan, startPos, startPos + specialTextLength, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
419 |
420 | if (specialLabelUnit.isClickable()) {
421 | addClickStateChangeListeners(startPos, startPos + specialTextLength, curCustomLabelSpan);
422 | }
423 | }
424 | } else if (st instanceof SpecialClickableUnit) {
425 | // clickable span
426 | SpecialClickableUnit specialClickableUnit = (SpecialClickableUnit) st;
427 |
428 | if (!isInitClickListener) {
429 | isInitClickListener = true;
430 | TextView curTextView = specialClickableUnit.getCurTextView();
431 | if (null != curTextView) {
432 | curTextView.setMovementMethod(CustomLinkMovementMethod.getInstance());
433 | }
434 | }
435 |
436 | int startPos = startPoss[0];
437 | spannableStringBuilder.setSpan(new CustomClickableSpan(specialClickableUnit), startPos, startPos + specialTextLength, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
438 | } else if (st instanceof SpecialRawSpanUnit) {
439 | // raw span
440 | SpecialRawSpanUnit specialRawSpanUnit = (SpecialRawSpanUnit) st;
441 |
442 | int startPos = startPoss[0];
443 | spannableStringBuilder.setSpan(specialRawSpanUnit.getSpanObj(), startPos, startPos + specialTextLength, specialRawSpanUnit.getFlags());
444 |
445 | // Temporarily unable to support all
446 | }
447 | }
448 |
449 | return spannableStringBuilder;
450 | }
451 |
452 | private void processMultiClickableSpecialUnit(boolean isToFirst, SpecialClickableUnit specialClickableUnit, Object... specialUnitOrStrings) {
453 | if (null == specialClickableUnit || null == specialUnitOrStrings || specialUnitOrStrings.length == 0)
454 | return;
455 |
456 | StringBuilder tempStrBuild = new StringBuilder();
457 | int baseStartPos;
458 | if (isToFirst) {
459 | baseStartPos = mBeforeStringBuilder.length();
460 | } else {
461 | baseStartPos = mStringBuilder.length();
462 | }
463 | for (Object su : specialUnitOrStrings) {
464 | if (su instanceof SpecialTextUnit) {
465 | SpecialTextUnit specialTextUnit = (SpecialTextUnit) su;
466 |
467 | String specialText = specialTextUnit.getText();
468 | if (TextUtils.isEmpty(specialText)) continue;
469 |
470 | specialTextUnit.setClickableUnit(null);
471 |
472 | // process start pos
473 | specialTextUnit.setStartPoss(new int[]{baseStartPos + tempStrBuild.length()});
474 | if (isToFirst) {
475 | mBeforeSpecialUnit.add(specialTextUnit);
476 | } else {
477 | mFinalSpecialUnit.add(specialTextUnit);
478 | }
479 | tempStrBuild.append(specialText);
480 | } else if (su instanceof SpecialImageUnit) {
481 | SpecialImageUnit specialImageUnit = (SpecialImageUnit) su;
482 |
483 | String specialText = specialImageUnit.getText();
484 | if (TextUtils.isEmpty(specialText)) continue;
485 |
486 | specialImageUnit.setClickable(true);
487 | if (specialImageUnit.getBgColor() == 0 && specialClickableUnit.getNormalBgColor() != 0) {
488 | specialImageUnit.setBgColor(specialClickableUnit.getNormalBgColor());
489 | }
490 |
491 | // process start pos
492 | specialImageUnit.setStartPoss(new int[]{baseStartPos + tempStrBuild.length()});
493 | if (isToFirst) {
494 | mBeforeSpecialUnit.add(specialImageUnit);
495 | } else {
496 | mFinalSpecialUnit.add(specialImageUnit);
497 | }
498 | tempStrBuild.append(specialText);
499 | } else if (su instanceof SpecialLabelUnit) {
500 | SpecialLabelUnit specialLabelUnit = (SpecialLabelUnit) su;
501 |
502 | String specialText = specialLabelUnit.getText();
503 | if (TextUtils.isEmpty(specialText)) continue;
504 |
505 | specialLabelUnit.setClickable(true);
506 | if (specialLabelUnit.getBgColor() == 0 && specialClickableUnit.getNormalBgColor() != 0) {
507 | specialLabelUnit.setBgColor(specialClickableUnit.getNormalBgColor());
508 | }
509 |
510 | // process start pos
511 | specialLabelUnit.setStartPoss(new int[]{baseStartPos + tempStrBuild.length()});
512 | if (isToFirst) {
513 | mBeforeSpecialUnit.add(specialLabelUnit);
514 | } else {
515 | mFinalSpecialUnit.add(specialLabelUnit);
516 | }
517 | tempStrBuild.append(specialText);
518 | } else if (su instanceof String) {
519 | tempStrBuild.append(su.toString());
520 | }
521 | }
522 |
523 | String finalStr = tempStrBuild.toString();
524 | specialClickableUnit.setText(finalStr);
525 | specialClickableUnit.setStartPoss(new int[]{baseStartPos});
526 | PositionInfo positionInfo = new PositionInfo(baseStartPos, finalStr.length());
527 | if (isToFirst) {
528 | mBeforeStringBuilder.insert(baseStartPos, finalStr);
529 | mBeforeSpecialUnit.add(specialClickableUnit);
530 | mBeforeCacheSpecialClickableUnitMap.put(specialClickableUnit, positionInfo);
531 | } else {
532 | mStringBuilder.append(finalStr);
533 | mFinalSpecialUnit.add(specialClickableUnit);
534 | mCacheSpecialClickableUnitMap.put(specialClickableUnit, positionInfo);
535 | }
536 | }
537 |
538 | private void addClickStateChangeListeners(int startPos, int endPos, OnClickStateChangeListener onClickStateChangeListener) {
539 | if (mCacheSpecialClickableUnitMap.isEmpty()) return;
540 |
541 | for (Map.Entry cm : mCacheSpecialClickableUnitMap.entrySet()) {
542 | PositionInfo curPositionInfo = cm.getValue();
543 | int curStartPos = curPositionInfo.startPos;
544 | int curEndPos = curStartPos + curPositionInfo.textLength;
545 | if (startPos >= curStartPos && endPos <= curEndPos) {
546 | SpecialClickableUnit curCacheSpecialClickableUnit = cm.getKey();
547 | List onClickStateChangeListeners = curCacheSpecialClickableUnit.getOnClickStateChangeListeners();
548 | if (null == onClickStateChangeListeners) {
549 | onClickStateChangeListeners = new ArrayList<>();
550 | curCacheSpecialClickableUnit.setOnClickStateChangeListeners(onClickStateChangeListeners);
551 | }
552 | onClickStateChangeListeners.add(onClickStateChangeListener);
553 | break;
554 | }
555 | }
556 | }
557 |
558 | static class PositionInfo {
559 | int startPos;
560 | int textLength;
561 |
562 | public PositionInfo(int startPos, int textLength) {
563 | this.startPos = startPos;
564 | this.textLength = textLength;
565 | }
566 | }
567 |
568 | }
569 |
--------------------------------------------------------------------------------