1- package com .variabletextinput ;
1+ package com .variabletextinput . view ;
22
33import android .app .Activity ;
44import android .content .Context ;
1111import android .text .Editable ;
1212import android .text .Spannable ;
1313import android .text .SpannableString ;
14+ import android .text .SpannableStringBuilder ;
15+ import android .text .Spanned ;
16+ import android .text .TextUtils ;
1417import android .text .TextWatcher ;
1518import android .text .style .ImageSpan ;
19+ import android .util .Log ;
1620import android .view .Gravity ;
1721import android .view .inputmethod .InputMethodManager ;
1822import android .widget .LinearLayout ;
2226
2327import com .facebook .react .bridge .Arguments ;
2428import com .facebook .react .bridge .ReactContext ;
29+ import com .facebook .react .bridge .ReadableArray ;
30+ import com .facebook .react .bridge .ReadableMap ;
2531import com .facebook .react .bridge .WritableMap ;
2632import com .facebook .react .uimanager .Spacing ;
2733import com .facebook .react .uimanager .events .RCTEventEmitter ;
34+ import com .variabletextinput .R ;
35+ import com .variabletextinput .bean .RichTextBean ;
36+ import com .variabletextinput .util .ActivityConst ;
37+ import com .variabletextinput .widget .TextSpan ;
38+
2839import static android .view .ViewGroup .LayoutParams .MATCH_PARENT ;
2940import static android .view .ViewGroup .LayoutParams .WRAP_CONTENT ;
3041
@@ -36,8 +47,12 @@ public class VariableTextInput extends LinearLayout {
3647 private VariableEditText editText ;
3748 private ScrollView scrollView ;
3849 private Boolean ignoreNextLocalTextChange = false ;
39- public VariableTextInput (Context context ){
50+
51+ private Context mContext ;
52+
53+ public VariableTextInput (Context context ) {
4054 super (context );
55+ this .mContext = context ;
4156 scrollView = new ScrollView (context );
4257 scrollView .setLayoutParams (new LinearLayout .LayoutParams (MATCH_PARENT , MATCH_PARENT ));
4358 scrollView .setClipToPadding (false );
@@ -58,32 +73,45 @@ public VariableTextInput(Context context){
5873 private int oldHeight = editText .getHeight (); // 保存旧的高度
5974 private int oldWidth = editText .getWidth (); // 保存旧的宽度
6075 private String mPreviousText ;
76+
6177 @ Override
6278 public void beforeTextChanged (CharSequence s , int start , int count , int after ) {
6379 mPreviousText = s .toString ();
6480 }
81+
6582 @ Override
6683 public void onTextChanged (CharSequence s , int start , int before , int count ) {
6784 // Rearranging the text (i.e. changing between singleline and multiline attributes) can
6885 // also trigger onTextChanged, call the event in JS only when the text actually changed
6986 if (count == 0 && before == 0 ) {
7087 return ;
7188 }
72- String newText = s .toString ().substring (start , start + count );
73- String oldText = mPreviousText .substring (start , start + before );
74- // Don't send same text changes
75- if (count == before && newText .equals (oldText )) {
76- return ;
89+ if (editText .getText () != null ) {
90+ TextSpan [] spans = editText .getText ().getSpans (0 , editText .getText ().length (), TextSpan .class );
91+ //整体删除span
92+ if (before == 1 && count == 0 ) {
93+ for (TextSpan textSpan : spans ) {
94+ if (editText .getText ().getSpanEnd (textSpan ) == start ) {
95+ editText .getText ().delete (editText .getText ().getSpanStart (textSpan ), editText .getText ().getSpanEnd (textSpan ));
96+ }
97+ }
98+ }
7799 }
78- if (ignoreNextLocalTextChange ) {
100+ // String newText = s.toString().substring(start, start + count);
101+ // String oldText = mPreviousText.substring(start, start + before);
102+ // // Don't send same text changes
103+ // if (count == before && newText.equals(oldText)) {
104+ // return;
105+ // }
106+ if (ignoreNextLocalTextChange ) {
79107 ignoreNextLocalTextChange = false ;
80108 return ;
81109 }
82110 WritableMap event = Arguments .createMap ();
83111 event .putString ("text" , s .toString ());
84112 final Context context = getContext ();
85113 if (context instanceof ReactContext ) {
86- ((ReactContext ) context ).getJSModule (RCTEventEmitter .class ).receiveEvent (getId (),"onAndroidChange" , event );
114+ ((ReactContext ) context ).getJSModule (RCTEventEmitter .class ).receiveEvent (getId (), "onAndroidChange" , event );
87115 }
88116 }
89117
@@ -96,22 +124,24 @@ public void afterTextChanged(Editable s) {
96124 // ...
97125 oldHeight = newHeight ; // 更新旧的高度
98126 WritableMap contentSize = Arguments .createMap ();
99- contentSize .putDouble ("height" ,newHeight * 4 / 11 );
100- contentSize .putDouble ("width" ,editText .getWidth ()* 4 / 11 );
127+ contentSize .putDouble ("height" , newHeight * 4 / 11 );
128+ contentSize .putDouble ("width" , editText .getWidth () * 4 / 11 );
101129 WritableMap event = Arguments .createMap ();
102- event .putMap ("contentSize" ,contentSize );
130+ event .putMap ("contentSize" , contentSize );
103131 final Context context = getContext ();
104132 if (context instanceof ReactContext ) {
105- ((ReactContext ) context ).getJSModule (RCTEventEmitter .class ).receiveEvent (getId (),"onAndroidContentSizeChange" , event );
133+ ((ReactContext ) context ).getJSModule (RCTEventEmitter .class ).receiveEvent (getId (), "onAndroidContentSizeChange" , event );
106134 }
107135 }
108136 }
109137 });
110138 }
139+
111140 @ Override
112141 protected void onLayout (boolean changed , int left , int top , int right , int bottom ) {
113142 super .onLayout (changed , left , top , right , bottom );
114143 }
144+
115145 public void setText (String text ) {
116146 // Fix for issue where first character typed does not trigger save event.
117147 // setText is called with an empty string originally as soon as the text view is initialized.
@@ -120,13 +150,15 @@ public void setText(String text) {
120150 ignoreNextLocalTextChange = !isEmpty ;
121151 editText .setText (text );
122152 }
153+
123154 public void setAutoFocus (boolean autoFocus ) {
124- if (autoFocus ) {
155+ if (autoFocus ) {
125156 editText .requestFocus ();
126157 InputMethodManager imm = (InputMethodManager ) getContext ().getSystemService (Context .INPUT_METHOD_SERVICE );
127158 imm .toggleSoftInput (InputMethodManager .SHOW_FORCED , InputMethodManager .HIDE_IMPLICIT_ONLY );
128159 }
129160 }
161+
130162 public void setEditable (boolean editable ) {
131163 /*
132164 setRawInputType is the only solution that works for keeping the text selectable while disabled
@@ -148,9 +180,11 @@ previously we used setEnabled(false), but this would make text unselectable when
148180 editText .setEnabled (editable );
149181
150182 }
151- public void focus (){
183+
184+ public void focus () {
152185 editText .requestFocus ();
153186 }
187+
154188 public void blur () {
155189 editText .clearFocus ();
156190
@@ -161,32 +195,37 @@ public void blur() {
161195 }
162196 imm .hideSoftInputFromWindow (editText .getWindowToken (), 0 );
163197 }
164- public void setImeOptions (int info ){
198+
199+ public void setImeOptions (int info ) {
165200 editText .setImeOptions (info );
166201 }
167- public void setInputType (int type ){
202+
203+ public void setInputType (int type ) {
168204 editText .setInputType (type );
169205 }
206+
170207 public void setContentPadding (int position , Integer padding ) {
171208 float scale = getResources ().getDisplayMetrics ().density ;
172209 int pixels = (int ) (padding * scale + 0.5f );
173210
174- if (position == Spacing .ALL ) {
211+ if (position == Spacing .ALL ) {
175212 editText .setPadding (pixels , pixels , pixels , pixels );
176- } else if (position == Spacing .LEFT ) {
213+ } else if (position == Spacing .LEFT ) {
177214 editText .setPadding (pixels , editText .getTotalPaddingTop (), editText .getPaddingRight (), editText .getPaddingBottom ());
178- } else if (position == Spacing .TOP ) {
215+ } else if (position == Spacing .TOP ) {
179216 editText .setPadding (editText .getPaddingLeft (), pixels , editText .getPaddingRight (), editText .getPaddingBottom ());
180- } else if (position == Spacing .RIGHT ) {
217+ } else if (position == Spacing .RIGHT ) {
181218 editText .setPadding (editText .getPaddingLeft (), editText .getTotalPaddingTop (), pixels , editText .getPaddingBottom ());
182- } else if (position == Spacing .BOTTOM ) {
219+ } else if (position == Spacing .BOTTOM ) {
183220 editText .setPadding (editText .getPaddingLeft (), editText .getTotalPaddingTop (), editText .getPaddingRight (), pixels );
184221 }
185222 }
223+
186224 public void setTextColor (Integer color ) {
187225 editText .setTextColor (color );
188226 }
189- public void setHighlightColor (Integer color ) {
227+
228+ public void setHighlightColor (Integer color ) {
190229 editText .setHighlightColor (color );
191230
192231 try {
@@ -209,8 +248,10 @@ public void setHighlightColor(Integer color) {
209248 field = editor .getClass ().getDeclaredField ("mCursorDrawable" );
210249 field .setAccessible (true );
211250 field .set (editor , drawables );
212- } catch (Exception ignored ) {}
251+ } catch (Exception ignored ) {
252+ }
213253 }
254+
214255 public void setHandlesColor (int color ) {
215256 try {
216257
@@ -252,12 +293,15 @@ public void setHandlesColor(int color) {
252293 e .printStackTrace ();
253294 }
254295 }
255- public void setPlaceholder (String placeholder ){
296+
297+ public void setPlaceholder (String placeholder ) {
256298 editText .setHint (placeholder );
257299 }
258- public void setUnderLineColorAndroid (Integer color ){
300+
301+ public void setUnderLineColorAndroid (Integer color ) {
259302 editText .setBackgroundTintList (ColorStateList .valueOf (color ));
260303 }
304+
261305 public void insertImage (String imagePath ) {
262306 Bitmap bitmap = BitmapFactory .decodeFile (imagePath );
263307 Drawable drawable = new BitmapDrawable (getResources (), bitmap );
@@ -267,5 +311,75 @@ public void insertImage(String imagePath) {
267311 spannableString .setSpan (span , 0 , 4 , Spannable .SPAN_EXCLUSIVE_EXCLUSIVE );
268312 editText .append (spannableString );
269313 }
314+
315+ public void insertMentions (ReadableArray args ) {
316+ if (args != null && args .size () > 0 ) {
317+ RichTextBean richTextBean = handleParams (args );
318+ setMentionsSpan (richTextBean );
319+ }
320+ }
321+
322+ public void insertEmoji (ReadableArray args ) {
323+ if (args != null && args .size () > 0 ) {
324+ RichTextBean richTextBean = handleParams (args );
325+ int startIndex = editText .getSelectionStart ();
326+ Log .e ("startIndex" , startIndex + "" );
327+ int endIndex = startIndex + richTextBean .tag .length ();
328+ Log .e ("endIndex" , endIndex + "" );
329+ Bitmap bitmap = BitmapFactory .decodeResource (getResources (), R .drawable .kuxiao );
330+ ImageSpan imageSpan = new ImageSpan (mContext , bitmap );
331+ if (editText .getText () != null ) {
332+ editText .getText ().insert (startIndex , richTextBean .tag );
333+ }
334+ SpannableStringBuilder ss = SpannableStringBuilder .valueOf (editText .getText ());
335+ ss .setSpan (imageSpan , startIndex , endIndex , Spanned .SPAN_EXCLUSIVE_EXCLUSIVE );
336+ editText .setText (ss );
337+ editText .setSelection (endIndex );
338+ editText .getText ().replace (startIndex , endIndex , richTextBean .content );
339+ }
340+ }
341+
342+ private RichTextBean handleParams (ReadableArray args ) {
343+ RichTextBean richTextBean = new RichTextBean ();
344+ ReadableMap map = args .getMap (0 );
345+ if (map .hasKey (ActivityConst .ID )) {
346+ richTextBean .id = map .getString (ActivityConst .ID );
347+ }
348+ if (map .hasKey (ActivityConst .TAG )) {
349+ String tag = map .getString (ActivityConst .TAG );
350+ //表情
351+ if (!TextUtils .isEmpty (tag ) && tag .startsWith ("[" )) {
352+ richTextBean .tag = tag ;
353+ richTextBean .content = String .format (mContext .getString (R .string .insert_emoji ), tag .replaceAll ("\\ [|\\ ]" , "" ));
354+ } else {
355+ richTextBean .tag = tag ;
356+ }
357+ }
358+ if (map .hasKey (ActivityConst .NAME )) {
359+ String name = map .getString (ActivityConst .NAME );
360+ richTextBean .name = name + " " ;
361+ richTextBean .content = String .format (mContext .getString (R .string .insert_mention ), richTextBean .tag , name , richTextBean .id );
362+ }
363+ if (map .hasKey (ActivityConst .COLOR )) {
364+ richTextBean .color = map .getInt (ActivityConst .COLOR );
365+ }
366+ return richTextBean ;
367+ }
368+
369+ private void setMentionsSpan (RichTextBean richTextBean ) {
370+ int startIndex = editText .getSelectionStart ();
371+ Log .e ("startIndex" , startIndex + "" );
372+ int endIndex = startIndex + richTextBean .name .length () + richTextBean .tag .length ();
373+ Log .e ("endIndex" , endIndex + "" );
374+ if (editText .getText () != null ) {
375+ editText .getText ().insert (startIndex , richTextBean .tag + richTextBean .name );
376+ }
377+ SpannableStringBuilder ss = SpannableStringBuilder .valueOf (editText .getText ());
378+ TextSpan textSpan = new TextSpan (mContext , richTextBean );
379+ ss .setSpan (textSpan , startIndex , endIndex , Spanned .SPAN_EXCLUSIVE_EXCLUSIVE );
380+ editText .setText (ss );
381+ editText .setSelection (endIndex );
382+ editText .getText ().replace (startIndex , endIndex , richTextBean .content );
383+ }
270384}
271385
0 commit comments