This is the implementation to handle emoticons inside a EditText
. This implementation uses the TextWatcher
to monitor the EditText
changes and detect if some emoticon was removed when some text is deleted.
Note that this implementation also verifies if a text selection was deleted (not only the delete key).
To avoid issues with text prediction when typing a text, it is recommended to surround the emoticon text with spaces (the text prediction can join the emoticon text with the adjacent text).
package com.takamori.testapp;
import java.util.ArrayList;
import android.app.Activity;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.text.Editable;
import android.text.Spanned;
import android.text.TextWatcher;
import android.text.style.ImageSpan;
import android.view.Menu;
import android.view.MenuItem;
import android.widget.EditText;
public class MainActivity extends Activity {
private EmoticonHandler mEmoticonHandler;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
EditText editor = (EditText) findViewById(R.id.messageEditor);
// Create the emoticon handler.
mEmoticonHandler = new EmoticonHandler(editor);
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.main, menu);
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.action_insert_emoticon:
// WARNING: The emoticon text shall be surrounded by spaces
// to avoid issues with text prediction.
mEmoticonHandler.insert(" :-) ", R.drawable.smile);
return true;
default:
return super.onOptionsItemSelected(item);
}
}
private static class EmoticonHandler implements TextWatcher {
private final EditText mEditor;
private final ArrayList<ImageSpan> mEmoticonsToRemove = new ArrayList<ImageSpan>();
public EmoticonHandler(EditText editor) {
// Attach the handler to listen for text changes.
mEditor = editor;
mEditor.addTextChangedListener(this);
}
public void insert(String emoticon, int resource) {
// Create the ImageSpan
Drawable drawable = mEditor.getResources().getDrawable(resource);
drawable.setBounds(0, 0, drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight());
ImageSpan span = new ImageSpan(drawable, ImageSpan.ALIGN_BASELINE);
// Get the selected text.
int start = mEditor.getSelectionStart();
int end = mEditor.getSelectionEnd();
Editable message = mEditor.getEditableText();
// Insert the emoticon.
message.replace(start, end, emoticon);
message.setSpan(span, start, start + emoticon.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
}
@Override
public void beforeTextChanged(CharSequence text, int start, int count, int after) {
// Check if some text will be removed.
if (count > 0) {
int end = start + count;
Editable message = mEditor.getEditableText();
ImageSpan[] list = message.getSpans(start, end, ImageSpan.class);
for (ImageSpan span : list) {
// Get only the emoticons that are inside of the changed
// region.
int spanStart = message.getSpanStart(span);
int spanEnd = message.getSpanEnd(span);
if ((spanStart < end) && (spanEnd > start)) {
// Add to remove list
mEmoticonsToRemove.add(span);
}
}
}
}
@Override
public void afterTextChanged(Editable text) {
Editable message = mEditor.getEditableText();
// Commit the emoticons to be removed.
for (ImageSpan span : mEmoticonsToRemove) {
int start = message.getSpanStart(span);
int end = message.getSpanEnd(span);
// Remove the span
message.removeSpan(span);
// Remove the remaining emoticon text.
if (start != end) {
message.delete(start, end);
}
}
mEmoticonsToRemove.clear();
}
@Override
public void onTextChanged(CharSequence text, int start, int before, int count) {
}
}
}
与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…