Trying to implement the display of tags when typing text from the keyboard

  1. There is the usual EditText , which is hung by the TextWatcher listener.

  2. Attributes:

     android:inputType="textCapSentences|textMultiLine|textShortMessage" android:maxLines="4" 
  3. As soon as in the TextWatcher'a method TextWatcher'a afterTextChanged(Editable string) see that a certain word contains sharpe (#) - ImageSpan draw it in the form of a tag through ImageSpan .

  4. Installing new text in EditText ->

     SpannableString spannableString = new SpannableString(string); ImageSpan imageSpan = getImageSpan(string); int startPosition = getStartPosition(string); int endPosition = getEndPosition(string); spannableString.setSpan(imageSpan, startPosition, endPosition,Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); setText(spannableString); 

Everything works well and looks like this: example

Problem : after calling setText(spannableString); the numeric keypad (if there was a number entry) or the keyboard with emoticons (if there was an entry emoticon) is changed to plain text.

Keyboards change regardless of whether there is an ImageSpan or not. (if in the afterTextChanged(Editable string) method I just call setText(string) - the keyboard changes).

Keyboards do NOT change unless setText(string) called.

Questions: how to ensure that the keyboard does not change when I add ImageSpan inside EditText ? Perhaps I generally go the wrong way. Perhaps there is a simpler and more effective solution, about working with the design of words within EditText ?

I will be glad to hear any ideas! Thank.

  • Friends, any ideas? - Andrew Grow

1 answer 1

So, the decision to add a picture inside EditText and so that the keyboard does not jump was this:

  1. Set the tag via setText and remember it.
  2. With the following entries, we look, if the tag has not changed - do not use setText. Then the keyboards do not switch (the input completion method is not called) and everything works correctly.
  3. When the tag changes, remove Spannable and reinstall them.

In the examples below you can see how a) Clean outdated spans b) Create an ImageSpan (in this case make a picture out of text) c) Install a picture - text inside EditText

Hope this helps someone.

  /** * Clean the formatting of the string. * * @param editable text which has spannable format */ private void removeOldSpans(Editable editable){ Object[] spans = editable.getSpans(0, editable.toString().length(), Object.class); for (Object span : spans) { if (span instanceof ImageSpan) { editable.removeSpan(span); } } } 
 /** * Setting formats to tags. * * @param startTag a first tag in the string * @param endTag a last tag in the string * @param editable the string with text */ public void setTagSpans(String startTag, String endTag, Editable editable) { oldStartTag = startTag; oldEndTag = endTag; removeTextChangedListener(tw); //disable listener String currentText = editable.toString(); //remove old span removeOldSpans(editable); //create new span SpannableString spannableString = new SpannableString(editable); if (!TextUtils.isEmpty(startTag) && TextUtils.isEmpty(endTag)) { //we have only first tag final ImageSpan startTagSpan = getChipsSpan(startTag); spannableString.setSpan(startTagSpan, 0, startTag.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); } else if (TextUtils.isEmpty(startTag) && !TextUtils.isEmpty(endTag)) { //we have only last tag final ImageSpan endTagSpan = getChipsSpan(endTag); int last = currentText.lastIndexOf("#"); spannableString.setSpan(endTagSpan, last, currentText.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); } else if (!TextUtils.isEmpty(startTag) && !TextUtils.isEmpty(endTag)) { //we have both tags at the same time final ImageSpan startTagSpan = getChipsSpan(startTag); final ImageSpan endTagSpan = getChipsSpan(endTag); int last = currentText.lastIndexOf("#"); spannableString.setSpan(startTagSpan, 0, startTag.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); spannableString.setSpan(endTagSpan, last, currentText.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); } setText(spannableString, BufferType.SPANNABLE); forceSetCursorPosition = spannableString.length(); setSelection(forceSetCursorPosition); addTextChangedListener(tw); } /** * Format the tag from a text into an image span. * * @param tag text * @return text which has been formatted into an image span */ private ImageSpan getChipsSpan(String tag) { //Build chips view final TextViewFontExt textViewFontExt = new TextViewFontExt(getContext()); textViewFontExt.setTypeface(Helper.Fonts.getTypeface(getContext(), "fonts/Roboto-Medium.ttf")); textViewFontExt.setLayoutParams( new LinearLayout.LayoutParams( LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.WRAP_CONTENT)); textViewFontExt.setText(tag); textViewFontExt.setTextSize(getResources().getDimension(R.dimen.chats_chips_span_text_size)); textViewFontExt.setTextColor(getResources().getColor(android.R.color.white)); textViewFontExt.setPadding( (int)getResources().getDimension(R.dimen.chats_chips_span_padding_lr), (int)getResources().getDimension(R.dimen.chats_chips_span_padding_top), (int)getResources().getDimension(R.dimen.chats_chips_span_padding_lr), (int)getResources().getDimension(R.dimen.chats_chips_span_padding_bottom)); Drawable tagBackground; if(Build.VERSION.SDK_INT < 21) { tagBackground = getResources().getDrawable(R.drawable.tag_input_background); } else { tagBackground = getResources().getDrawable(R.drawable.tag_input_background, null); } if(Build.VERSION.SDK_INT < 16) { textViewFontExt.setBackgroundDrawable(tagBackground); } else { textViewFontExt.setBackground(tagBackground); } //Build chips bitmap int spec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED); textViewFontExt.measure(spec, spec); textViewFontExt.layout(0, 0, textViewFontExt.getMeasuredWidth(), textViewFontExt.getMeasuredHeight()); Bitmap bitmap = Bitmap.createBitmap(textViewFontExt.getWidth(), textViewFontExt.getHeight(), Bitmap.Config.ARGB_8888); Canvas canvas = new Canvas(bitmap); canvas.translate(-textViewFontExt.getScrollX(), -textViewFontExt.getScrollY()); textViewFontExt.draw(canvas); textViewFontExt.setDrawingCacheEnabled(true); Bitmap cacheBitmap = textViewFontExt.getDrawingCache(); Bitmap tagBitmap = cacheBitmap.copy(Bitmap.Config.ARGB_8888, true); BitmapDrawable bitmapDrawable = new BitmapDrawable(getResources(), tagBitmap); bitmapDrawable.setBounds(0, 0, bitmapDrawable.getIntrinsicWidth(), bitmapDrawable.getIntrinsicHeight()); //Cleanup textViewFontExt.destroyDrawingCache(); bitmap.recycle(); cacheBitmap.recycle(); return new ImageSpan(bitmapDrawable); }