How to hashtag value in different colour in EditTe

2020-02-07 05:08发布

问题:

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>

回答1:

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);
}


回答2:

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)
        }
    }