6

I am writing some text in EditText when i write "#data" its colour should be changed but doesn't change how can i do. Please check the below EditText which i have used

<EditText
         android:id="@+id/et_simple"
         android:layout_height="wrap_content"
         android:layout_width="match_parent">
</EditText>
Phantômaxx
  • 37,901
  • 21
  • 84
  • 115
Salman
  • 159
  • 1
  • 2
  • 7

5 Answers5

11

Hope this solution will help..!

I used this solution it is very useful! like adding the textWatcher interface over your editText and listening to textChange and finding out if the word starts with a hashTag then call the Change The color method on that word! it has some flaws but those are ignorable see this simple one here.

Spannable mspanable;
int hashTagIsComing = 0;
@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);



    final EditText edtTxtMine = (EditText) findViewById(R.id.editText1);

    mspanable = edtTxtMine.getText();

    edtTxtMine.addTextChangedListener(new TextWatcher() {

        @Override
        public void onTextChanged(CharSequence s, int start, int before, int count) {

            String startChar = null;

            try{
                startChar = Character.toString(s.charAt(start));
                Log.i(getClass().getSimpleName(), "CHARACTER OF NEW WORD: " + startChar);
            }
            catch(Exception ex){
                startChar = "";
            }

                if (startChar.equals("#")) {
                     changeTheColor(s.toString().substring(start), start, start + count);
                     hashTagIsComing++;
                }

                if(startChar.equals(" ")){
                    hashTagIsComing = 0;
                }

                if(hashTagIsComing != 0) {
                    changeTheColor(s.toString().substring(start), start, start + count);
                    hashTagIsComing++;
                }
        }

        @Override
        public void beforeTextChanged(CharSequence s, int start, int count,
                int after) {
            // TODO Auto-generated method stub

        }

        @Override
        public void afterTextChanged(Editable s) {
            // TODO Auto-generated method stub

        }
    });



}


private void changeTheColor(String s, int start, int end) {
    mspanable.setSpan(new ForegroundColorSpan(getResources().getColor(R.color.color)), start, end, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
}
Rizwan Atta
  • 3,222
  • 2
  • 19
  • 31
  • It starts doing weird things when you change the cursor position and start randomly adding or removing text or hashtags, it eventually ends writing everything in the changed color, no matters if hashtag or not. – Ezequiel Adrian Sep 02 '21 at 01:40
1

You can easily use the CodeView library to highlight hashtags, email or data format

CodeView mCodeView = findViewById(R.id.codeView);
Pattern hashTagPattern = Pattern.compile("#[a-zA-z0-9]+");
mCodeView.addSyntaxPattern(hashTagPattern, Color.BLUE);

You need to initialize it and set a pattern with colou for hashtag then you can change the color in the runtime and the result will be like this

Test hashtags

Library link: https://github.com/AmrDeveloper/codeview

AmrDeveloper
  • 3,826
  • 1
  • 21
  • 30
0

Kotlin

Complete solution with custom attributes

    <declare-styleable name="EditTextView">
        <attr name="enableTagging" format="boolean"/>
    </declare-styleable>
class EditTextView @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0) : AppCompatEditText(context, attrs, defStyleAttr) {
    private var hashtagindex = -1
    private var enableTagging:Boolean = false
        set(value) {
            field = value
            invalidate()
            requestLayout()
        }

    init {
        context.theme.obtainStyledAttributes(
            attrs,
            R.styleable.EditTextView, 0, 0).apply {
                try {
                    enableTagging = getBoolean(R.styleable.EditTextView_enableTagging, false)
                } finally {
                    recycle()
                }
        }
    }


    override fun onTextChanged(text: CharSequence?, start: Int, lengthBefore: Int, lengthAfter: Int) {
        text?.let { handleTagging(text, start, lengthBefore, lengthAfter) }
    }

    private fun handleTagging(text: CharSequence, start: Int, lengthBefore: Int, lengthAfter: Int) {
        if (!enableTagging || text.length <= start) return
        if (text[start] == '#') hashtagindex = start
        if (text[start] == ' ') hashtagindex = -1
        // If hashtag then color the work
        if (hashtagindex >= 0) {
            val spannableString = getText()
            val foregroundSpan = ForegroundColorSpan(ContextCompat.getColor(context, R.color.colorPrimary))
            spannableString?.setSpan(foregroundSpan, hashtagindex, text.length, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)
        }
    }
}

This is how you can enable tagging for the EditText

<com.example.xyz.util.editbox.EditTextView
        xmlns:custom="http://schemas.android.com/apk/res-auto"
        android:id="@+id/et"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"

        custom:enableTagging="true"

        tools:textAppearance="@style/TextAppearance.AppCompat.Moment.Date"
        tools:text="Name the "/>

EDIT :

The above mechanism does not work in case you delete any char or your edit text is multiline, the below should work :

 private fun handleTagging(text: CharSequence, start: Int, lengthBefore: Int, lengthAfter: Int) {
    if (!enableTagging || text.length <= start) return

    val formattedText = text.substring(0, start).trim();
    val lastWord =  formattedText.split(Regex("\\s+")).last()
    val tagIndex = if (lastWord.isNotEmpty() && lastWord[0] == '#') formattedText.lastIndexOf('#') else -1

    if (tagIndex >= 0) {
        val foregroundSpan = ForegroundColorSpan(ContextCompat.getColor(context, R.color.colorPrimary))
        getText()?.setSpan(foregroundSpan, tagIndex, start + 1, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)
    }
}

Or if you like extensions as I do, this can be refactored as

fun CharSequence.getTagIndices(toIndex:Int = 0):Pair<Int, Int>? {
    val formattedText = substring(0, toIndex).trim()
    val lastWord =  formattedText.split(Regex("\\s+")).last().trim()
    if (!lastWord.startsWith('#') || lastWord.endsWith('#')) return null
    val startIndex = formattedText.lastIndexOf('#')
    val endIndex = formattedText.length - 1
    if (startIndex < 0 || endIndex < 0) return null
    return Pair(startIndex, endIndex)
}

 private fun handleTagging(text: CharSequence, start: Int, lengthBefore: Int, lengthAfter: Int) {
        if (!enableTagging || text.length <= start) return

        text.getTagIndices(start + 1)?.let {
            val foregroundSpan = ForegroundColorSpan(ContextCompat.getColor(context, R.color.colorPrimary))
            getText()?.setSpan(foregroundSpan, it.first, it.second + 1, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)
        }
    }
Tasneem
  • 793
  • 8
  • 24
  • EditTextView is not getting focused. Cursor blinks when user taps on it and then disappears after that. – BST Kaal Oct 03 '19 at 09:10
0

I have tried all the above method mentioned by others. Nothing worked properly. But it worked properly

mSpannable = binding?.captionLayout?.etInput?.text

    binding?.captionLayout?.etInput?.addTextChangedListener(object : TextWatcher{
        override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {}
        override fun onTextChanged(short_text: CharSequence, start: Int, before: Int, count: Int) {

            val text = binding?.captionLayout?.etInput?.text.toString()
            var last_index = 0
            text.trim().split(Regex("\\s+")).forEach {
             
                if (it.isNotEmpty() && it[0] == '#'){
                    //here you can style the hashtag text(I have made it bold and black)
                    val boldSpan = StyleSpan(Typeface
                        .BOLD)
                    val foregroundSpan = ForegroundColorSpan(ContextCompat.getColor(requireActivity(), R.color.black))
                    mSpannable?.setSpan(foregroundSpan, text.indexOf(it,startIndex = last_index), text.indexOf(it,startIndex = last_index)+it.length, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)
                    mSpannable?.setSpan(boldSpan, text.indexOf(it,startIndex = last_index), text.indexOf(it,startIndex = last_index)+it.length, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)

                }

                last_index+=it.length-1

            }
       
        }
        override fun afterTextChanged(s: Editable?) {}
    })
sum20156
  • 646
  • 1
  • 7
  • 19
0

i know this is old but here is for new people same as @sum20156 did but with fixes

private class PostTextHashTag implements TextWatcher {
    @Override
    public void onTextChanged(CharSequence s, int start, int before, int count) {
        char startChar = 0;
        Log.i(getClass().getSimpleName(), s.length() + "");
        try {
            startChar = s.charAt(start);
            Log.i(getClass().getSimpleName(), "CHARACTER OF NEW WORD: " + startChar);
        } catch (Exception ex) {
            startChar = 0;
        }

        boolean isHashTagChar = startChar == '#';
        if (isHashTagChar) {
            changeTheColor(s.toString().substring(start), start, start + count, R.color.HASH_TAG);
            hashTagIsComing++;
        }

        if (isAllowedChar(startChar)) {
            hashTagIsComing = 0;
        }


        if (s.length() > 1) {
            char previousChar = s.charAt(start - 1);
            // DoubleHashTag fix and new line
            if (previousChar == '#' && (isHashTagChar || startChar == '\n')) {
                changeTheColor(s.toString().substring(start), start - 1, start + count, R.color.PRIMARY_TEXT);
            }
        }
        if (hashTagIsComing != 0) {
            changeTheColor(s.toString().substring(start), start, start + count, R.color.HASH_TAG);
            hashTagIsComing++;
        }
    }

public static boolean isAllowedChar(char c) {
    boolean isHashTagChar = c == '#';
    boolean isDigit = Character.isDigit(c);
    boolean isLetter = Character.isLetter(c);
    boolean isUnderscore = c == '_';
    return !isUnderscore && !isDigit && !isLetter && !isHashTagChar;
}

private void changeTheColor(String s, int start, int end, int colorId) {
    mspanable.setSpan(new ForegroundColorSpan(ContextCompat.getColor(getApplicationContext(), colorId)), start, end, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
}
NoXSaeeD
  • 924
  • 1
  • 7
  • 13