├── LICENSE ├── README.md ├── RichEditText ├── RichEditText-RichEditText.iml ├── build.gradle ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── com │ │ └── eastearly │ │ └── richedittextview │ │ └── ApplicationTest.java │ ├── main │ ├── AndroidManifest.xml │ ├── java │ │ └── com │ │ │ └── eastearly │ │ │ └── richedittextview │ │ │ ├── ColorPickerDialog.java │ │ │ ├── EmailHtmlUtil.java │ │ │ ├── HtmlAbsoluteSizeSpan.java │ │ │ ├── IEditText.java │ │ │ ├── IRichEditText.java │ │ │ └── RichEditText.java │ └── res │ │ ├── drawable-hdpi │ │ ├── ic_format_color_text_black_24dp.png │ │ ├── ic_keyboard_arrow_left_black_24dp.png │ │ └── ic_keyboard_arrow_right_black_24dp.png │ │ ├── drawable-mdpi │ │ ├── ic_format_color_text_black_24dp.png │ │ ├── ic_keyboard_arrow_left_black_24dp.png │ │ └── ic_keyboard_arrow_right_black_24dp.png │ │ ├── drawable-xhdpi │ │ ├── ic_bold.png │ │ ├── ic_format_color_text_black_24dp.png │ │ ├── ic_hyperlink.png │ │ ├── ic_italic.png │ │ ├── ic_keyboard_arrow_left_black_24dp.png │ │ ├── ic_keyboard_arrow_right_black_24dp.png │ │ ├── ic_menu_done_holo_light.png │ │ ├── ic_menu_send_holo_light.png │ │ ├── ic_strikethrough.png │ │ ├── ic_textcolor.png │ │ ├── ic_textsize.png │ │ └── ic_underline.png │ │ ├── drawable-xxhdpi │ │ ├── ic_format_color_text_black_24dp.png │ │ ├── ic_keyboard_arrow_left_black_24dp.png │ │ └── ic_keyboard_arrow_right_black_24dp.png │ │ ├── drawable-xxxhdpi │ │ ├── ic_format_color_text_black_24dp.png │ │ ├── ic_keyboard_arrow_left_black_24dp.png │ │ └── ic_keyboard_arrow_right_black_24dp.png │ │ ├── drawable │ │ └── arrow.xml │ │ ├── layout │ │ └── htmloptions.xml │ │ └── values │ │ ├── attr_richedit_text.xml │ │ ├── colors.xml │ │ ├── strings.xml │ │ └── styles.xml │ └── test │ └── java │ └── com │ └── eastearly │ └── richedittextview │ └── ExampleUnitTest.java ├── build.gradle ├── demo2 ├── build.gradle └── src │ ├── androidTest │ └── java │ │ └── com │ │ └── eastearly │ │ └── demo2 │ │ └── ApplicationTest.java │ ├── main │ ├── AndroidManifest.xml │ ├── java │ │ └── com │ │ │ └── eastearly │ │ │ └── demo2 │ │ │ └── MainActivity.java │ └── res │ │ ├── layout │ │ ├── activity_main.xml │ │ └── content_main.xml │ │ ├── menu │ │ └── menu_main.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-v21 │ │ └── styles.xml │ │ ├── values-w820dp │ │ └── dimens.xml │ │ └── values │ │ ├── colors.xml │ │ ├── dimens.xml │ │ ├── strings.xml │ │ └── styles.xml │ └── test │ └── java │ └── com │ └── eastearly │ └── demo2 │ └── ExampleUnitTest.java ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── raw └── screen-shot │ ├── addlink.png │ └── changefontcolor.png ├── richedittextview ├── .gitignore └── richedittextview.iml └── settings.gradle /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "{}" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright {yyyy} {name of copyright owner} 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | 203 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # RichEditText 2 | A rich edit text view on Android to replace native EditText view,it let user show/input more informational text. 3 | The library support editting rich text on UI, like bold, different font color, add link for the selection and so on. 4 | 5 | Comparing to a famous github project [cwac-richedit](https://github.com/commonsguy/cwac-richedit),which published by master 'commonsguy', this library is mainly 6 | focusing on editing process, for example, when you underline some segments but after that strike through the later part, how the former style is kept and get well with the next style impacting later words. 7 | 8 | 9 | 10 | 11 | 12 | #Usage 13 | 14 | ### JCentor 15 | 16 | This project has been pushed to JCentor [here](https://bintray.com/xiaodong666/maven/dach-richedit-android/view#) , in 'aar'. To import the library in gradle: 17 | 18 | `repositories { 19 | jcenter() 20 | }` 21 | 22 | with specified version: 23 | 24 | `compile 'com.earlyeast.android:RichEditText:0.6.0'` 25 | 26 | ### Supported Effects 27 | 28 | At the time of this writing, here are all effects: 29 | 30 | - `BOLD` 31 | - `ITALIC` 32 | - `UNDERLINE` 33 | - `STRIKETHROUGH` 34 | - `URL` 35 | - `FOREGROUND` (font color) 36 | - `BACKGROUND` 37 | - `FONTSIZE` 38 | 39 | A folding-able toolbar is supplied to edit/switch the selection of text with different font styles, you can click the 'up/down' arrow to shrink/pop-down the tool bar. 40 | 41 | ### Editing 42 | 43 | - Status 44 | The buttons on toolbar shows styles of current text segment with grey/normal. For example, the a word where cursor at is not bold,then the 'Bold' icon is gray. 45 | - Edit 46 | To edit font style,you need to select text first, then click relative icon to change its font style.For example,to make two words bold, first make the two words selected, 47 | then click 'bold' to set that, if they are changed un-bold, click the 'bold' again to make the whole selection part bold. 48 | 49 | ### Demo 50 | 51 | Demo 'demo2' is based on Android module template 'Blank Activity' on Android Studio,showing all features of this library. 52 | The screen shots showing like [addlink] (https://raw.githubusercontent.com/eastearly/RichEditText/master/raw/screen-shot/addlink.png) and [changefontcolor](https://raw.githubusercontent.com/eastearly/RichEditText/master/raw/screen-shot/changefontcolor.png). 53 | 54 | ![demo](https://raw.githubusercontent.com/eastearly/RichEditText/master/raw/screen-shot/addlink.png) 55 | 56 | 57 | 58 | ### Bugs found 59 | 60 | 1.When a segment was strike through, can not revoke by select the segment and click icon 'StrikeThrough', there is something not correct with the range logic. -------------------------------------------------------------------------------- /RichEditText/RichEditText-RichEditText.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 8 | 9 | 10 | 11 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | -------------------------------------------------------------------------------- /RichEditText/build.gradle: -------------------------------------------------------------------------------- 1 | /*** 2 | Copyright (c) 2015 earlyeast , xiaodong666@gmail.com 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); you may 5 | not use this file except in compliance with the License. You may obtain 6 | 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 | 15 | apply plugin: 'com.android.library' 16 | ext { 17 | bintrayRepo = 'maven' 18 | bintrayName = 'dach-richedit-android' 19 | 20 | publishedGroupId = 'com.earlyeast.android' 21 | libraryName = 'dach-rich-edittext' 22 | artifact = 'RichEditText' 23 | 24 | libraryDescription = '' 25 | 26 | siteUrl = 'https://github.com/eastearly/RichEditText' 27 | gitUrl = 'https://github.com/eastearly/RichEditText.git' 28 | 29 | libraryVersion = '0.6.0' 30 | 31 | developerId = 'earlyeast' 32 | developerName = 'Chen Xiaodong' 33 | developerEmail = 'xiaodong666@gmail.com' 34 | 35 | licenseName = 'The Apache Software License, Version 2.0' 36 | licenseUrl = 'http://www.apache.org/licenses/LICENSE-2.0.txt' 37 | allLicenses = ["Apache-2.0"] 38 | } 39 | android { 40 | compileSdkVersion 23 41 | buildToolsVersion "23.0.1" 42 | 43 | defaultConfig { 44 | minSdkVersion 16 45 | targetSdkVersion 20 46 | versionCode 1 47 | versionName "1.0" 48 | } 49 | buildTypes { 50 | release { 51 | minifyEnabled false 52 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 53 | } 54 | } 55 | } 56 | 57 | dependencies { 58 | compile fileTree(dir: 'libs', include: ['*.jar']) 59 | testCompile 'junit:junit:4.12' 60 | compile 'com.android.support:appcompat-v7:22.2.1' 61 | 62 | } 63 | 64 | apply from: '../installv1.gradle' 65 | apply from: '../bintrayv1.gradle' 66 | -------------------------------------------------------------------------------- /RichEditText/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 /Applications/adt-bundle-mac/sdk/tools/proguard/proguard-android.txt 4 | # You can edit the include path and order by changing the proguardFiles 5 | # directive in build.gradle. 6 | # 7 | # For more details, see 8 | # http://developer.android.com/guide/developing/tools/proguard.html 9 | 10 | # Add any project specific keep options here: 11 | 12 | # If your project uses WebView with JS, uncomment the following 13 | # and specify the fully qualified class name to the JavaScript interface 14 | # class: 15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 16 | # public *; 17 | #} 18 | -------------------------------------------------------------------------------- /RichEditText/src/androidTest/java/com/eastearly/richedittextview/ApplicationTest.java: -------------------------------------------------------------------------------- 1 | package com.eastearly.richedittextview; 2 | 3 | import android.app.Application; 4 | import android.test.ApplicationTestCase; 5 | 6 | /** 7 | * Testing Fundamentals 8 | */ 9 | public class ApplicationTest extends ApplicationTestCase { 10 | public ApplicationTest() { 11 | super(Application.class); 12 | } 13 | } -------------------------------------------------------------------------------- /RichEditText/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /RichEditText/src/main/java/com/eastearly/richedittextview/ColorPickerDialog.java: -------------------------------------------------------------------------------- 1 | package com.eastearly.richedittextview; 2 | 3 | /* 4 | * Copyright (C) 2007 The Android Open Source Project 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | 19 | import android.app.Dialog; 20 | import android.content.Context; 21 | import android.graphics.*; 22 | import android.os.Bundle; 23 | import android.view.MotionEvent; 24 | import android.view.View; 25 | 26 | public class ColorPickerDialog extends Dialog { 27 | 28 | public interface OnColorChangedListener { 29 | void colorChanged(int color); 30 | } 31 | 32 | private OnColorChangedListener mListener; 33 | private int mInitialColor; 34 | 35 | private static class ColorPickerView extends View { 36 | private Paint mPaint; 37 | private Paint mCenterPaint; 38 | private final int[] mColors; 39 | private OnColorChangedListener mListener; 40 | 41 | ColorPickerView(Context c, OnColorChangedListener l, int color) { 42 | super(c); 43 | mListener = l; 44 | mColors = new int[] { 45 | 0xFFFF0000, 0xFFFF00FF, 0xFF0000FF, 0xFF00FFFF, 0xFF00FF00, 46 | 0xFFFFFF00, 0xFFFF0000 47 | }; 48 | Shader s = new SweepGradient(0, 0, mColors, null); 49 | 50 | mPaint = new Paint(Paint.ANTI_ALIAS_FLAG); 51 | mPaint.setShader(s); 52 | mPaint.setStyle(Paint.Style.STROKE); 53 | mPaint.setStrokeWidth(32); 54 | 55 | mCenterPaint = new Paint(Paint.ANTI_ALIAS_FLAG); 56 | mCenterPaint.setColor(color); 57 | mCenterPaint.setStrokeWidth(5); 58 | } 59 | 60 | private boolean mTrackingCenter; 61 | private boolean mHighlightCenter; 62 | 63 | @Override 64 | protected void onDraw(Canvas canvas) { 65 | float r = CENTER_X - mPaint.getStrokeWidth()*0.5f; 66 | 67 | canvas.translate(CENTER_X, CENTER_X); 68 | 69 | canvas.drawOval(new RectF(-r, -r, r, r), mPaint); 70 | canvas.drawCircle(0, 0, CENTER_RADIUS, mCenterPaint); 71 | 72 | if (mTrackingCenter) { 73 | int c = mCenterPaint.getColor(); 74 | mCenterPaint.setStyle(Paint.Style.STROKE); 75 | 76 | if (mHighlightCenter) { 77 | mCenterPaint.setAlpha(0xFF); 78 | } else { 79 | mCenterPaint.setAlpha(0x80); 80 | } 81 | canvas.drawCircle(0, 0, 82 | CENTER_RADIUS + mCenterPaint.getStrokeWidth(), 83 | mCenterPaint); 84 | 85 | mCenterPaint.setStyle(Paint.Style.FILL); 86 | mCenterPaint.setColor(c); 87 | } 88 | } 89 | 90 | @Override 91 | protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 92 | setMeasuredDimension(CENTER_X*2, CENTER_Y*2); 93 | } 94 | 95 | private static final int CENTER_X = 100; 96 | private static final int CENTER_Y = 100; 97 | private static final int CENTER_RADIUS = 32; 98 | 99 | private int floatToByte(float x) { 100 | return Math.round(x); 101 | } 102 | private int pinToByte(int n) { 103 | if (n < 0) { 104 | n = 0; 105 | } else if (n > 255) { 106 | n = 255; 107 | } 108 | return n; 109 | } 110 | 111 | private int ave(int s, int d, float p) { 112 | return s + Math.round(p * (d - s)); 113 | } 114 | 115 | private int interpColor(int colors[], float unit) { 116 | if (unit <= 0) { 117 | return colors[0]; 118 | } 119 | if (unit >= 1) { 120 | return colors[colors.length - 1]; 121 | } 122 | 123 | float p = unit * (colors.length - 1); 124 | int i = (int)p; 125 | p -= i; 126 | 127 | // now p is just the fractional part [0...1) and i is the index 128 | int c0 = colors[i]; 129 | int c1 = colors[i+1]; 130 | int a = ave(Color.alpha(c0), Color.alpha(c1), p); 131 | int r = ave(Color.red(c0), Color.red(c1), p); 132 | int g = ave(Color.green(c0), Color.green(c1), p); 133 | int b = ave(Color.blue(c0), Color.blue(c1), p); 134 | 135 | return Color.argb(a, r, g, b); 136 | } 137 | 138 | private int rotateColor(int color, float rad) { 139 | float deg = rad * 180 / 3.1415927f; 140 | int r = Color.red(color); 141 | int g = Color.green(color); 142 | int b = Color.blue(color); 143 | 144 | ColorMatrix cm = new ColorMatrix(); 145 | ColorMatrix tmp = new ColorMatrix(); 146 | 147 | cm.setRGB2YUV(); 148 | tmp.setRotate(0, deg); 149 | cm.postConcat(tmp); 150 | tmp.setYUV2RGB(); 151 | cm.postConcat(tmp); 152 | 153 | final float[] a = cm.getArray(); 154 | 155 | int ir = floatToByte(a[0] * r + a[1] * g + a[2] * b); 156 | int ig = floatToByte(a[5] * r + a[6] * g + a[7] * b); 157 | int ib = floatToByte(a[10] * r + a[11] * g + a[12] * b); 158 | 159 | return Color.argb(Color.alpha(color), pinToByte(ir), 160 | pinToByte(ig), pinToByte(ib)); 161 | } 162 | 163 | private static final float PI = 3.1415926f; 164 | 165 | @Override 166 | public boolean onTouchEvent(MotionEvent event) { 167 | float x = event.getX() - CENTER_X; 168 | float y = event.getY() - CENTER_Y; 169 | boolean inCenter = Math.sqrt(x*x + y*y) <= CENTER_RADIUS; 170 | 171 | switch (event.getAction()) { 172 | case MotionEvent.ACTION_DOWN: 173 | mTrackingCenter = inCenter; 174 | if (inCenter) { 175 | mHighlightCenter = true; 176 | invalidate(); 177 | break; 178 | } 179 | case MotionEvent.ACTION_MOVE: 180 | if (mTrackingCenter) { 181 | if (mHighlightCenter != inCenter) { 182 | mHighlightCenter = inCenter; 183 | invalidate(); 184 | } 185 | } else { 186 | float angle = (float) Math.atan2(y, x); 187 | // need to turn angle [-PI ... PI] into unit [0....1] 188 | float unit = angle/(2*PI); 189 | if (unit < 0) { 190 | unit += 1; 191 | } 192 | mCenterPaint.setColor(interpColor(mColors, unit)); 193 | invalidate(); 194 | } 195 | break; 196 | case MotionEvent.ACTION_UP: 197 | if (mTrackingCenter) { 198 | if (inCenter) { 199 | mListener.colorChanged(mCenterPaint.getColor()); 200 | } 201 | mTrackingCenter = false; // so we draw w/o halo 202 | invalidate(); 203 | } 204 | break; 205 | } 206 | return true; 207 | } 208 | } 209 | 210 | public ColorPickerDialog(Context context, 211 | OnColorChangedListener listener, 212 | int initialColor) { 213 | super(context); 214 | 215 | mListener = listener; 216 | mInitialColor = Color.BLACK; 217 | } 218 | 219 | @Override 220 | protected void onCreate(Bundle savedInstanceState) { 221 | super.onCreate(savedInstanceState); 222 | OnColorChangedListener l = new OnColorChangedListener() { 223 | public void colorChanged(int color) { 224 | mListener.colorChanged(color); 225 | dismiss(); 226 | } 227 | }; 228 | 229 | setContentView(new ColorPickerView(getContext(), l, mInitialColor)); 230 | setTitle("Pick a Color"); 231 | } 232 | } 233 | 234 | -------------------------------------------------------------------------------- /RichEditText/src/main/java/com/eastearly/richedittextview/EmailHtmlUtil.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2008 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.eastearly.richedittextview; 18 | 19 | import java.util.regex.Matcher; 20 | import java.util.regex.Pattern; 21 | 22 | import android.graphics.Typeface; 23 | import android.text.Layout; 24 | import android.text.Spanned; 25 | import android.text.TextUtils; 26 | import android.text.style.AbsoluteSizeSpan; 27 | import android.text.style.AlignmentSpan; 28 | import android.text.style.BackgroundColorSpan; 29 | import android.text.style.CharacterStyle; 30 | import android.text.style.ForegroundColorSpan; 31 | import android.text.style.ImageSpan; 32 | import android.text.style.ParagraphStyle; 33 | import android.text.style.QuoteSpan; 34 | import android.text.style.StrikethroughSpan; 35 | import android.text.style.StyleSpan; 36 | import android.text.style.SubscriptSpan; 37 | import android.text.style.SuperscriptSpan; 38 | import android.text.style.TypefaceSpan; 39 | import android.text.style.URLSpan; 40 | import android.text.style.UnderlineSpan; 41 | 42 | public class EmailHtmlUtil { 43 | 44 | // Regex that matches characters that have special meaning in HTML. '<', 45 | // '>', '&' and 46 | // multiple continuous spaces. 47 | private static final Pattern PLAIN_TEXT_TO_ESCAPE = Pattern 48 | .compile("[<>&]| {2,}|\r?\n"); 49 | 50 | /** 51 | * Escape some special character as HTML escape sequence. 52 | * 53 | * @param text 54 | * Text to be displayed using WebView. 55 | * @return Text correctly escaped. 56 | */ 57 | public static String escapeCharacterToDisplay(String text) { 58 | Pattern pattern = PLAIN_TEXT_TO_ESCAPE; 59 | Matcher match = pattern.matcher(text); 60 | 61 | if (match.find()) { 62 | StringBuilder out = new StringBuilder(); 63 | int end = 0; 64 | do { 65 | int start = match.start(); 66 | out.append(text.substring(end, start)); 67 | end = match.end(); 68 | int c = text.codePointAt(start); 69 | if (c == ' ') { 70 | // Escape successive spaces into series of " ". 71 | for (int i = 1, n = end - start; i < n; ++i) { 72 | out.append(" "); 73 | } 74 | out.append(' '); 75 | } else if (c == '\r' || c == '\n') { 76 | out.append("
"); 77 | } else if (c == '<') { 78 | out.append("<"); 79 | } else if (c == '>') { 80 | out.append(">"); 81 | } else if (c == '&') { 82 | out.append("&"); 83 | } 84 | } while (match.find()); 85 | out.append(text.substring(end)); 86 | text = out.toString(); 87 | } 88 | return text; 89 | } 90 | 91 | public static String toHtml(Spanned spans) { 92 | StringBuilder out = new StringBuilder(); 93 | withinHtml(out, spans); 94 | return out.toString(); 95 | } 96 | 97 | private static void withinHtml(StringBuilder out, Spanned text ) { 98 | int len = text.length(); 99 | 100 | int next; 101 | for (int i = 0; i < text.length(); i = next) { 102 | next = text.nextSpanTransition(i, len, ParagraphStyle.class); 103 | ParagraphStyle[] style = text.getSpans(i, next, 104 | ParagraphStyle.class); 105 | String elements = " "; 106 | boolean needDiv = false; 107 | 108 | for (int j = 0; j < style.length; j++) { 109 | if (style[j] instanceof AlignmentSpan) { 110 | Layout.Alignment align = ((AlignmentSpan) style[j]) 111 | .getAlignment(); 112 | needDiv = true; 113 | if (align == Layout.Alignment.ALIGN_CENTER) { 114 | elements = "align=\"center\" " + elements; 115 | } else if (align == Layout.Alignment.ALIGN_OPPOSITE) { 116 | elements = "align=\"right\" " + elements; 117 | } else { 118 | elements = "align=\"left\" " + elements; 119 | } 120 | } 121 | } 122 | if (needDiv) { 123 | out.append("
"); 124 | } 125 | 126 | withinDiv(out, text, i, next); 127 | 128 | if (needDiv) { 129 | out.append("
"); 130 | } 131 | } 132 | } 133 | 134 | private static void withinDiv(StringBuilder out, Spanned text, int start, 135 | int end) { 136 | int next; 137 | for (int i = start; i < end; i = next) { 138 | next = text.nextSpanTransition(i, end, QuoteSpan.class); 139 | QuoteSpan[] quotes = text.getSpans(i, next, QuoteSpan.class); 140 | 141 | for (QuoteSpan quote : quotes) { 142 | out.append("
"); 143 | } 144 | 145 | withinBlockquote(out, text, i, next); 146 | 147 | for (QuoteSpan quote : quotes) { 148 | out.append("
\n"); 149 | } 150 | } 151 | } 152 | 153 | private static void withinBlockquote(StringBuilder out, Spanned text, 154 | int start, int end) { 155 | out.append(getOpenParaTagWithDirection(text, start, end)); 156 | 157 | int next; 158 | for (int i = start; i < end; i = next) { 159 | next = TextUtils.indexOf(text, '\n', i, end); 160 | if (next < 0) { 161 | next = end; 162 | } 163 | 164 | int nl = 0; 165 | 166 | while (next < end && text.charAt(next) == '\n') { 167 | nl++; 168 | next++; 169 | } 170 | 171 | withinParagraph(out, text, i, next - nl, nl, next == end); 172 | } 173 | 174 | out.append("

\n"); 175 | } 176 | 177 | private static String getOpenParaTagWithDirection(Spanned text, int start, 178 | int end) { 179 | 180 | return "

"; 181 | } 182 | 183 | private static void withinParagraph(StringBuilder out, Spanned text, 184 | int start, int end, int nl, boolean last) { 185 | int next; 186 | for (int i = start; i < end; i = next) { 187 | next = text.nextSpanTransition(i, end, CharacterStyle.class); 188 | CharacterStyle[] style = text.getSpans(i, next, 189 | CharacterStyle.class); 190 | 191 | for (int j = 0; j < style.length; j++) { 192 | if (style[j] instanceof StyleSpan) { 193 | int s = ((StyleSpan) style[j]).getStyle(); 194 | 195 | if ((s & Typeface.BOLD) != 0) { 196 | out.append(""); 197 | } 198 | if ((s & Typeface.ITALIC) != 0) { 199 | out.append(""); 200 | } 201 | } 202 | if (style[j] instanceof TypefaceSpan) { 203 | String s = ((TypefaceSpan) style[j]).getFamily(); 204 | 205 | if (s.equals("monospace")) { 206 | out.append(""); 207 | } 208 | } 209 | if (style[j] instanceof SuperscriptSpan) { 210 | out.append(""); 211 | } 212 | if (style[j] instanceof SubscriptSpan) { 213 | out.append(""); 214 | } 215 | if (style[j] instanceof UnderlineSpan) { 216 | out.append(""); 217 | } 218 | if (style[j] instanceof StrikethroughSpan) { 219 | out.append(""); 220 | } 221 | if (style[j] instanceof URLSpan) { 222 | out.append(""); 225 | } 226 | if (style[j] instanceof ImageSpan) { 227 | out.append(""); 230 | 231 | // Don't output the dummy character underlying the image. 232 | i = next; 233 | } 234 | if (style[j] instanceof AbsoluteSizeSpan) { 235 | out.append(String.format("", ((AbsoluteSizeSpan) style[j]).getSize()/3)); 236 | } 237 | if (style[j] instanceof ForegroundColorSpan) { 238 | out.append(""); 247 | } 248 | if (style[j] instanceof BackgroundColorSpan) { 249 | out.append(""); 258 | } 259 | } 260 | 261 | withinStyle(out, text, i, next); 262 | 263 | for (int j = style.length - 1; j >= 0; j--) { 264 | if (style[j] instanceof BackgroundColorSpan) { 265 | out.append(""); 266 | } 267 | if (style[j] instanceof ForegroundColorSpan) { 268 | out.append(""); 269 | } 270 | // if (style[j] instanceof AbsoluteSizeSpan) { 271 | // out.append(""); 272 | // int fontSize = ((HtmlAbsoluteSizeSpan) style[j]).getHtmlFontSize(); 273 | // if(fontSize>HtmlAbsoluteSizeSpan.STANDARD_FONT_SIZE) 274 | // { 275 | // out.append(""); 276 | // } 277 | // else if(fontSize < HtmlAbsoluteSizeSpan.STANDARD_FONT_SIZE) 278 | // { 279 | // out.append(""); 280 | // } 281 | // } 282 | if (style[j] instanceof AbsoluteSizeSpan) { 283 | out.append(""); 284 | } 285 | if (style[j] instanceof URLSpan) { 286 | out.append(""); 287 | } 288 | if (style[j] instanceof StrikethroughSpan) { 289 | out.append(""); 290 | } 291 | if (style[j] instanceof UnderlineSpan) { 292 | out.append(""); 293 | } 294 | if (style[j] instanceof SubscriptSpan) { 295 | out.append(""); 296 | } 297 | if (style[j] instanceof SuperscriptSpan) { 298 | out.append(""); 299 | } 300 | if (style[j] instanceof TypefaceSpan) { 301 | String s = ((TypefaceSpan) style[j]).getFamily(); 302 | 303 | if (s.equals("monospace")) { 304 | out.append(""); 305 | } 306 | } 307 | if (style[j] instanceof StyleSpan) { 308 | int s = ((StyleSpan) style[j]).getStyle(); 309 | 310 | if ((s & Typeface.BOLD) != 0) { 311 | out.append(""); 312 | } 313 | if ((s & Typeface.ITALIC) != 0) { 314 | out.append(""); 315 | } 316 | } 317 | } 318 | } 319 | 320 | String p = last ? "" : "

\n" 321 | + getOpenParaTagWithDirection(text, start, end); 322 | 323 | if (nl == 1) { 324 | out.append("
\n"); 325 | } else if (nl == 2) { 326 | out.append(p); 327 | } else { 328 | for (int i = 2; i < nl; i++) { 329 | out.append("
"); 330 | } 331 | out.append(p); 332 | } 333 | } 334 | 335 | private static void withinStyle(StringBuilder out, CharSequence text, 336 | int start, int end) { 337 | for (int i = start; i < end; i++) { 338 | char c = text.charAt(i); 339 | 340 | if (c == '<') { 341 | out.append("<"); 342 | } else if (c == '>') { 343 | out.append(">"); 344 | } else if (c == '&') { 345 | out.append("&"); 346 | } else if (c > 0x7E || c < ' ') { 347 | out.append("&#" + ((int) c) + ";"); 348 | } else if (c == ' ') { 349 | while (i + 1 < end && text.charAt(i + 1) == ' ') { 350 | out.append(" "); 351 | i++; 352 | } 353 | 354 | out.append(' '); 355 | } else { 356 | out.append(c); 357 | } 358 | } 359 | } 360 | } 361 | -------------------------------------------------------------------------------- /RichEditText/src/main/java/com/eastearly/richedittextview/HtmlAbsoluteSizeSpan.java: -------------------------------------------------------------------------------- 1 | package com.eastearly.richedittextview; 2 | import android.text.style.AbsoluteSizeSpan; 3 | 4 | public class HtmlAbsoluteSizeSpan extends AbsoluteSizeSpan{ 5 | public static final int STANDARD_FONT_SIZE = 3; 6 | private float _basicSize; 7 | //private static String _fontSizeScaleSample; 8 | private static String[]scales = null; 9 | public HtmlAbsoluteSizeSpan(int size,float bkSize) { 10 | super(size); 11 | _basicSize = bkSize; 12 | // TODO Auto-generated constructor stub 13 | } 14 | public static void Init(String fontSizeSampleString) 15 | { 16 | scales = fontSizeSampleString.split(","); 17 | } 18 | int getHtmlFontSize() 19 | { 20 | for(int i = 0; i < scales.length;i++) 21 | if(Math.abs(_basicSize * Float.parseFloat(scales[i]) - super.getSize()) < 0.1) 22 | { 23 | if(i==0) 24 | return 2; 25 | else if(i==1) 26 | return STANDARD_FONT_SIZE; 27 | else 28 | return 5; 29 | } 30 | return STANDARD_FONT_SIZE; 31 | } 32 | public static int switchFontSize(float bkSize,float lastUpdateSize) 33 | { 34 | 35 | float MIN = STANDARD_FONT_SIZE; 36 | int index = 0; 37 | if(scales == null) return -1; 38 | for(int i =0; i < scales.length;i++) 39 | { 40 | float absDiff = (float) Math.abs(Float.parseFloat(scales[i]) - lastUpdateSize/ bkSize); 41 | if(MIN > absDiff) 42 | { 43 | MIN = absDiff; 44 | index = i; 45 | } 46 | } 47 | if(index==scales.length-1)index=-1; 48 | return (int) (Float.parseFloat(scales[index+1]) * bkSize + 0.5); 49 | } 50 | } 51 | 52 | -------------------------------------------------------------------------------- /RichEditText/src/main/java/com/eastearly/richedittextview/IEditText.java: -------------------------------------------------------------------------------- 1 | package com.eastearly.richedittextview; 2 | 3 | import android.os.Bundle; 4 | import android.text.Editable; 5 | import android.text.TextUtils; 6 | import android.text.TextWatcher; 7 | import android.view.accessibility.AccessibilityEvent; 8 | import android.view.accessibility.AccessibilityNodeInfo; 9 | import android.widget.TextView; 10 | 11 | /** 12 | * Created by dachen on 10/10/15. 13 | */ 14 | public interface IEditText { 15 | Editable getText(); 16 | 17 | void setText(CharSequence text, TextView.BufferType type); 18 | 19 | void setSelection(int start, int stop); 20 | 21 | void setSelection(int index); 22 | 23 | void selectAll(); 24 | 25 | void extendSelection(int index); 26 | 27 | void setEllipsize(TextUtils.TruncateAt ellipsis); 28 | 29 | void onInitializeAccessibilityEvent(AccessibilityEvent event); 30 | 31 | void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info); 32 | 33 | boolean performAccessibilityAction(int action, Bundle arguments); 34 | 35 | /** 36 | * Adds a TextWatcher to the list of those whose methods are called 37 | * whenever this TextView's text changes. 38 | *

39 | * In 1.0, the {@link TextWatcher#afterTextChanged} method was erroneously 40 | * not called after {@link #setText} calls. Now, doing {@link #setText} 41 | * if there are any text changed listeners forces the buffer type to 42 | * Editable if it would not otherwise be and does call this method. 43 | */ 44 | void addTextChangedListener(TextWatcher watcher); 45 | void removeTextChangedListener(TextWatcher watcher); 46 | } 47 | -------------------------------------------------------------------------------- /RichEditText/src/main/java/com/eastearly/richedittextview/IRichEditText.java: -------------------------------------------------------------------------------- 1 | package com.eastearly.richedittextview; 2 | 3 | /** 4 | * Created by dachen on 10/9/15. 5 | */ 6 | public interface IRichEditText { 7 | public String toHtml(); 8 | 9 | } 10 | -------------------------------------------------------------------------------- /RichEditText/src/main/java/com/eastearly/richedittextview/RichEditText.java: -------------------------------------------------------------------------------- 1 | /*** 2 | Copyright (c) 2015 earlyeast , xiaodong666@gmail.com 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); you may 5 | not use this file except in compliance with the License. You may obtain 6 | 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 | 15 | package com.eastearly.richedittextview; 16 | 17 | 18 | import android.animation.AnimatorSet; 19 | import android.animation.ObjectAnimator; 20 | import android.annotation.TargetApi; 21 | import android.app.AlertDialog; 22 | import android.content.Context; 23 | import android.content.DialogInterface; 24 | import android.content.res.TypedArray; 25 | import android.graphics.Color; 26 | import android.graphics.Typeface; 27 | import android.os.Build; 28 | import android.os.Bundle; 29 | import android.text.Editable; 30 | import android.text.Selection; 31 | import android.text.Spannable; 32 | import android.text.SpannableStringBuilder; 33 | import android.text.Spanned; 34 | import android.text.TextUtils; 35 | import android.text.TextWatcher; 36 | import android.text.style.AbsoluteSizeSpan; 37 | import android.text.style.BackgroundColorSpan; 38 | import android.text.style.CharacterStyle; 39 | import android.text.style.ForegroundColorSpan; 40 | import android.text.style.StrikethroughSpan; 41 | import android.text.style.StyleSpan; 42 | import android.text.style.URLSpan; 43 | import android.text.style.UnderlineSpan; 44 | import android.util.AttributeSet; 45 | import android.util.Log; 46 | import android.util.TypedValue; 47 | import android.view.LayoutInflater; 48 | import android.view.View; 49 | import android.view.ViewGroup; 50 | import android.view.accessibility.AccessibilityEvent; 51 | import android.view.accessibility.AccessibilityNodeInfo; 52 | import android.view.animation.Animation; 53 | import android.view.animation.AnimationUtils; 54 | import android.widget.EditText; 55 | import android.widget.ImageButton; 56 | import android.widget.ImageView; 57 | import android.widget.LinearLayout; 58 | import android.widget.RelativeLayout; 59 | import android.widget.TextView; 60 | 61 | /** 62 | * Created by dachen on 9/28/15. 63 | */ 64 | public class RichEditText extends LinearLayout implements View.OnClickListener,IRichEditText, IEditText { 65 | 66 | public static final int EDIT_TOGGLE_ID = 1002; 67 | public static final int EDIT_TEXT_ID = 1001; 68 | private static final String TAG = "RichEditText"; 69 | private final float initialAlpha = 0.7f; 70 | private Context _context; 71 | private String textContent; 72 | private int textColor; 73 | private EditText mEditText; 74 | private LinearLayout mHtmloptions; 75 | private ImageButton mImageButton; 76 | private SpannableStringBuilder mSS; 77 | private boolean mToolbarClosed = true; 78 | private boolean mRichEditEnabled = true; 79 | private final static String Tag = "RichEditTextView"; 80 | 81 | public RichEditText(Context context) { 82 | super(context, null); 83 | setupView(context,null,0,0); 84 | } 85 | 86 | public RichEditText(Context context, AttributeSet attrs) { 87 | super(context, attrs, 0); 88 | setupView(context, attrs, 0, 0); 89 | } 90 | 91 | 92 | @TargetApi(Build.VERSION_CODES.LOLLIPOP) 93 | public RichEditText(Context context, AttributeSet attrs, int defStyleAttr) { 94 | super(context, attrs, defStyleAttr, 0); 95 | setupView(context, attrs, defStyleAttr, 0); 96 | } 97 | 98 | @TargetApi(Build.VERSION_CODES.LOLLIPOP) 99 | public RichEditText(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes){ 100 | super(context,attrs,defStyleAttr,defStyleRes); 101 | setupView(context,attrs,defStyleAttr,defStyleRes); 102 | 103 | } 104 | //Test to add a relativelayout programtically 105 | private void setupView(Context context, AttributeSet attrs, int defStyleAttr,int defStyleRes){ 106 | _context = context; 107 | RelativeLayout relativeLayout = new RelativeLayout(context); 108 | RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(LayoutParams.MATCH_PARENT, 109 | LayoutParams.MATCH_PARENT); 110 | 111 | EditText editText = new EditText(context,attrs); 112 | 113 | editText.setId(EDIT_TEXT_ID); 114 | // 115 | TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.RichEditText); 116 | mRichEditEnabled = a.getBoolean(R.styleable.RichEditText_richEditAble,true); 117 | a.recycle(); 118 | 119 | mImageButton = new ImageButton(context); 120 | int px = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 24, getResources().getDisplayMetrics()); 121 | RelativeLayout.LayoutParams editToggleParams = new RelativeLayout.LayoutParams(px,px); 122 | mImageButton.setBackground(getResources().getDrawable(R.drawable.ic_keyboard_arrow_left_black_24dp)); 123 | editToggleParams.addRule(RelativeLayout.ALIGN_BOTTOM, EDIT_TEXT_ID); 124 | editToggleParams.addRule(RelativeLayout.ALIGN_RIGHT, EDIT_TEXT_ID); 125 | mImageButton.setLayoutParams(editToggleParams); 126 | mImageButton.setId(EDIT_TOGGLE_ID); 127 | mImageButton.setRotation(-90); 128 | mImageButton.setOnClickListener(this); 129 | 130 | View htmlOptions = inflate(context,R.layout.htmloptions,null); 131 | 132 | RelativeLayout.LayoutParams htmlOptionsLayoutParams = new RelativeLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT,ViewGroup.LayoutParams.WRAP_CONTENT); 133 | 134 | htmlOptionsLayoutParams.addRule(RelativeLayout.BELOW, 1001); 135 | htmlOptionsLayoutParams.addRule(RelativeLayout.ALIGN_RIGHT, 1001); 136 | 137 | htmlOptions.setLayoutParams(htmlOptionsLayoutParams); 138 | relativeLayout.setLayoutParams(params); 139 | relativeLayout.addView(editText); 140 | relativeLayout.addView(mImageButton); 141 | 142 | //htmlOptions.setVisibility(View.GONE); 143 | if(mRichEditEnabled) { 144 | relativeLayout.addView(htmlOptions); 145 | } 146 | addView(relativeLayout); 147 | 148 | this.mEditText = editText; 149 | if(mRichEditEnabled) { 150 | findViewById(R.id.makeBold).setOnClickListener(this); 151 | findViewById(R.id.makeItalic).setOnClickListener(this); 152 | findViewById(R.id.makeUnderline).setOnClickListener(this); 153 | findViewById(R.id.makeBackground).setOnClickListener(this); 154 | findViewById(R.id.makeForeground).setOnClickListener(this); 155 | findViewById(R.id.makeHyperlink).setOnClickListener(this); 156 | findViewById(R.id.makeStrikethrough).setOnClickListener(this); 157 | findViewById(R.id.makeScaleX).setOnClickListener(this); 158 | mHtmloptions = (LinearLayout) findViewById(R.id.rich_toolbar); 159 | mHtmloptions.setVisibility(View.GONE); 160 | // mImageButton = (ImageButton) findViewById(R.id.list_toggle); 161 | // mImageButton.setOnClickListener(this); 162 | } 163 | this.mEditText.setOnClickListener(this); 164 | setOnClickListener(this); 165 | mSS = new SpannableStringBuilder(mEditText.getText()); 166 | 167 | } 168 | @Override 169 | public void onClick(View view) { 170 | 171 | //refresh tool bar status 172 | getHtmloptionToolButton(); 173 | mSS = new SpannableStringBuilder(mEditText.getText()); 174 | final int start = mEditText.getSelectionStart(); 175 | final int end = mEditText.getSelectionEnd(); 176 | 177 | int viewId = view.getId(); 178 | if (viewId == mEditText.getId()) { 179 | this.refreshHtmloptionBar(); 180 | } 181 | 182 | CharacterStyle span = null; 183 | 184 | if (viewId == R.id.makeBold) { 185 | if (toggleImageView((ImageView) view)) 186 | span = new StyleSpan(Typeface.BOLD); 187 | else 188 | disableStyleSpan(start, end, Typeface.BOLD); 189 | 190 | } else if (viewId == R.id.makeItalic) { 191 | if (toggleImageView((ImageView) view)) 192 | span = new StyleSpan(Typeface.ITALIC); 193 | else 194 | disableStyleSpan(start, end, Typeface.ITALIC); 195 | 196 | } else if (viewId == R.id.makeUnderline) { 197 | if (toggleImageView((ImageView) view)) 198 | span = new UnderlineSpan(); 199 | else 200 | disableSpan(start, end, UnderlineSpan.class); 201 | 202 | } 203 | else if(viewId == R.id.makeStrikethrough){ 204 | if(toggleImageView((ImageView) view)) 205 | span = new StrikethroughSpan(); 206 | else 207 | disableSpan(start, end, StrikethroughSpan.class); 208 | } 209 | else if(viewId == R.id.makeScaleX && start!=end){ 210 | AbsoluteSizeSpan[] spans = mSS.getSpans(start, end, AbsoluteSizeSpan.class); 211 | float textSize; 212 | if (spans.length > 0) { 213 | textSize = (float) (spans[spans.length-1].getSize() * 1.1); 214 | } else { 215 | textSize = (float) (mEditText.getTextSize() * 1.1); 216 | } 217 | for (AbsoluteSizeSpan spanItem : spans) { 218 | mSS.removeSpan(spanItem); 219 | } 220 | span = new AbsoluteSizeSpan((int)textSize); 221 | 222 | 223 | } 224 | else if (viewId == R.id.makeForeground && start!=end) { 225 | new ColorPickerDialog(_context, new ColorPickerDialog.OnColorChangedListener() { 226 | @Override 227 | public void colorChanged(int color) { 228 | mSS.setSpan(new ForegroundColorSpan(color), 229 | start, end, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); 230 | mEditText.setText(mSS, TextView.BufferType.SPANNABLE); 231 | //((TextView)MessageCompose.this.findViewById(R.id.makeForeground)).setTextColor(color); 232 | } 233 | 234 | }, Color.BLACK).show(); 235 | 236 | } 237 | else if(viewId == R.id.makeBackground && start!=end){ 238 | new ColorPickerDialog(_context, new ColorPickerDialog.OnColorChangedListener () { 239 | @Override 240 | public void colorChanged(int color) { 241 | mSS.setSpan(new BackgroundColorSpan(color), 242 | start, end, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); 243 | mEditText.setText(mSS, TextView.BufferType.SPANNABLE); 244 | //MessageCompose.this.findViewById(R.id.makeBackground).setBackgroundColor(color); 245 | } 246 | 247 | }, Color.WHITE).show(); 248 | } 249 | else if (viewId == R.id.makeHyperlink && start!= end) { 250 | 251 | AlertDialog.Builder builder = new AlertDialog.Builder(_context); 252 | final EditText urlText = new EditText(_context); 253 | urlText.setText("http://www."); 254 | builder.setView(urlText) 255 | .setTitle(getResources().getString(R.string.url_entry_title)) 256 | .setPositiveButton(R.string.ok, new DialogInterface.OnClickListener() { 257 | @Override 258 | public void onClick(DialogInterface dialog, int id) { 259 | if (urlText.getText() != null) { 260 | String url = urlText.getText().toString(); 261 | mSS.setSpan(new URLSpan(url), 262 | start, end, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); 263 | mEditText.setText(mSS, TextView.BufferType.SPANNABLE); 264 | } 265 | 266 | } 267 | }) 268 | .setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() { 269 | public void onClick(DialogInterface dialog, int id) { 270 | dialog.cancel(); 271 | } 272 | }); 273 | builder.create().show(); 274 | 275 | } 276 | else if( viewId == EDIT_TOGGLE_ID){ 277 | if(!mToolbarClosed){ 278 | mToolbarClosed = !mToolbarClosed; 279 | mImageButton.setRotation(-90); 280 | //mImageButton.setBackground(getResources().getDrawable(R.drawable.ic_keyboard_arrow_left_black_24dp)); 281 | // AnimatorSet set = new AnimatorSet(); 282 | // set.playTogether( 283 | // ObjectAnimator.ofFloat(mHtmloptions, "translationX", mHtmloptions.getMeasuredWidth()), 284 | // ObjectAnimator.ofFloat(mHtmloptions, "alpha", 1, 0) 285 | // ); 286 | // set.start(); 287 | 288 | mHtmloptions.setVisibility(View.GONE); 289 | //requestLayout(); 290 | 291 | } 292 | 293 | else 294 | { 295 | mToolbarClosed = !mToolbarClosed; 296 | mHtmloptions.setVisibility(View.VISIBLE); 297 | //requestLayout(); 298 | mImageButton.setRotation(90); 299 | // ObjectAnimator objectAnimator = new ObjectAnimator(); 300 | // AnimatorSet set = new AnimatorSet(); 301 | // set.playTogether( 302 | // ObjectAnimator.ofFloat(mHtmloptions, "translationX", 0), 303 | // ObjectAnimator.ofFloat(mHtmloptions, "alpha", 0, 1) 304 | // ); 305 | // set.start(); 306 | } 307 | } 308 | if (span != null) { 309 | 310 | if (start == end) 311 | mSS.setSpan(span, start, end, Spanned.SPAN_INCLUSIVE_INCLUSIVE); 312 | else 313 | mSS.setSpan(span, start, end, Spanned.SPAN_EXCLUSIVE_INCLUSIVE); 314 | mEditText.setText(mSS, TextView.BufferType.SPANNABLE); 315 | mEditText.setSelection(end); 316 | } 317 | } 318 | private void refreshHtmloptionBar() { 319 | if(!mRichEditEnabled)return; 320 | int start = mEditText.getSelectionStart(); 321 | int end = mEditText.getSelectionEnd(); 322 | if (start == end && end == 0) return; 323 | letImageViewOff((ImageView) findViewById(R.id.makeBold)); 324 | letImageViewOff((ImageView) findViewById(R.id.makeItalic)); 325 | letImageViewOff((ImageView) findViewById(R.id.makeUnderline)); 326 | Object[] spans = mSS.getSpans(start, end, Object.class); 327 | for (Object span : spans) { 328 | ImageView iv = getHtmloptionToolButton(span); 329 | if (iv != null) 330 | this.letImageViewOn(iv); 331 | } 332 | } 333 | private boolean imageViewOff(ImageView iv) { 334 | 335 | return (iv.getAlpha() - initialAlpha < 0.01); 336 | } 337 | 338 | private void letImageViewOn(ImageView iv) { 339 | iv.setAlpha(1.0f); 340 | 341 | } 342 | 343 | private void letImageViewOff(ImageView iv) { 344 | iv.setAlpha(initialAlpha); 345 | 346 | } 347 | private void getHtmloptionToolButton() { 348 | if(!mRichEditEnabled) return; 349 | int start = mEditText.getSelectionStart(); 350 | int end = mEditText.getSelectionEnd(); 351 | if (start == end && end == 0) return; 352 | letImageViewOff((ImageView) findViewById(R.id.makeBold)); 353 | letImageViewOff((ImageView) findViewById(R.id.makeItalic)); 354 | letImageViewOff((ImageView) findViewById(R.id.makeUnderline)); 355 | letImageViewOff((ImageView) findViewById(R.id.makeStrikethrough)); 356 | Object[] spans = mSS.getSpans(start, end, Object.class); 357 | for (Object span : spans) { 358 | ImageView iv = getHtmloptionToolButton(span); 359 | if (iv != null) 360 | this.letImageViewOn(iv); 361 | } 362 | } 363 | private ImageView getHtmloptionToolButton(Object span) { 364 | if (span instanceof StyleSpan) { 365 | switch (((StyleSpan) span).getStyle()) { 366 | case Typeface.BOLD: 367 | return (ImageView) findViewById(R.id.makeBold); 368 | case Typeface.ITALIC: 369 | return (ImageView) findViewById(R.id.makeItalic); 370 | default: 371 | return null; 372 | } 373 | } else if (span instanceof UnderlineSpan) { 374 | return (ImageView) findViewById(R.id.makeUnderline); 375 | } 376 | else if(span instanceof StrikethroughSpan) 377 | return (ImageView) findViewById(R.id.makeStrikethrough); 378 | return null; 379 | } 380 | private boolean toggleImageView(ImageView iv) { 381 | if (imageViewOff(iv)) { 382 | letImageViewOn(iv); 383 | return true; 384 | } else { 385 | letImageViewOff(iv); 386 | return false; 387 | } 388 | } 389 | private void disableStyleSpan(int start, int end, int typeFace) { 390 | StyleSpan[] spans = mSS.getSpans(start, end, StyleSpan.class); 391 | for (int i = spans.length - 1; i >= 0; i--) 392 | if (spans[i].getStyle() == typeFace) { 393 | if (mSS.getSpanStart(spans[i]) <= start) 394 | mSS.setSpan(spans[i], mSS.getSpanStart(spans[i]), start, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); 395 | if (mSS.getSpanEnd(spans[i]) > start) { 396 | 397 | mSS.setSpan(spans[i], start, mSS.getSpanEnd(spans[i]), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); 398 | } 399 | //mSS.removeSpan(spans[i]); 400 | } 401 | mEditText.setText(mSS, TextView.BufferType.SPANNABLE); 402 | mEditText.setSelection(end); 403 | } 404 | 405 | private void disableSpan(int start, int end, Class clz) { 406 | CharacterStyle[] spans = mSS.getSpans(start, end, clz); 407 | for (int i = spans.length - 1; i >= 0; i--) 408 | 409 | { 410 | if (mSS.getSpanStart(spans[i]) <= start) 411 | mSS.setSpan(spans[i], mSS.getSpanStart(spans[i]), start, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); 412 | if (mSS.getSpanEnd(spans[i]) > start) { 413 | 414 | mSS.setSpan(spans[i], start, mSS.getSpanEnd(spans[i]), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); 415 | } 416 | //mSS.removeSpan(spans[i]); 417 | } 418 | mEditText.setText(mSS, TextView.BufferType.SPANNABLE); 419 | mEditText.setSelection(end); 420 | } 421 | 422 | 423 | 424 | public Editable getText() { 425 | return mEditText.getText(); 426 | } 427 | 428 | 429 | 430 | public void setText(CharSequence text, TextView.BufferType type) { 431 | mEditText.setText(text, type); 432 | } 433 | 434 | /** 435 | * Convenience for {@link Selection#setSelection(Spannable, int, int)}. 436 | */ 437 | 438 | public void setSelection(int start, int stop) { 439 | mEditText.setSelection(start, stop); 440 | } 441 | 442 | /** 443 | * Convenience for {@link Selection#setSelection(Spannable, int)}. 444 | */ 445 | 446 | public void setSelection(int index) { 447 | mEditText.setSelection(index); 448 | } 449 | 450 | /** 451 | * Convenience for {@link Selection#selectAll}. 452 | */ 453 | 454 | public void selectAll() { 455 | mEditText.selectAll(); 456 | } 457 | 458 | /** 459 | * Convenience for {@link Selection#extendSelection}. 460 | */ 461 | 462 | public void extendSelection(int index) { 463 | mEditText.extendSelection(index); 464 | } 465 | 466 | 467 | public void setEllipsize(TextUtils.TruncateAt ellipsis) { 468 | mEditText.setEllipsize(ellipsis); 469 | } 470 | 471 | 472 | public void onInitializeAccessibilityEvent(AccessibilityEvent event) { 473 | mEditText.onInitializeAccessibilityEvent(event); 474 | } 475 | 476 | 477 | public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) { 478 | mEditText.onInitializeAccessibilityNodeInfo(info); 479 | } 480 | 481 | 482 | public boolean performAccessibilityAction(int action, Bundle arguments) { 483 | return mEditText.performAccessibilityAction(action,arguments); 484 | 485 | } 486 | 487 | @Override 488 | public void addTextChangedListener(TextWatcher watcher) { 489 | mEditText.addTextChangedListener(watcher); 490 | } 491 | 492 | @Override 493 | public void removeTextChangedListener(TextWatcher watcher) { 494 | mEditText.removeTextChangedListener(watcher); 495 | } 496 | 497 | //implements IRichEditText 498 | public String toHtml() { 499 | if(mSS==null){ 500 | return null; 501 | } 502 | return EmailHtmlUtil.toHtml (mSS); 503 | } 504 | } 505 | -------------------------------------------------------------------------------- /RichEditText/src/main/res/drawable-hdpi/ic_format_color_text_black_24dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chen-xiao-dong/RichEditText/47669c069425a7e9fc294b525ca91f7e75ad67ad/RichEditText/src/main/res/drawable-hdpi/ic_format_color_text_black_24dp.png -------------------------------------------------------------------------------- /RichEditText/src/main/res/drawable-hdpi/ic_keyboard_arrow_left_black_24dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chen-xiao-dong/RichEditText/47669c069425a7e9fc294b525ca91f7e75ad67ad/RichEditText/src/main/res/drawable-hdpi/ic_keyboard_arrow_left_black_24dp.png -------------------------------------------------------------------------------- /RichEditText/src/main/res/drawable-hdpi/ic_keyboard_arrow_right_black_24dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chen-xiao-dong/RichEditText/47669c069425a7e9fc294b525ca91f7e75ad67ad/RichEditText/src/main/res/drawable-hdpi/ic_keyboard_arrow_right_black_24dp.png -------------------------------------------------------------------------------- /RichEditText/src/main/res/drawable-mdpi/ic_format_color_text_black_24dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chen-xiao-dong/RichEditText/47669c069425a7e9fc294b525ca91f7e75ad67ad/RichEditText/src/main/res/drawable-mdpi/ic_format_color_text_black_24dp.png -------------------------------------------------------------------------------- /RichEditText/src/main/res/drawable-mdpi/ic_keyboard_arrow_left_black_24dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chen-xiao-dong/RichEditText/47669c069425a7e9fc294b525ca91f7e75ad67ad/RichEditText/src/main/res/drawable-mdpi/ic_keyboard_arrow_left_black_24dp.png -------------------------------------------------------------------------------- /RichEditText/src/main/res/drawable-mdpi/ic_keyboard_arrow_right_black_24dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chen-xiao-dong/RichEditText/47669c069425a7e9fc294b525ca91f7e75ad67ad/RichEditText/src/main/res/drawable-mdpi/ic_keyboard_arrow_right_black_24dp.png -------------------------------------------------------------------------------- /RichEditText/src/main/res/drawable-xhdpi/ic_bold.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chen-xiao-dong/RichEditText/47669c069425a7e9fc294b525ca91f7e75ad67ad/RichEditText/src/main/res/drawable-xhdpi/ic_bold.png -------------------------------------------------------------------------------- /RichEditText/src/main/res/drawable-xhdpi/ic_format_color_text_black_24dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chen-xiao-dong/RichEditText/47669c069425a7e9fc294b525ca91f7e75ad67ad/RichEditText/src/main/res/drawable-xhdpi/ic_format_color_text_black_24dp.png -------------------------------------------------------------------------------- /RichEditText/src/main/res/drawable-xhdpi/ic_hyperlink.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chen-xiao-dong/RichEditText/47669c069425a7e9fc294b525ca91f7e75ad67ad/RichEditText/src/main/res/drawable-xhdpi/ic_hyperlink.png -------------------------------------------------------------------------------- /RichEditText/src/main/res/drawable-xhdpi/ic_italic.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chen-xiao-dong/RichEditText/47669c069425a7e9fc294b525ca91f7e75ad67ad/RichEditText/src/main/res/drawable-xhdpi/ic_italic.png -------------------------------------------------------------------------------- /RichEditText/src/main/res/drawable-xhdpi/ic_keyboard_arrow_left_black_24dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chen-xiao-dong/RichEditText/47669c069425a7e9fc294b525ca91f7e75ad67ad/RichEditText/src/main/res/drawable-xhdpi/ic_keyboard_arrow_left_black_24dp.png -------------------------------------------------------------------------------- /RichEditText/src/main/res/drawable-xhdpi/ic_keyboard_arrow_right_black_24dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chen-xiao-dong/RichEditText/47669c069425a7e9fc294b525ca91f7e75ad67ad/RichEditText/src/main/res/drawable-xhdpi/ic_keyboard_arrow_right_black_24dp.png -------------------------------------------------------------------------------- /RichEditText/src/main/res/drawable-xhdpi/ic_menu_done_holo_light.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chen-xiao-dong/RichEditText/47669c069425a7e9fc294b525ca91f7e75ad67ad/RichEditText/src/main/res/drawable-xhdpi/ic_menu_done_holo_light.png -------------------------------------------------------------------------------- /RichEditText/src/main/res/drawable-xhdpi/ic_menu_send_holo_light.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chen-xiao-dong/RichEditText/47669c069425a7e9fc294b525ca91f7e75ad67ad/RichEditText/src/main/res/drawable-xhdpi/ic_menu_send_holo_light.png -------------------------------------------------------------------------------- /RichEditText/src/main/res/drawable-xhdpi/ic_strikethrough.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chen-xiao-dong/RichEditText/47669c069425a7e9fc294b525ca91f7e75ad67ad/RichEditText/src/main/res/drawable-xhdpi/ic_strikethrough.png -------------------------------------------------------------------------------- /RichEditText/src/main/res/drawable-xhdpi/ic_textcolor.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chen-xiao-dong/RichEditText/47669c069425a7e9fc294b525ca91f7e75ad67ad/RichEditText/src/main/res/drawable-xhdpi/ic_textcolor.png -------------------------------------------------------------------------------- /RichEditText/src/main/res/drawable-xhdpi/ic_textsize.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chen-xiao-dong/RichEditText/47669c069425a7e9fc294b525ca91f7e75ad67ad/RichEditText/src/main/res/drawable-xhdpi/ic_textsize.png -------------------------------------------------------------------------------- /RichEditText/src/main/res/drawable-xhdpi/ic_underline.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chen-xiao-dong/RichEditText/47669c069425a7e9fc294b525ca91f7e75ad67ad/RichEditText/src/main/res/drawable-xhdpi/ic_underline.png -------------------------------------------------------------------------------- /RichEditText/src/main/res/drawable-xxhdpi/ic_format_color_text_black_24dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chen-xiao-dong/RichEditText/47669c069425a7e9fc294b525ca91f7e75ad67ad/RichEditText/src/main/res/drawable-xxhdpi/ic_format_color_text_black_24dp.png -------------------------------------------------------------------------------- /RichEditText/src/main/res/drawable-xxhdpi/ic_keyboard_arrow_left_black_24dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chen-xiao-dong/RichEditText/47669c069425a7e9fc294b525ca91f7e75ad67ad/RichEditText/src/main/res/drawable-xxhdpi/ic_keyboard_arrow_left_black_24dp.png -------------------------------------------------------------------------------- /RichEditText/src/main/res/drawable-xxhdpi/ic_keyboard_arrow_right_black_24dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chen-xiao-dong/RichEditText/47669c069425a7e9fc294b525ca91f7e75ad67ad/RichEditText/src/main/res/drawable-xxhdpi/ic_keyboard_arrow_right_black_24dp.png -------------------------------------------------------------------------------- /RichEditText/src/main/res/drawable-xxxhdpi/ic_format_color_text_black_24dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chen-xiao-dong/RichEditText/47669c069425a7e9fc294b525ca91f7e75ad67ad/RichEditText/src/main/res/drawable-xxxhdpi/ic_format_color_text_black_24dp.png -------------------------------------------------------------------------------- /RichEditText/src/main/res/drawable-xxxhdpi/ic_keyboard_arrow_left_black_24dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chen-xiao-dong/RichEditText/47669c069425a7e9fc294b525ca91f7e75ad67ad/RichEditText/src/main/res/drawable-xxxhdpi/ic_keyboard_arrow_left_black_24dp.png -------------------------------------------------------------------------------- /RichEditText/src/main/res/drawable-xxxhdpi/ic_keyboard_arrow_right_black_24dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chen-xiao-dong/RichEditText/47669c069425a7e9fc294b525ca91f7e75ad67ad/RichEditText/src/main/res/drawable-xxxhdpi/ic_keyboard_arrow_right_black_24dp.png -------------------------------------------------------------------------------- /RichEditText/src/main/res/drawable/arrow.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /RichEditText/src/main/res/layout/htmloptions.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 16 | 26 | 38 | 50 | 61 | 62 | 68 | 69 | 70 | 71 | 77 | 87 | 96 | 97 | 98 | 99 | -------------------------------------------------------------------------------- /RichEditText/src/main/res/values/attr_richedit_text.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /RichEditText/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #3F51B5 4 | #303F9F 5 | #FF4081 6 | #ff808080 7 | #d3d3d3 8 | #3cb8e6 9 | 10 | -------------------------------------------------------------------------------- /RichEditText/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | RichEditTextView 3 | 0.7 4 | Enter the URL to hyperlink 5 | OK 6 | Cancel 7 | 8 | -------------------------------------------------------------------------------- /RichEditText/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 10 | 11 | 22 | 23 | #aaaaaa 24 | 25 | -------------------------------------------------------------------------------- /RichEditText/src/test/java/com/eastearly/richedittextview/ExampleUnitTest.java: -------------------------------------------------------------------------------- 1 | package com.eastearly.richedittextview; 2 | 3 | import org.junit.Test; 4 | 5 | import static org.junit.Assert.*; 6 | 7 | /** 8 | * To work on unit tests, switch the Test Artifact in the Build Variants view. 9 | */ 10 | public class ExampleUnitTest { 11 | @Test 12 | public void addition_isCorrect() throws Exception { 13 | assertEquals(4, 2 + 2); 14 | } 15 | } -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | // Top-level build file where you can add configuration options common to all sub-projects/modules. 2 | 3 | buildscript { 4 | repositories { 5 | jcenter() 6 | } 7 | dependencies { 8 | classpath 'com.android.tools.build:gradle:1.3.0' 9 | classpath 'com.jfrog.bintray.gradle:gradle-bintray-plugin:1.2' 10 | classpath 'com.github.dcendents:android-maven-plugin:1.2' 11 | // NOTE: Do not place your application dependencies here; they belong 12 | // in the individual module build.gradle files 13 | } 14 | } 15 | 16 | allprojects { 17 | repositories { 18 | jcenter() 19 | } 20 | } 21 | 22 | task clean(type: Delete) { 23 | delete rootProject.buildDir 24 | } 25 | -------------------------------------------------------------------------------- /demo2/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | 3 | android { 4 | compileSdkVersion 23 5 | buildToolsVersion "23.0.1" 6 | 7 | defaultConfig { 8 | applicationId "com.eastearly.demo2" 9 | minSdkVersion 16 10 | targetSdkVersion 23 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 | compile fileTree(dir: 'libs', include: ['*.jar']) 24 | testCompile 'junit:junit:4.12' 25 | compile 'com.android.support:appcompat-v7:23.0.1' 26 | compile 'com.android.support:design:23.0.1' 27 | releaseCompile 'com.earlyeast.android:RichEditText:0.2.4' 28 | debugCompile project(':RichEditText') 29 | } 30 | -------------------------------------------------------------------------------- /demo2/src/androidTest/java/com/eastearly/demo2/ApplicationTest.java: -------------------------------------------------------------------------------- 1 | package com.eastearly.demo2; 2 | 3 | import android.app.Application; 4 | import android.test.ApplicationTestCase; 5 | 6 | /** 7 | * Testing Fundamentals 8 | */ 9 | public class ApplicationTest extends ApplicationTestCase { 10 | public ApplicationTest() { 11 | super(Application.class); 12 | } 13 | } -------------------------------------------------------------------------------- /demo2/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 11 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /demo2/src/main/java/com/eastearly/demo2/MainActivity.java: -------------------------------------------------------------------------------- 1 | package com.eastearly.demo2; 2 | 3 | import android.os.Bundle; 4 | import android.support.design.widget.FloatingActionButton; 5 | import android.support.design.widget.Snackbar; 6 | import android.support.v7.app.AppCompatActivity; 7 | import android.support.v7.widget.Toolbar; 8 | import android.text.Editable; 9 | import android.text.TextWatcher; 10 | import android.view.View; 11 | import android.view.Menu; 12 | import android.view.MenuItem; 13 | import android.webkit.WebView; 14 | import android.widget.TextView; 15 | 16 | import com.eastearly.richedittextview.RichEditText; 17 | 18 | public class MainActivity extends AppCompatActivity { 19 | 20 | private RichEditText mRichEditText; 21 | private WebView mWebView; 22 | private TextView mPlainHtml; 23 | 24 | @Override 25 | protected void onCreate(Bundle savedInstanceState) { 26 | super.onCreate(savedInstanceState); 27 | setContentView(R.layout.activity_main); 28 | Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar); 29 | setSupportActionBar(toolbar); 30 | 31 | FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab); 32 | fab.setOnClickListener(new View.OnClickListener() { 33 | @Override 34 | public void onClick(View view) { 35 | Snackbar.make(view, "Replace with your own action", Snackbar.LENGTH_LONG) 36 | .setAction("Action", null).show(); 37 | } 38 | }); 39 | 40 | mRichEditText = (RichEditText)findViewById(R.id.rich_edittext); 41 | mWebView = (WebView)findViewById(R.id.load_webview); 42 | mPlainHtml = (TextView)findViewById(R.id.plainhtml); 43 | mRichEditText.addTextChangedListener(new TextWatcher() { 44 | @Override 45 | public void beforeTextChanged(CharSequence s, int start, int count, int after) { 46 | 47 | } 48 | 49 | @Override 50 | public void onTextChanged(CharSequence s, int start, int before, int count) { 51 | 52 | } 53 | 54 | @Override 55 | public void afterTextChanged(Editable s) { 56 | mWebView.loadData(mRichEditText.toHtml(), "text/html; charset=utf-8",null); 57 | mPlainHtml.setText(mRichEditText.toHtml()); 58 | } 59 | }); 60 | 61 | } 62 | @Override 63 | public void onResume(){ 64 | super.onResume(); 65 | String plainHtml = mRichEditText.toHtml(); 66 | mPlainHtml.setText(plainHtml); 67 | mWebView.loadData(plainHtml, "text/html; charset=utf-8",null); 68 | } 69 | @Override 70 | public boolean onCreateOptionsMenu(Menu menu) { 71 | // Inflate the menu; this adds items to the action bar if it is present. 72 | getMenuInflater().inflate(R.menu.menu_main, menu); 73 | return true; 74 | } 75 | 76 | @Override 77 | public boolean onOptionsItemSelected(MenuItem item) { 78 | // Handle action bar item clicks here. The action bar will 79 | // automatically handle clicks on the Home/Up button, so long 80 | // as you specify a parent activity in AndroidManifest.xml. 81 | int id = item.getItemId(); 82 | 83 | //noinspection SimplifiableIfStatement 84 | if (id == R.id.action_settings) { 85 | return true; 86 | } 87 | 88 | return super.onOptionsItemSelected(item); 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /demo2/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 11 | 12 | 15 | 16 | 17 | 18 | 19 | 20 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /demo2/src/main/res/layout/content_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 11 | 12 | 16 | 17 | 25 | 26 | 35 | 36 | 45 | 46 | 55 | 56 | 65 | 66 | 67 | 68 | -------------------------------------------------------------------------------- /demo2/src/main/res/menu/menu_main.xml: -------------------------------------------------------------------------------- 1 |

4 | 6 | 7 | -------------------------------------------------------------------------------- /demo2/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chen-xiao-dong/RichEditText/47669c069425a7e9fc294b525ca91f7e75ad67ad/demo2/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /demo2/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chen-xiao-dong/RichEditText/47669c069425a7e9fc294b525ca91f7e75ad67ad/demo2/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /demo2/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chen-xiao-dong/RichEditText/47669c069425a7e9fc294b525ca91f7e75ad67ad/demo2/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /demo2/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chen-xiao-dong/RichEditText/47669c069425a7e9fc294b525ca91f7e75ad67ad/demo2/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /demo2/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chen-xiao-dong/RichEditText/47669c069425a7e9fc294b525ca91f7e75ad67ad/demo2/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /demo2/src/main/res/values-v21/styles.xml: -------------------------------------------------------------------------------- 1 | > 2 | 8 | 9 | -------------------------------------------------------------------------------- /demo2/src/main/res/values-w820dp/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 64dp 6 | 7 | -------------------------------------------------------------------------------- /demo2/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #3F51B5 4 | #303F9F 5 | #FF4081 6 | 7 | -------------------------------------------------------------------------------- /demo2/src/main/res/values/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 16dp 4 | 16dp 5 | 16dp 6 | 7 | -------------------------------------------------------------------------------- /demo2/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | demo2 3 | Settings 4 | 5 | -------------------------------------------------------------------------------- /demo2/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 10 | 14 |