Skip to content

Commit ead5d83

Browse files
author
ellieshen
committed
feat: 插入话题@自定义表情
1 parent bb9dd02 commit ead5d83

File tree

8 files changed

+234
-28
lines changed

8 files changed

+234
-28
lines changed

android/src/main/java/com/variabletextinput/VariableTextInputViewManager.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
import com.facebook.react.uimanager.ViewProps;
1414
import com.facebook.react.uimanager.annotations.ReactProp;
1515
import com.facebook.react.uimanager.annotations.ReactPropGroup;
16+
import com.variabletextinput.view.VariableTextInput;
1617

1718
import java.util.HashMap;
1819
import java.util.Map;
@@ -176,10 +177,11 @@ public void receiveCommand(VariableTextInput root, int commandId, @Nullable Read
176177
break;
177178
case 2:
178179
//插入emoji
179-
this.insertImage(args);
180+
root.insertEmoji(args);
180181
break;
181182
case 3:
182183
//插入tag 或者@
184+
root.insertMentions(args);
183185
//todo
184186
break;
185187
case 4:
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
package com.variabletextinput.bean;
2+
3+
public class RichTextBean {
4+
public String name;
5+
public String id;
6+
public String tag;
7+
public int color;
8+
public String content;
9+
public int type; //0 插入表情 1插入@或者#
10+
11+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
package com.variabletextinput.util;
2+
3+
public class ActivityConst {
4+
public static final String NAME = "name";
5+
public static final String ID = "id";
6+
public static final String TAG = "tag";
7+
public static final String COLOR = "color";
8+
}

android/src/main/java/com/variabletextinput/VariableEditText.java renamed to android/src/main/java/com/variabletextinput/view/VariableEditText.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
package com.variabletextinput;
1+
package com.variabletextinput.view;
22
import android.content.Context;
33
import android.util.AttributeSet;
44

android/src/main/java/com/variabletextinput/VariableTextInput.java renamed to android/src/main/java/com/variabletextinput/view/VariableTextInput.java

Lines changed: 140 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
package com.variabletextinput;
1+
package com.variabletextinput.view;
22

33
import android.app.Activity;
44
import android.content.Context;
@@ -11,8 +11,12 @@
1111
import android.text.Editable;
1212
import android.text.Spannable;
1313
import android.text.SpannableString;
14+
import android.text.SpannableStringBuilder;
15+
import android.text.Spanned;
16+
import android.text.TextUtils;
1417
import android.text.TextWatcher;
1518
import android.text.style.ImageSpan;
19+
import android.util.Log;
1620
import android.view.Gravity;
1721
import android.view.inputmethod.InputMethodManager;
1822
import android.widget.LinearLayout;
@@ -22,9 +26,16 @@
2226

2327
import com.facebook.react.bridge.Arguments;
2428
import com.facebook.react.bridge.ReactContext;
29+
import com.facebook.react.bridge.ReadableArray;
30+
import com.facebook.react.bridge.ReadableMap;
2531
import com.facebook.react.bridge.WritableMap;
2632
import com.facebook.react.uimanager.Spacing;
2733
import 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+
2839
import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
2940
import 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

Comments
 (0)