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