(context, textViewResourceId, objects) {
28 |
29 |
30 | override fun getView(position: Int, convertViewT: View?, parent: ViewGroup): View {
31 | var convertView = convertViewT
32 | if (convertView == null) {
33 | convertView = View.inflate(context, R.layout.rich_smile_image_row_expression, null)
34 | }
35 |
36 | val imageView = convertView!!.findViewById(R.id.iv_expression) as ImageView
37 |
38 | val filename = getItem(position)
39 | if ("delete_expression" == filename) {
40 | val resId = context.resources.getIdentifier(filename, "drawable", context.packageName)
41 | imageView.setImageResource(resId)
42 | } else {
43 | val resId = SmileUtils.getRedId(filename)
44 | imageView.setImageResource(resId)
45 | }
46 | return convertView
47 | }
48 |
49 | }
50 |
--------------------------------------------------------------------------------
/textUtilsLib/src/main/res/values/attrs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_input.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
13 |
14 |
18 |
19 |
24 |
25 |
32 |
33 |
42 |
43 |
44 |
45 |
46 |
47 |
52 |
--------------------------------------------------------------------------------
/textUtilsLib-kotlin/src/main/res/values/attrs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
--------------------------------------------------------------------------------
/textUtilsLib/src/main/java/com/shuyu/textutillib/adapter/SmileImageExpressionAdapter.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (C) 2013-2014 EaseMob Technologies. All rights reserved.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | * http://www.apache.org/licenses/LICENSE-2.0
8 | * Unless required by applicable law or agreed to in writing, software
9 | * distributed under the License is distributed on an "AS IS" BASIS,
10 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11 | * See the License for the specific language governing permissions and
12 | * limitations under the License.
13 | */
14 | package com.shuyu.textutillib.adapter;
15 |
16 | import android.content.Context;
17 | import android.view.View;
18 | import android.view.ViewGroup;
19 | import android.widget.ArrayAdapter;
20 | import android.widget.ImageView;
21 |
22 | import com.shuyu.textutillib.R;
23 | import com.shuyu.textutillib.SmileUtils;
24 |
25 | import java.util.List;
26 |
27 |
28 | public class SmileImageExpressionAdapter extends ArrayAdapter {
29 |
30 | public SmileImageExpressionAdapter(Context context, int textViewResourceId, List objects) {
31 | super(context, textViewResourceId, objects);
32 | }
33 |
34 |
35 | @Override
36 | public View getView(int position, View convertView, ViewGroup parent) {
37 | if (convertView == null) {
38 | convertView = View.inflate(getContext(), R.layout.rich_smile_image_row_expression, null);
39 | }
40 |
41 | ImageView imageView = (ImageView) convertView.findViewById(R.id.iv_expression);
42 |
43 | String filename = getItem(position);
44 | if ("delete_expression".equals(filename)) {
45 | int resId = getContext().getResources().getIdentifier(filename, "drawable", getContext().getPackageName());
46 | imageView.setImageResource(resId);
47 | } else {
48 | int resId = SmileUtils.getRedId(filename);
49 | imageView.setImageResource(resId);
50 | }
51 | return convertView;
52 | }
53 |
54 | }
55 |
--------------------------------------------------------------------------------
/app/src/main/java/com/example/richtext/utils/ScreenUtils.java:
--------------------------------------------------------------------------------
1 | package com.example.richtext.utils;
2 |
3 | /**
4 | * Created by shuyu on 2016/8/10.
5 | */
6 |
7 |
8 | import android.content.Context;
9 | import android.util.DisplayMetrics;
10 | import android.view.WindowManager;
11 |
12 | public class ScreenUtils {
13 |
14 |
15 | /**
16 | * sp转为px
17 | */
18 | public static int sp2px(Context context, float spValue) {
19 | float fontScale = context.getResources().getDisplayMetrics().density;
20 | return (int) (spValue * fontScale + 0.5f);
21 | }
22 |
23 | /**
24 | * px转为sp
25 | */
26 | public static int px2sp(Context context, float pxValue) {
27 | final float fontScale = context.getResources().getDisplayMetrics().scaledDensity;
28 | return (int) (pxValue / fontScale + 0.5f);
29 | }
30 |
31 | /**
32 | * dip转为PX
33 | */
34 | public static int dip2px(Context context, float dipValue) {
35 | float fontScale = context.getResources().getDisplayMetrics().density;
36 | return (int) (dipValue * fontScale + 0.5f);
37 | }
38 |
39 | /**
40 | * 根据手机的分辨率从 px(像素) 的单位 转成为 dp
41 | */
42 | public static int px2dip(Context context, float pxValue) {
43 | final float scale = context.getResources().getDisplayMetrics().density;
44 | return (int) (pxValue / scale + 0.5f);
45 | }
46 |
47 | /**
48 | * 获取屏幕的宽度px
49 | *
50 | * @param context 上下文
51 | * @return 屏幕宽px
52 | */
53 | public static int getScreenWidth(Context context) {
54 | WindowManager windowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
55 | DisplayMetrics outMetrics = new DisplayMetrics();// 创建了一张白纸
56 | windowManager.getDefaultDisplay().getMetrics(outMetrics);// 给白纸设置宽高
57 | return outMetrics.widthPixels;
58 | }
59 |
60 | /**
61 | * 获取屏幕的高度px
62 | *
63 | * @param context 上下文
64 | * @return 屏幕高px
65 | */
66 | public static int getScreenHeight(Context context) {
67 | WindowManager windowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
68 | DisplayMetrics outMetrics = new DisplayMetrics();// 创建了一张白纸
69 | windowManager.getDefaultDisplay().getMetrics(outMetrics);// 给白纸设置宽高
70 | return outMetrics.heightPixels;
71 | }
72 |
73 | }
--------------------------------------------------------------------------------
/textUtilsLib/src/main/java/com/shuyu/textutillib/RichEditBuilder.java:
--------------------------------------------------------------------------------
1 | package com.shuyu.textutillib;
2 |
3 |
4 | import com.shuyu.textutillib.listener.OnEditTextUtilJumpListener;
5 | import com.shuyu.textutillib.model.TopicModel;
6 | import com.shuyu.textutillib.model.UserModel;
7 |
8 | import java.util.List;
9 |
10 | /**
11 | * 富文本设置 话题、at某人,链接识别
12 | * Created by guoshuyu on 2017/8/18.
13 | */
14 |
15 | public class RichEditBuilder {
16 |
17 | private RichEditText editText;
18 |
19 | private List userModels;
20 |
21 | private List topicModels;
22 |
23 | private OnEditTextUtilJumpListener editTextAtUtilJumpListener;
24 |
25 | /**
26 | * At颜色
27 | */
28 | private String colorTopic = "#0000FF";
29 | /**
30 | * 话题颜色
31 | */
32 | private String colorAtUser = "#f77521";
33 |
34 | /**
35 | * 输入框
36 | */
37 | public RichEditBuilder setEditText(RichEditText editText) {
38 | this.editText = editText;
39 | return this;
40 | }
41 |
42 | /**
43 | * at列表
44 | */
45 | public RichEditBuilder setUserModels(List userModels) {
46 | this.userModels = userModels;
47 | return this;
48 | }
49 |
50 | /**
51 | * 话题列表
52 | */
53 | public RichEditBuilder setTopicModels(List topicModels) {
54 | this.topicModels = topicModels;
55 | return this;
56 | }
57 |
58 | /**
59 | * 输入监听回调
60 | */
61 | public RichEditBuilder setEditTextAtUtilJumpListener(OnEditTextUtilJumpListener editTextAtUtilJumpListener) {
62 | this.editTextAtUtilJumpListener = editTextAtUtilJumpListener;
63 | return this;
64 | }
65 |
66 | /**
67 | * 话题颜色
68 | */
69 | public RichEditBuilder setColorTopic(String colorTopic) {
70 | this.colorTopic = colorTopic;
71 | return this;
72 | }
73 |
74 | /**
75 | * at颜色
76 | */
77 | public RichEditBuilder setColorAtUser(String colorAtUser) {
78 | this.colorAtUser = colorAtUser;
79 | return this;
80 | }
81 |
82 | public RichEditText builder() {
83 | editText.setEditTextAtUtilJumpListener(editTextAtUtilJumpListener);
84 | editText.setModelList(userModels, topicModels);
85 | editText.setColorAtUser(colorAtUser);
86 | editText.setColorTopic(colorTopic);
87 | return editText;
88 | }
89 |
90 | }
91 |
--------------------------------------------------------------------------------
/textUtilsLib-kotlin/src/main/java/com/shuyu/textutillib/RichEditBuilder.kt:
--------------------------------------------------------------------------------
1 | package com.shuyu.textutillib
2 |
3 |
4 | import com.shuyu.textutillib.listener.OnEditTextUtilJumpListener
5 | import com.shuyu.textutillib.model.TopicModel
6 | import com.shuyu.textutillib.model.UserModel
7 |
8 | /**
9 | * 富文本设置 话题、at某人,链接识别
10 | * Created by guoshuyu on 2017/8/18.
11 | */
12 |
13 | class RichEditBuilder {
14 |
15 | private var editText: RichEditText? = null
16 |
17 | private var userModels: MutableList? = null
18 |
19 | private var topicModels: MutableList? = null
20 |
21 | private var editTextAtUtilJumpListener: OnEditTextUtilJumpListener? = null
22 |
23 | /**
24 | * At颜色
25 | */
26 | private var colorTopic = "#0000FF"
27 | /**
28 | * 话题颜色
29 | */
30 | private var colorAtUser = "#f77521"
31 |
32 | /**
33 | * 输入框
34 | */
35 | fun setEditText(editText: RichEditText): RichEditBuilder {
36 | this.editText = editText
37 | return this
38 | }
39 |
40 | /**
41 | * at列表
42 | */
43 | fun setUserModels(userModels: MutableList): RichEditBuilder {
44 | this.userModels = userModels
45 | return this
46 | }
47 |
48 | /**
49 | * 话题列表
50 | */
51 | fun setTopicModels(topicModels: MutableList): RichEditBuilder {
52 | this.topicModels = topicModels
53 | return this
54 | }
55 |
56 | /**
57 | * 输入监听回调
58 | */
59 | fun setEditTextAtUtilJumpListener(editTextAtUtilJumpListener: OnEditTextUtilJumpListener): RichEditBuilder {
60 | this.editTextAtUtilJumpListener = editTextAtUtilJumpListener
61 | return this
62 | }
63 |
64 | /**
65 | * 话题颜色
66 | */
67 | fun setColorTopic(colorTopic: String): RichEditBuilder {
68 | this.colorTopic = colorTopic
69 | return this
70 | }
71 |
72 | /**
73 | * at颜色
74 | */
75 | fun setColorAtUser(colorAtUser: String): RichEditBuilder {
76 | this.colorAtUser = colorAtUser
77 | return this
78 | }
79 |
80 | fun builder(): RichEditText {
81 | editText?.setEditTextAtUtilJumpListener(editTextAtUtilJumpListener)
82 | editText?.setModelList(userModels, topicModels)
83 | editText?.setColorAtUser(colorAtUser)
84 | editText?.setColorTopic(colorTopic)
85 | return editText!!
86 | }
87 |
88 | }
89 |
--------------------------------------------------------------------------------
/gradlew.bat:
--------------------------------------------------------------------------------
1 | @if "%DEBUG%" == "" @echo off
2 | @rem ##########################################################################
3 | @rem
4 | @rem Gradle startup script for Windows
5 | @rem
6 | @rem ##########################################################################
7 |
8 | @rem Set local scope for the variables with windows NT shell
9 | if "%OS%"=="Windows_NT" setlocal
10 |
11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
12 | set DEFAULT_JVM_OPTS=
13 |
14 | set DIRNAME=%~dp0
15 | if "%DIRNAME%" == "" set DIRNAME=.
16 | set APP_BASE_NAME=%~n0
17 | set APP_HOME=%DIRNAME%
18 |
19 | @rem Find java.exe
20 | if defined JAVA_HOME goto findJavaFromJavaHome
21 |
22 | set JAVA_EXE=java.exe
23 | %JAVA_EXE% -version >NUL 2>&1
24 | if "%ERRORLEVEL%" == "0" goto init
25 |
26 | echo.
27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
28 | echo.
29 | echo Please set the JAVA_HOME variable in your environment to match the
30 | echo location of your Java installation.
31 |
32 | goto fail
33 |
34 | :findJavaFromJavaHome
35 | set JAVA_HOME=%JAVA_HOME:"=%
36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
37 |
38 | if exist "%JAVA_EXE%" goto init
39 |
40 | echo.
41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
42 | echo.
43 | echo Please set the JAVA_HOME variable in your environment to match the
44 | echo location of your Java installation.
45 |
46 | goto fail
47 |
48 | :init
49 | @rem Get command-line arguments, handling Windowz variants
50 |
51 | if not "%OS%" == "Windows_NT" goto win9xME_args
52 | if "%@eval[2+2]" == "4" goto 4NT_args
53 |
54 | :win9xME_args
55 | @rem Slurp the command line arguments.
56 | set CMD_LINE_ARGS=
57 | set _SKIP=2
58 |
59 | :win9xME_args_slurp
60 | if "x%~1" == "x" goto execute
61 |
62 | set CMD_LINE_ARGS=%*
63 | goto execute
64 |
65 | :4NT_args
66 | @rem Get arguments from the 4NT Shell from JP Software
67 | set CMD_LINE_ARGS=%$
68 |
69 | :execute
70 | @rem Setup the command line
71 |
72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
73 |
74 | @rem Execute Gradle
75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
76 |
77 | :end
78 | @rem End local scope for the variables with windows NT shell
79 | if "%ERRORLEVEL%"=="0" goto mainEnd
80 |
81 | :fail
82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
83 | rem the _cmd.exe /c_ return code!
84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
85 | exit /b 1
86 |
87 | :mainEnd
88 | if "%OS%"=="Windows_NT" endlocal
89 |
90 | :omega
91 |
--------------------------------------------------------------------------------
/bintray.gradle:
--------------------------------------------------------------------------------
1 | group = PROJ_GROUP
2 | version = PROJ_VERSION
3 | project.archivesBaseName = PROJ_ARTIFACTID
4 |
5 | apply plugin: 'com.jfrog.bintray'
6 | apply plugin: 'com.github.dcendents.android-maven'
7 |
8 | task sourcesJar(type: Jar) {
9 | from android.sourceSets.main.java.srcDirs
10 | classifier = 'sources'
11 | }
12 |
13 | task javadoc(type: Javadoc) {
14 | source = android.sourceSets.main.java.srcDirs
15 | classpath += configurations.compile
16 | classpath += project.files(android.getBootClasspath().join(File.pathSeparator))
17 | }
18 |
19 | task javadocJar(type: Jar, dependsOn: javadoc) {
20 | classifier = 'javadoc'
21 | from javadoc.destinationDir
22 | }
23 |
24 | javadoc {
25 | options{
26 | encoding "UTF-8"
27 | charSet 'UTF-8'
28 | author true
29 | version true
30 | links "http://docs.oracle.com/javase/7/docs/api"
31 | title "$PROJ_NAME $PROJ_VERSION"
32 | }
33 | }
34 |
35 | artifacts {
36 | archives sourcesJar
37 | archives javadocJar
38 | }
39 |
40 | install {
41 | repositories.mavenInstaller {
42 | pom.project {
43 | name PROJ_NAME
44 | description PROJ_DESCRIPTION
45 | url PROJ_WEBSITEURL
46 | inceptionYear '2016'
47 |
48 | packaging 'aar'
49 | groupId PROJ_GROUP
50 | artifactId PROJ_ARTIFACTID
51 | version PROJ_VERSION
52 |
53 | licenses {
54 | license {
55 | name 'The Apache Software License, Version 2.0'
56 | url 'http://www.apache.org/licenses/LICENSE-2.0.txt'
57 | distribution 'repo'
58 | }
59 | }
60 | scm {
61 | connection PROJ_VCSURL
62 | url PROJ_WEBSITEURL
63 |
64 | }
65 | developers {
66 | developer {
67 | id DEVELOPER_ID
68 | name DEVELOPER_NAME
69 | email DEVELOPER_EMAIL
70 | }
71 | }
72 | }
73 | }
74 | }
75 |
76 | bintray {
77 | user = project.hasProperty('bintrayUser') ? project.property('bintrayUser') : project.property('BINTRAY_USER')
78 | key = project.hasProperty('bintrayKey') ? project.property('bintrayKey') : project.property('BINTRAY_KEY')
79 |
80 | configurations = ['archives']
81 |
82 | dryRun = false
83 | publish = true
84 |
85 | pkg {
86 | userOrg = PROJ_USER_ORG //这个是你创建时的organization,必须要有,不然没办法找到指定路径
87 | repo = PROJ_USER_MAVEN
88 | name = PROJ_NAME
89 | licenses = ['Apache-2.0']
90 | vcsUrl = PROJ_VCSURL
91 | websiteUrl = PROJ_WEBSITEURL
92 | issueTrackerUrl = PROJ_ISSUETRACKERURL
93 | publicDownloadNumbers = true
94 | version {
95 | name = PROJ_VERSION
96 | desc = PROJ_DESCRIPTION
97 | vcsTag = PROJ_VERSION
98 |
99 | gpg {
100 | sign = true
101 | }
102 | }
103 | }
104 | }
105 |
--------------------------------------------------------------------------------
/app-kotlin/src/main/java/com/example/shuyu/MVVMActivity.kt:
--------------------------------------------------------------------------------
1 | package com.example.shuyu
2 |
3 | import android.app.Activity
4 | import android.content.Context
5 | import android.content.Intent
6 | import android.databinding.DataBindingUtil
7 | import android.graphics.Color
8 | import android.os.Bundle
9 | import android.view.View
10 | import android.widget.TextView
11 | import android.widget.Toast
12 | import com.example.shuyu.bind.RichEditTextBindingsComponent
13 |
14 | import com.example.shuyu.contract.IMVVMView
15 | import com.example.shuyu.databinding.ActivityMvvmBinding
16 | import com.example.shuyu.model.MVViewModel
17 | import com.shuyu.textutillib.listener.SpanAtUserCallBack
18 | import com.shuyu.textutillib.listener.SpanTopicCallBack
19 | import com.shuyu.textutillib.listener.SpanUrlCallBack
20 | import com.shuyu.textutillib.model.TopicModel
21 | import com.shuyu.textutillib.model.UserModel
22 |
23 | class MVVMActivity : Activity(), IMVVMView {
24 |
25 | private var mvViewModel: MVViewModel? = null
26 |
27 | override val context: Context
28 | get() = this
29 |
30 | /**
31 | * 链接回调
32 | */
33 | override val spanUrlCallBack: SpanUrlCallBack = object : SpanUrlCallBack {
34 | override fun phone(view: View, phone: String) {
35 | Toast.makeText(view.context, phone + " 被点击了", Toast.LENGTH_SHORT).show()
36 | if (view is TextView) {
37 | view.highlightColor = Color.TRANSPARENT
38 | }
39 | }
40 |
41 | override fun url(view: View, url: String) {
42 | Toast.makeText(view.context, url + " 被点击了", Toast.LENGTH_SHORT).show()
43 | if (view is TextView) {
44 | view.highlightColor = Color.TRANSPARENT
45 | }
46 | }
47 | }
48 |
49 | /**
50 | * at回调
51 | */
52 | override val spanAtUserCallBack: SpanAtUserCallBack = object : SpanAtUserCallBack {
53 | override fun onClick(view: View, userModel1: UserModel) {
54 | Toast.makeText(view.context, userModel1.user_name + " 被点击了", Toast.LENGTH_SHORT).show()
55 | if (view is TextView) {
56 | view.highlightColor = Color.TRANSPARENT
57 | }
58 | }
59 | }
60 |
61 | /**
62 | * 话题回调
63 | */
64 | override val spanTopicCallBack: SpanTopicCallBack = object : SpanTopicCallBack {
65 | override fun onClick(view: View, topicModel: TopicModel) {
66 | Toast.makeText(view.context, topicModel.topicName + " 被点击了", Toast.LENGTH_SHORT).show()
67 | if (view is TextView) {
68 | view.highlightColor = Color.TRANSPARENT
69 | }
70 | }
71 | }
72 |
73 | override fun onCreate(savedInstanceState: Bundle?) {
74 | super.onCreate(savedInstanceState)
75 | DataBindingUtil.setDefaultComponent(RichEditTextBindingsComponent())
76 | val binding = DataBindingUtil.setContentView(this, R.layout.activity_mvvm)
77 |
78 | mvViewModel = MVViewModel(this)
79 | binding.viewmodel = mvViewModel
80 |
81 | }
82 |
83 | override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent) {
84 | super.onActivityResult(requestCode, resultCode, data)
85 | mvViewModel?.onActivityResult(requestCode, resultCode, data)
86 | }
87 | }
88 |
--------------------------------------------------------------------------------
/textUtilsLib-kotlin/src/main/java/com/shuyu/textutillib/span/CenteredImageSpan.kt:
--------------------------------------------------------------------------------
1 | package com.shuyu.textutillib.span
2 |
3 | import android.content.Context
4 | import android.graphics.Bitmap
5 | import android.graphics.Canvas
6 | import android.graphics.Paint
7 | import android.graphics.Rect
8 | import android.graphics.drawable.Drawable
9 | import android.net.Uri
10 | import android.support.annotation.DrawableRes
11 | import android.text.style.DynamicDrawableSpan
12 | import android.text.style.ImageSpan
13 |
14 | /**
15 | * 带有居中功能的ImageSpan
16 | * Created by shuyu on 2017/09/19.
17 | */
18 | open class CenteredImageSpan : ImageSpan {
19 |
20 | constructor(drawableRes: Drawable) : super(drawableRes) {}
21 |
22 | constructor(context: Context, b: Bitmap) : super(context, b) {}
23 |
24 | constructor(context: Context, b: Bitmap, verticalAlignment: Int) : super(context, b, verticalAlignment) {}
25 |
26 | constructor(d: Drawable, verticalAlignment: Int) : super(d, verticalAlignment) {}
27 |
28 | constructor(d: Drawable, source: String) : super(d, source) {}
29 |
30 | constructor(d: Drawable, source: String, verticalAlignment: Int) : super(d, source, verticalAlignment) {}
31 |
32 | constructor(context: Context, uri: Uri) : super(context, uri) {}
33 |
34 | constructor(context: Context, uri: Uri, verticalAlignment: Int) : super(context, uri, verticalAlignment) {}
35 |
36 | constructor(context: Context, @DrawableRes resourceId: Int) : super(context, resourceId) {}
37 |
38 | constructor(context: Context, @DrawableRes resourceId: Int, verticalAlignment: Int) : super(context, resourceId, verticalAlignment) {}
39 |
40 | override fun getSize(paint: Paint, text: CharSequence, start: Int, end: Int,
41 | fm: Paint.FontMetricsInt?): Int {
42 |
43 | if (mVerticalAlignment == DynamicDrawableSpan.ALIGN_BASELINE || mVerticalAlignment == DynamicDrawableSpan.ALIGN_BOTTOM) {
44 | return super.getSize(paint, text, start, end, fm)
45 | }
46 |
47 | val d = drawable
48 | val rect = d.bounds
49 | if (fm != null) {
50 | val fmPaint = paint.fontMetricsInt
51 | //获得文字、图片高度
52 | val fontHeight = fmPaint.bottom - fmPaint.top
53 | val drHeight = rect.bottom - rect.top
54 |
55 | val top = drHeight / 2 - fontHeight / 4
56 | val bottom = drHeight / 2 + fontHeight / 4
57 |
58 | fm.ascent = -bottom
59 | fm.top = -bottom
60 | fm.bottom = top
61 | fm.descent = top
62 | }
63 | return rect.right
64 | }
65 |
66 | override fun draw(canvas: Canvas, text: CharSequence, start: Int, end: Int, x: Float, top: Int, y: Int, bottom: Int,
67 | paint: Paint) {
68 |
69 | if (mVerticalAlignment == DynamicDrawableSpan.ALIGN_BASELINE || mVerticalAlignment == DynamicDrawableSpan.ALIGN_BOTTOM) {
70 | super.draw(canvas, text, start, end, x, top, y, bottom, paint)
71 | return
72 | }
73 |
74 | val b = drawable
75 | canvas.save()
76 | var transY = 0
77 | //获得将要显示的文本高度-图片高度除2等居中位置+top(换行情况)
78 | transY = (bottom - top - b.bounds.bottom) / 2 + top
79 | //偏移画布后开始绘制
80 | canvas.translate(x, transY.toFloat())
81 | b.draw(canvas)
82 | canvas.restore()
83 | }
84 |
85 | companion object {
86 |
87 | val ALIGN_CENTER = 2
88 | }
89 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/example/richtext/NewEmojiActivity.java:
--------------------------------------------------------------------------------
1 | package com.example.richtext;
2 |
3 | import android.os.Bundle;
4 | import android.support.v7.app.AppCompatActivity;
5 | import android.view.View;
6 | import android.widget.ImageView;
7 |
8 | import com.shuyu.textutillib.EmojiLayout;
9 | import com.shuyu.textutillib.KeyBoardLockLayout;
10 | import com.shuyu.textutillib.RichEditText;
11 | import com.shuyu.textutillib.SmileUtils;
12 |
13 | import java.util.ArrayList;
14 | import java.util.List;
15 |
16 | import butterknife.BindView;
17 | import butterknife.ButterKnife;
18 | import butterknife.OnClick;
19 |
20 | public class NewEmojiActivity extends AppCompatActivity {
21 |
22 |
23 | @BindView(R.id.edit_text)
24 | RichEditText editText;
25 | @BindView(R.id.keyboard_layout)
26 | KeyBoardLockLayout keyboardLayout;
27 | @BindView(R.id.emoji_layout)
28 | EmojiLayout emojiLayout;
29 | @BindView(R.id.emoji_show_bottom)
30 | ImageView emojiShowBottom;
31 |
32 | @Override
33 | protected void onCreate(Bundle savedInstanceState) {
34 | super.onCreate(savedInstanceState);
35 | List data = new ArrayList<>();
36 | List strings = new ArrayList<>();
37 | data.add(R.drawable.gxx1);
38 | strings.add("[测试1]");
39 | data.add(R.drawable.gxx2);
40 | strings.add("[测试2]");
41 | data.add(R.drawable.gxx3);
42 | strings.add("[测试3]");
43 | data.add(R.drawable.gxx4);
44 | strings.add("[测试4]");
45 | data.add(R.drawable.gxx5);
46 | strings.add("[测试5]");
47 | data.add(R.drawable.gxx6);
48 | strings.add("[测试8]");
49 | /**初始化为自己的**/
50 | SmileUtils.addPatternAll(SmileUtils.getEmoticons(), strings, data);
51 |
52 |
53 | setContentView(R.layout.activity_input);
54 | ButterKnife.bind(this);
55 |
56 | emojiLayout.setEditTextSmile(editText);
57 | keyboardLayout.setBottomView(emojiLayout);
58 | keyboardLayout.setKeyBoardStateListener(new KeyBoardLockLayout.KeyBoardStateListener() {
59 | @Override
60 | public void onState(boolean show) {
61 | if (show)
62 | emojiLayout.showKeyboard();
63 | else
64 | emojiLayout.hideKeyboard();
65 | }
66 | });
67 |
68 | }
69 |
70 | @Override
71 | protected void onDestroy() {
72 | super.onDestroy();
73 | //恢复原本的
74 | initEmoji();
75 | }
76 |
77 | /**
78 | * 处理自己的表情
79 | */
80 | private void initEmoji() {
81 | List data = new ArrayList<>();
82 | List strings = new ArrayList<>();
83 | for (int i = 1; i < 64; i++) {
84 | int resId = getResources().getIdentifier("e" + i, "drawable", getPackageName());
85 | data.add(resId);
86 | strings.add("[e" + i + "]");
87 | }
88 | /**初始化为自己的**/
89 | SmileUtils.addPatternAll(SmileUtils.getEmoticons(), strings, data);
90 | }
91 |
92 | @OnClick({R.id.emoji_show_bottom, R.id.edit_text})
93 | public void onClick(View view) {
94 | switch (view.getId()) {
95 | case R.id.emoji_show_bottom:
96 | if (emojiLayout.getVisibility() != View.VISIBLE) {
97 | keyboardLayout.showBottomViewLockHeight();
98 | } else {
99 | keyboardLayout.hideBottomViewLockHeight();
100 | }
101 | break;
102 | case R.id.edit_text:
103 | keyboardLayout.hideBottomViewLockHeight();
104 | break;
105 | }
106 | }
107 | }
108 |
--------------------------------------------------------------------------------
/textUtilsLib/src/main/java/com/shuyu/textutillib/span/CenteredImageSpan.java:
--------------------------------------------------------------------------------
1 | package com.shuyu.textutillib.span;
2 |
3 | import android.content.Context;
4 | import android.graphics.Bitmap;
5 | import android.graphics.Canvas;
6 | import android.graphics.Paint;
7 | import android.graphics.Rect;
8 | import android.graphics.drawable.Drawable;
9 | import android.net.Uri;
10 | import android.support.annotation.DrawableRes;
11 | import android.text.style.DynamicDrawableSpan;
12 | import android.text.style.ImageSpan;
13 |
14 | /**
15 | * 带有居中功能的ImageSpan
16 | * Created by shuyu on 2017/09/19.
17 | */
18 | public class CenteredImageSpan extends ImageSpan {
19 |
20 | public static final int ALIGN_CENTER = 2;
21 |
22 | public CenteredImageSpan(Drawable drawableRes) {
23 | super(drawableRes);
24 | }
25 |
26 | public CenteredImageSpan(Context context, Bitmap b) {
27 | super(context, b);
28 | }
29 |
30 | public CenteredImageSpan(Context context, Bitmap b, int verticalAlignment) {
31 | super(context, b, verticalAlignment);
32 | }
33 |
34 | public CenteredImageSpan(Drawable d, int verticalAlignment) {
35 | super(d, verticalAlignment);
36 | }
37 |
38 | public CenteredImageSpan(Drawable d, String source) {
39 | super(d, source);
40 | }
41 |
42 | public CenteredImageSpan(Drawable d, String source, int verticalAlignment) {
43 | super(d, source, verticalAlignment);
44 | }
45 |
46 | public CenteredImageSpan(Context context, Uri uri) {
47 | super(context, uri);
48 | }
49 |
50 | public CenteredImageSpan(Context context, Uri uri, int verticalAlignment) {
51 | super(context, uri, verticalAlignment);
52 | }
53 |
54 | public CenteredImageSpan(Context context, @DrawableRes int resourceId) {
55 | super(context, resourceId);
56 | }
57 |
58 | public CenteredImageSpan(Context context, @DrawableRes int resourceId, int verticalAlignment) {
59 | super(context, resourceId, verticalAlignment);
60 | }
61 |
62 | @Override
63 | public int getSize(Paint paint, CharSequence text, int start, int end,
64 | Paint.FontMetricsInt fm) {
65 |
66 | if (mVerticalAlignment == DynamicDrawableSpan.ALIGN_BASELINE || mVerticalAlignment == DynamicDrawableSpan.ALIGN_BOTTOM) {
67 | return super.getSize(paint, text, start, end, fm);
68 | }
69 |
70 | Drawable d = getDrawable();
71 | Rect rect = d.getBounds();
72 | if (fm != null) {
73 | Paint.FontMetricsInt fmPaint = paint.getFontMetricsInt();
74 | //获得文字、图片高度
75 | int fontHeight = fmPaint.bottom - fmPaint.top;
76 | int drHeight = rect.bottom - rect.top;
77 |
78 | int top = drHeight / 2 - fontHeight / 4;
79 | int bottom = drHeight / 2 + fontHeight / 4;
80 |
81 | fm.ascent = -bottom;
82 | fm.top = -bottom;
83 | fm.bottom = top;
84 | fm.descent = top;
85 | }
86 | return rect.right;
87 | }
88 |
89 | @Override
90 | public void draw(Canvas canvas, CharSequence text, int start, int end, float x, int top, int y, int bottom,
91 | Paint paint) {
92 |
93 | if (mVerticalAlignment == DynamicDrawableSpan.ALIGN_BASELINE || mVerticalAlignment == DynamicDrawableSpan.ALIGN_BOTTOM) {
94 | super.draw(canvas, text, start, end, x, top, y, bottom, paint);
95 | return;
96 | }
97 |
98 | Drawable b = getDrawable();
99 | canvas.save();
100 | int transY = 0;
101 | //获得将要显示的文本高度-图片高度除2等居中位置+top(换行情况)
102 | transY = ((bottom - top) - b.getBounds().bottom) / 2 + top;
103 | //偏移画布后开始绘制
104 | canvas.translate(x, transY);
105 | b.draw(canvas);
106 | canvas.restore();
107 | }
108 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/example/richtext/MVVMActivity.java:
--------------------------------------------------------------------------------
1 | package com.example.richtext;
2 |
3 | import android.app.Activity;
4 | import android.content.Context;
5 | import android.content.Intent;
6 | import android.databinding.DataBindingUtil;
7 | import android.graphics.Color;
8 | import android.os.Bundle;
9 | import android.view.View;
10 | import android.widget.TextView;
11 | import android.widget.Toast;
12 |
13 | import com.example.richtext.contract.IMVVMView;
14 | import com.example.richtext.databinding.ActivityMvvmBinding;
15 | import com.example.richtext.model.MVViewModel;
16 | import com.shuyu.textutillib.listener.SpanAtUserCallBack;
17 | import com.shuyu.textutillib.listener.SpanTopicCallBack;
18 | import com.shuyu.textutillib.listener.SpanUrlCallBack;
19 | import com.shuyu.textutillib.model.TopicModel;
20 | import com.shuyu.textutillib.model.UserModel;
21 |
22 | public class MVVMActivity extends Activity implements IMVVMView {
23 |
24 | MVViewModel mvViewModel;
25 |
26 | @Override
27 | protected void onCreate(Bundle savedInstanceState) {
28 | super.onCreate(savedInstanceState);
29 | ActivityMvvmBinding binding = DataBindingUtil.setContentView(this, R.layout.activity_mvvm);
30 |
31 | mvViewModel = new MVViewModel(this);
32 | binding.setViewmodel(mvViewModel);
33 |
34 | }
35 |
36 | @Override
37 | public SpanTopicCallBack getSpanTopicCallBack() {
38 | return spanTopicCallBack;
39 | }
40 |
41 | @Override
42 | public SpanAtUserCallBack getSpanAtUserCallBack() {
43 | return spanAtUserCallBack;
44 | }
45 |
46 | @Override
47 | public SpanUrlCallBack getSpanUrlCallBack() {
48 | return spanUrlCallBack;
49 | }
50 |
51 | @Override
52 | public Context getContext() {
53 | return this;
54 | }
55 |
56 | @Override
57 | protected void onActivityResult(int requestCode, int resultCode, Intent data) {
58 | super.onActivityResult(requestCode, resultCode, data);
59 | mvViewModel.onActivityResult(requestCode, resultCode, data);
60 | }
61 |
62 | /**
63 | * 链接回调
64 | */
65 | private SpanUrlCallBack spanUrlCallBack = new SpanUrlCallBack() {
66 | @Override
67 | public void phone(View view, String phone) {
68 | Toast.makeText(view.getContext(), phone + " 被点击了", Toast.LENGTH_SHORT).show();
69 | if (view instanceof TextView) {
70 | ((TextView) view).setHighlightColor(Color.TRANSPARENT);
71 | }
72 | }
73 |
74 | @Override
75 | public void url(View view, String url) {
76 | Toast.makeText(view.getContext(), url + " 被点击了", Toast.LENGTH_SHORT).show();
77 | if (view instanceof TextView) {
78 | ((TextView) view).setHighlightColor(Color.TRANSPARENT);
79 | }
80 | }
81 | };
82 |
83 | /**
84 | * at回调
85 | */
86 | private SpanAtUserCallBack spanAtUserCallBack = new SpanAtUserCallBack() {
87 | @Override
88 | public void onClick(View view, UserModel userModel1) {
89 | Toast.makeText(view.getContext(), userModel1.getUser_name() + " 被点击了", Toast.LENGTH_SHORT).show();
90 | if (view instanceof TextView) {
91 | ((TextView) view).setHighlightColor(Color.TRANSPARENT);
92 | }
93 | }
94 | };
95 |
96 | /**
97 | * 话题回调
98 | */
99 | private SpanTopicCallBack spanTopicCallBack = new SpanTopicCallBack() {
100 | @Override
101 | public void onClick(View view, TopicModel topicModel) {
102 | Toast.makeText(view.getContext(), topicModel.getTopicName() + " 被点击了", Toast.LENGTH_SHORT).show();
103 | if (view instanceof TextView) {
104 | ((TextView) view).setHighlightColor(Color.TRANSPARENT);
105 | }
106 | }
107 | };
108 | }
109 |
--------------------------------------------------------------------------------
/gradlew:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | ##############################################################################
4 | ##
5 | ## Gradle start up script for UN*X
6 | ##
7 | ##############################################################################
8 |
9 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
10 | DEFAULT_JVM_OPTS=""
11 |
12 | APP_NAME="Gradle"
13 | APP_BASE_NAME=`basename "$0"`
14 |
15 | # Use the maximum available, or set MAX_FD != -1 to use that value.
16 | MAX_FD="maximum"
17 |
18 | warn ( ) {
19 | echo "$*"
20 | }
21 |
22 | die ( ) {
23 | echo
24 | echo "$*"
25 | echo
26 | exit 1
27 | }
28 |
29 | # OS specific support (must be 'true' or 'false').
30 | cygwin=false
31 | msys=false
32 | darwin=false
33 | case "`uname`" in
34 | CYGWIN* )
35 | cygwin=true
36 | ;;
37 | Darwin* )
38 | darwin=true
39 | ;;
40 | MINGW* )
41 | msys=true
42 | ;;
43 | esac
44 |
45 | # Attempt to set APP_HOME
46 | # Resolve links: $0 may be a link
47 | PRG="$0"
48 | # Need this for relative symlinks.
49 | while [ -h "$PRG" ] ; do
50 | ls=`ls -ld "$PRG"`
51 | link=`expr "$ls" : '.*-> \(.*\)$'`
52 | if expr "$link" : '/.*' > /dev/null; then
53 | PRG="$link"
54 | else
55 | PRG=`dirname "$PRG"`"/$link"
56 | fi
57 | done
58 | SAVED="`pwd`"
59 | cd "`dirname \"$PRG\"`/" >/dev/null
60 | APP_HOME="`pwd -P`"
61 | cd "$SAVED" >/dev/null
62 |
63 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
64 |
65 | # Determine the Java command to use to start the JVM.
66 | if [ -n "$JAVA_HOME" ] ; then
67 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
68 | # IBM's JDK on AIX uses strange locations for the executables
69 | JAVACMD="$JAVA_HOME/jre/sh/java"
70 | else
71 | JAVACMD="$JAVA_HOME/bin/java"
72 | fi
73 | if [ ! -x "$JAVACMD" ] ; then
74 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
75 |
76 | Please set the JAVA_HOME variable in your environment to match the
77 | location of your Java installation."
78 | fi
79 | else
80 | JAVACMD="java"
81 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
82 |
83 | Please set the JAVA_HOME variable in your environment to match the
84 | location of your Java installation."
85 | fi
86 |
87 | # Increase the maximum file descriptors if we can.
88 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
89 | MAX_FD_LIMIT=`ulimit -H -n`
90 | if [ $? -eq 0 ] ; then
91 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
92 | MAX_FD="$MAX_FD_LIMIT"
93 | fi
94 | ulimit -n $MAX_FD
95 | if [ $? -ne 0 ] ; then
96 | warn "Could not set maximum file descriptor limit: $MAX_FD"
97 | fi
98 | else
99 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
100 | fi
101 | fi
102 |
103 | # For Darwin, add options to specify how the application appears in the dock
104 | if $darwin; then
105 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
106 | fi
107 |
108 | # For Cygwin, switch paths to Windows format before running java
109 | if $cygwin ; then
110 | APP_HOME=`cygpath --path --mixed "$APP_HOME"`
111 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
112 | JAVACMD=`cygpath --unix "$JAVACMD"`
113 |
114 | # We build the pattern for arguments to be converted via cygpath
115 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
116 | SEP=""
117 | for dir in $ROOTDIRSRAW ; do
118 | ROOTDIRS="$ROOTDIRS$SEP$dir"
119 | SEP="|"
120 | done
121 | OURCYGPATTERN="(^($ROOTDIRS))"
122 | # Add a user-defined pattern to the cygpath arguments
123 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then
124 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
125 | fi
126 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
127 | i=0
128 | for arg in "$@" ; do
129 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
130 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
131 |
132 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
133 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
134 | else
135 | eval `echo args$i`="\"$arg\""
136 | fi
137 | i=$((i+1))
138 | done
139 | case $i in
140 | (0) set -- ;;
141 | (1) set -- "$args0" ;;
142 | (2) set -- "$args0" "$args1" ;;
143 | (3) set -- "$args0" "$args1" "$args2" ;;
144 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
145 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
146 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
147 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
148 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
149 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
150 | esac
151 | fi
152 |
153 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
154 | function splitJvmOpts() {
155 | JVM_OPTS=("$@")
156 | }
157 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
158 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
159 |
160 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
161 |
--------------------------------------------------------------------------------
/app-kotlin/src/main/res/layout/activity_main.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
20 |
21 |
29 |
30 |
38 |
39 |
45 |
46 |
55 |
56 |
57 |
58 |
65 |
66 |
74 |
75 |
76 |
83 |
84 |
88 |
89 |
93 |
94 |
102 |
103 |
110 |
111 |
119 |
120 |
129 |
130 |
131 |
132 |
133 |
145 |
146 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 | 
4 |
5 | 鸿蒙第三方迁移版本:https://gitee.com/ts_ohos/richtext_ohos
6 |
7 |
8 | ## 支持类似微博的文本效果,表情、@某人、话题、url链接等。DEMO同时演示了MVVM模式的使用。
9 |
10 | 状态 | 功能
11 | -------- | ---
12 | **支持**|**表情**
13 | **支持**|**#话题**
14 | **支持**|**@某人**
15 | **支持**|**url与数字(可配置)**
16 | **支持**|**点击效果**
17 | **支持**|**自定义span效果**
18 | **支持**|**表情、#话题与@某人编辑时整块删除**
19 | **支持**|**表情、#话题与@某人编辑时选择复制限制整块选择。**
20 | **支持**|**表情大小设置、居中显示**
21 | **支持**|**MVVM(DataBing)。**
22 | **支持**|**kotlin(demo与lib)**
23 |
24 |
25 | ----------------------------------
26 |
27 | [](https://jitpack.io/#CarGuo/RickText)
28 | [](https://travis-ci.org/CarGuo/RickText)
29 |
30 | ## 依赖方式
31 |
32 | ### 在project下的build.gradle添加
33 | ```
34 | allprojects {
35 | repositories {
36 | ...
37 | maven { url 'https://jitpack.io' }
38 | }
39 | }
40 | ```
41 |
42 | ### 在module下的build.gradle添加
43 |
44 | ```
45 | dependencies {
46 | compile 'com.github.CarGuo.RickText:textUtilsLib:v2.1.5'
47 | }
48 |
49 | ```
50 |
51 | ### kotlin版本的依赖
52 | ```
53 | dependencies {
54 | compile 'com.github.CarGuo.RickText:textUtilsLib-kotlin:v2.1.5'
55 | }
56 | ```
57 |
58 | ----------------------------------
59 |
60 | ## DEMO效果图
61 |
62 |
63 |
64 |
65 | ### [旧版简书解析](http://www.jianshu.com/p/cd9e197a5c04)
66 |
67 | ### [旧版README](https://github.com/CarGuo/RickText/blob/master/OLD_README.md)
68 |
69 | ### QQ群,有兴趣的可以进来,群里视频项目的人居多,平时多吹水吐槽:174815284 。
70 |
71 | 
72 |
73 | ----------------------------------
74 |
75 | ## 使用方式参考demo
76 |
77 | ### 1、文本模式
78 |
79 | #### 1.1、RichTextView
80 | ```
81 | richTextView = (RichTextView) findViewById(R.id.rich_text_2);
82 | //直接使用RichTextView
83 | richTextView.setAtColor(Color.RED);
84 | richTextView.setTopicColor(Color.BLUE);
85 | richTextView.setLinkColor(Color.YELLOW);
86 | richTextView.setNeedNumberShow(true);
87 | richTextView.setNeedUrlShow(true);
88 | richTextView.setSpanAtUserCallBackListener(spanAtUserCallBack);
89 | richTextView.setSpanTopicCallBackListener(spanTopicCallBack);
90 | richTextView.setSpanUrlCallBackListener(spanUrlCallBack);
91 | //所有配置完成后才设置text
92 | richTextView.setRichText(content, nameList, topicModels);
93 | ```
94 |
95 | #### 1.2、普通TextView
96 |
97 | ```
98 | //url点击回调
99 | SpanUrlCallBack spanUrlCallBack = new SpanUrlCallBack() {
100 | @Override
101 | public void phone(String phone) {
102 | Toast.makeText(MainActivity.this, phone + " 被点击了", Toast.LENGTH_SHORT).show();
103 | richText.setHighlightColor(Color.TRANSPARENT);
104 | }
105 |
106 | @Override
107 | public void url(String url) {
108 | Toast.makeText(MainActivity.this, url + " 被点击了", Toast.LENGTH_SHORT).show();
109 | richText.setHighlightColor(Color.TRANSPARENT);
110 | }
111 | };
112 |
113 | //@点击回调
114 | SpanAtUserCallBack spanAtUserCallBack = new SpanAtUserCallBack() {
115 | @Override
116 | public void onClick(UserModel userModel1) {
117 | Toast.makeText(MainActivity.this, userModel1.getUser_name() + " 被点击了", Toast.LENGTH_SHORT).show();
118 | richText.setHighlightColor(Color.TRANSPARENT);
119 | }
120 | };
121 |
122 | //话题点击回调
123 | SpanTopicCallBack spanTopicCallBack = new SpanTopicCallBack() {
124 | @Override
125 | public void onClick(TopicModel topicModel) {
126 | Toast.makeText(MainActivity.this, topicModel.getTopicName() + " 被点击了", Toast.LENGTH_SHORT).show();
127 | richText.setHighlightColor(Color.TRANSPARENT);
128 | }
129 | };
130 |
131 | //配置TextView显示文本
132 | RichTextBuilder richTextBuilder = new RichTextBuilder(this);
133 | richTextBuilder.setContent(content)
134 | .setAtColor(Color.RED)
135 | .setLinkColor(Color.BLUE)
136 | .setTopicColor(Color.YELLOW)
137 | .setListUser(nameList)
138 | .setListTopic(topicModels)
139 | .setTextView(richText)
140 | .setSpanAtUserCallBack(spanAtUserCallBack)
141 | .setSpanUrlCallBack(spanUrlCallBack)
142 | .setSpanTopicCallBack(spanTopicCallBack)
143 | .build();
144 |
145 | ```
146 |
147 | ### 2、编辑模式(RichEditText)
148 |
149 | ```
150 | richEditText = (RichEditText) findViewById(R.id.emoji_edit_text);
151 | emojiLayout.setEditTextSmile(richEditText);
152 | RichEditBuilder richEditBuilder = new RichEditBuilder();
153 | richEditBuilder.setEditText(richEditText)
154 | .setTopicModels(topicModels)
155 | .setUserModels(nameList)
156 | .setColorAtUser("#FF00C0")
157 | .setColorTopic("#F0F0C0")
158 | .setEditTextAtUtilJumpListener(new OnEditTextUtilJumpListener() {
159 | @Override
160 | public void notifyAt() {
161 | JumpUtil.goToUserList(MainActivity.this, MainActivity.REQUEST_USER_CODE_INPUT);
162 | }
163 |
164 | @Override
165 | public void notifyTopic() {
166 | JumpUtil.goToTopicList(MainActivity.this, MainActivity.REQUEST_TOPIC_CODE_INPUT);
167 | }
168 | })
169 | .builder();
170 | ```
171 |
172 | ----------------------------------
173 |
174 | ## 版本更新
175 |
176 | #### v2.1.5 (2018-01-23)
177 | * 修复了ellipsize问题
178 |
179 | #### v2.1.4 (2017-12-05)
180 | * 修复了一个特殊场景下让人🥚疼的问题。
181 | * 修复某些情况下删除按键导致的问题
182 |
183 | #### v2.1.2 (2017-10-20)
184 | * 优化一些问题
185 | * 增加kotlin lib 与 kotlin demo
186 |
187 | #### v2.1.1 (2017-09-19)
188 | * 增加表情居中功能的支持。
189 |
190 | #### v2.1.0 (2017-09-17)
191 | * 修复了@和#输入存在的问题。
192 |
193 |
194 | ### [历史版本](https://github.com/CarGuo/RickText/blob/master/HISTORY_README.md)
195 |
196 |
197 | ## License
198 |
199 | ```
200 | MIT
201 | ```
202 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_main.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
10 |
22 |
23 |
31 |
32 |
40 |
41 |
47 |
48 |
57 |
58 |
59 |
60 |
67 |
68 |
76 |
77 |
78 |
85 |
86 |
90 |
91 |
95 |
96 |
104 |
105 |
112 |
113 |
121 |
122 |
131 |
132 |
133 |
134 |
135 |
136 |
148 |
149 |
--------------------------------------------------------------------------------
/app-kotlin/src/main/res/layout/activity_mvvm.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
7 |
8 |
9 |
10 |
13 |
14 |
15 |
19 |
20 |
29 |
30 |
38 |
39 |
40 |
62 |
63 |
90 |
91 |
92 |
97 |
98 |
106 |
107 |
116 |
117 |
130 |
131 |
142 |
143 |
144 |
145 |
146 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_mvvm.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
7 |
8 |
9 |
10 |
13 |
14 |
15 |
19 |
20 |
29 |
30 |
38 |
39 |
40 |
63 |
64 |
91 |
92 |
93 |
98 |
99 |
107 |
108 |
117 |
118 |
131 |
132 |
143 |
144 |
145 |
146 |
147 |
--------------------------------------------------------------------------------
/textUtilsLib-kotlin/src/main/java/com/shuyu/textutillib/RichTextBuilder.kt:
--------------------------------------------------------------------------------
1 | package com.shuyu.textutillib
2 |
3 | import android.content.Context
4 | import android.graphics.Color
5 | import android.text.Spannable
6 | import android.text.method.MovementMethod
7 | import android.text.style.DynamicDrawableSpan
8 | import android.widget.TextView
9 |
10 | import com.shuyu.textutillib.listener.ITextViewShow
11 | import com.shuyu.textutillib.listener.SpanAtUserCallBack
12 | import com.shuyu.textutillib.listener.SpanCreateListener
13 | import com.shuyu.textutillib.listener.SpanTopicCallBack
14 | import com.shuyu.textutillib.listener.SpanUrlCallBack
15 | import com.shuyu.textutillib.model.TopicModel
16 | import com.shuyu.textutillib.model.UserModel
17 | import com.shuyu.textutillib.span.ClickAtUserSpan
18 | import com.shuyu.textutillib.span.ClickTopicSpan
19 | import com.shuyu.textutillib.span.LinkSpan
20 |
21 | /**
22 | * 富文本设置 话题、at某人,链接识别
23 | * Created by guoshuyu on 2017/8/17.
24 | */
25 |
26 | class RichTextBuilder(private val context: Context?) {
27 | private var content = ""
28 | private var listUser: List? = ArrayList()
29 | private var listTopic: List? = ArrayList()
30 | private var textView: TextView? = null
31 | private var spanAtUserCallBack: SpanAtUserCallBack? = null
32 | private var spanUrlCallBack: SpanUrlCallBack? = null
33 | private var spanTopicCallBack: SpanTopicCallBack? = null
34 | private var spanCreateListener: SpanCreateListener? = null
35 | private var atColor = Color.BLUE
36 | private var topicColor = Color.BLUE
37 | private var linkColor = Color.BLUE
38 | private var emojiSize = 0
39 | private var verticalAlignment = DynamicDrawableSpan.ALIGN_BOTTOM
40 | private var needNum = false
41 | private var needUrl = false
42 |
43 | /**
44 | * 文本内容
45 | */
46 | fun setContent(content: String?): RichTextBuilder {
47 | if (content != null) {
48 | this.content = content
49 | }
50 | return this
51 | }
52 |
53 | /**
54 | * at某人的list
55 | */
56 | fun setListUser(listUser: List?): RichTextBuilder {
57 | this.listUser = listUser
58 | return this
59 | }
60 |
61 | /**
62 | * 话题list
63 | */
64 | fun setListTopic(listTopic: List?): RichTextBuilder {
65 | this.listTopic = listTopic
66 | return this
67 | }
68 |
69 | /**
70 | * 显示文本view
71 | */
72 | fun setTextView(textView: TextView): RichTextBuilder {
73 | this.textView = textView
74 | return this
75 | }
76 |
77 | /**
78 | * at某人显示颜色
79 | */
80 | fun setAtColor(atColor: Int): RichTextBuilder {
81 | this.atColor = atColor
82 | return this
83 | }
84 |
85 | /**
86 | * 话题显示颜色
87 | */
88 | fun setTopicColor(topicColor: Int): RichTextBuilder {
89 | this.topicColor = topicColor
90 | return this
91 | }
92 |
93 | /**
94 | * 链接显示颜色
95 | */
96 | fun setLinkColor(linkColor: Int): RichTextBuilder {
97 | this.linkColor = linkColor
98 | return this
99 | }
100 |
101 | /**
102 | * 是否需要识别电话
103 | */
104 | fun setNeedNum(needNum: Boolean): RichTextBuilder {
105 | this.needNum = needNum
106 | return this
107 | }
108 |
109 | fun setNeedUrl(needUrl: Boolean): RichTextBuilder {
110 | this.needUrl = needUrl
111 | return this
112 | }
113 |
114 | /**
115 | * at某人点击回调
116 | */
117 | fun setSpanAtUserCallBack(spanAtUserCallBack: SpanAtUserCallBack): RichTextBuilder {
118 | this.spanAtUserCallBack = spanAtUserCallBack
119 | return this
120 | }
121 |
122 | /**
123 | * url点击回调
124 | */
125 | fun setSpanUrlCallBack(spanUrlCallBack: SpanUrlCallBack): RichTextBuilder {
126 | this.spanUrlCallBack = spanUrlCallBack
127 | return this
128 | }
129 |
130 | /**
131 | * 话题点击回调
132 | */
133 | fun setSpanTopicCallBack(spanTopicCallBack: SpanTopicCallBack): RichTextBuilder {
134 | this.spanTopicCallBack = spanTopicCallBack
135 | return this
136 | }
137 |
138 | /**
139 | * emoji大小,不设置默认图片大小
140 | */
141 | fun setEmojiSize(emojiSize: Int): RichTextBuilder {
142 | this.emojiSize = emojiSize
143 | return this
144 | }
145 |
146 | /**
147 | * emoji垂直
148 | */
149 | fun setVerticalAlignment(verticalAlignment: Int): RichTextBuilder {
150 | this.verticalAlignment = verticalAlignment
151 | return this
152 | }
153 |
154 | /**
155 | * 自定义span回调,如果不需要可不设置
156 | */
157 | fun setSpanCreateListener(spanCreateListener: SpanCreateListener?): RichTextBuilder {
158 | this.spanCreateListener = spanCreateListener
159 | return this
160 | }
161 |
162 | fun buildSpan(iTextViewShow: ITextViewShow): Spannable {
163 | if (context == null) {
164 | throw IllegalStateException("context could not be null.")
165 | }
166 |
167 | return TextCommonUtils.getAllSpanText(
168 | context,
169 | content,
170 | listUser,
171 | listTopic,
172 | iTextViewShow,
173 | atColor,
174 | linkColor,
175 | topicColor,
176 | needNum,
177 | needUrl,
178 | spanAtUserCallBack!!,
179 | spanUrlCallBack!!,
180 | spanTopicCallBack)
181 | }
182 |
183 |
184 | fun build() {
185 |
186 | if (context == null) {
187 | throw IllegalStateException("context could not be null.")
188 | }
189 |
190 | if (textView == null) {
191 | throw IllegalStateException("textView could not be null.")
192 | }
193 |
194 | val iTextViewShow = object : ITextViewShow {
195 |
196 | override var text: CharSequence
197 | get() = textView!!.text
198 | set(charSequence) {
199 | textView!!.text = charSequence
200 | }
201 |
202 | override fun setMovementMethod(movementMethod: MovementMethod) {
203 | textView?.movementMethod = movementMethod
204 | }
205 |
206 | override fun setAutoLinkMask(flag: Int) {
207 | textView?.autoLinkMask = flag
208 | }
209 |
210 |
211 | override fun getCustomClickAtUserSpan(context: Context, userModel: UserModel, color: Int, spanClickCallBack: SpanAtUserCallBack): ClickAtUserSpan? =
212 | spanCreateListener?.getCustomClickAtUserSpan(context, userModel, color, spanClickCallBack)
213 |
214 | override fun getCustomClickTopicSpan(context: Context, topicModel: TopicModel, color: Int, spanTopicCallBack: SpanTopicCallBack): ClickTopicSpan? =
215 | spanCreateListener?.getCustomClickTopicSpan(context, topicModel, color, spanTopicCallBack)
216 |
217 | override fun getCustomLinkSpan(context: Context, url: String, color: Int, spanUrlCallBack: SpanUrlCallBack): LinkSpan? =
218 | spanCreateListener?.getCustomLinkSpan(context, url, color, spanUrlCallBack)
219 |
220 | override fun emojiSize(): Int = emojiSize
221 |
222 | override fun verticalAlignment(): Int = verticalAlignment
223 | }
224 |
225 | val spannable = TextCommonUtils.getAllSpanText(
226 | context,
227 | content,
228 | listUser,
229 | listTopic,
230 | iTextViewShow,
231 | atColor,
232 | linkColor,
233 | topicColor,
234 | needNum,
235 | needUrl,
236 | spanAtUserCallBack!!,
237 | spanUrlCallBack!!,
238 | spanTopicCallBack)
239 | textView?.text = spannable
240 | }
241 | }
242 |
--------------------------------------------------------------------------------