Material EditText "password_toggle" Icon Visible without focus/data - Android - android

I want to hide this Icon of password_toggle when EditText is Blank. Only show this icon when Any data inside this.
Like: clear_text icon work inside Name Section.
I am using app:endIconMode="password_toggle" inside TextInputLayout
<com.google.android.material.textfield.TextInputLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
style="#style/Widget.MaterialComponents.TextInputLayout.OutlinedBox"
app:endIconMode="password_toggle"
android:hint="Password">
<com.google.android.material.textfield.TextInputEditText
android:id="#+id/edittextPassword"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:inputType="textPassword"
/>
</com.google.android.material.textfield.TextInputLayout>

You can achieve what you want using a TextWatcher.
If you use Android KTX via implementation "androidx.core:core-ktx:1.9.0"
You will get access to a convenient extension method you can use like this:
edittextPassword.doOnTextChanged { text, start, count, after ->
// if text is blank, hide end icon,
// otherwise, show password toggle
}
Reference: https://developer.android.com/reference/kotlin/androidx/core/widget/package-summary#(android.widget.TextView).doOnTextChanged(kotlin.Function4)

1- put this line on create()
binding.etPasswordLayout.isPasswordVisibilityToggleEnabled = false
2- setText Watcher like this
binding.edtPassword.addTextChangedListener(object : TextWatcher {
override fun beforeTextChanged(charSequence: CharSequence, i: Int,
i1: Int, i2: Int) {
binding.etPasswordLayout.isPasswordVisibilityToggleEnabled =
true
}
override fun onTextChanged(charSequence: CharSequence, i: Int, i1: Int, i2: Int) {}
override fun afterTextChanged(editable: Editable) {
binding.etPasswordLayout.isPasswordVisibilityToggleEnabled = false
}
})

Related

How can I change the background color of an EditText when text is entered using Kotlin in Android Studio?

Just to be clear, I want to change the color of the textField to be clear once text is entered and for it to go back to its old color, which in my case is gray when it has no text. I looked through stackoverflow and found some answers, however it was in Java and I am still a beginner with Kotlin, but I am writing this in Kotlin for my work.
Essentially, I want to know how can you change the background of the textField once text is entered?
I want to have the background color of that to change once text is entered using Kotlin.
This is my XML EditText that I am referring to:
<EditText
android:id="#+id/description"
android:layout_width="350dp"
android:layout_height="200dp"
android:layout_below="#id/addImage"
android:layout_alignBottom="#id/addImage"
android:layout_marginStart="50dp"
android:layout_marginTop="125dp"
android:layout_marginEnd="50dp"
android:layout_marginBottom="75dp"
android:background="#color/lightGray"
android:gravity="center"
android:hint="#string/description"
android:inputType="text"
app:layout_constraintBottom_toTopOf="#+id/bottomNavigationView"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
Where do I go from here now?
class PostActivity : AppCompatActivity() {
private lateinit var button: Button
private lateinit var imageView: ImageView
private lateinit var editText: EditText
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_post)
editText = findViewById(R.id.description)
}
}
This is just what I have on my activity. I don't have any method really on how to do this.
Add a TextWatcher to react when text has been changed.
val originalColor = (editText.background as ColorDrawable).color // or Color.LIGHT_GRAY
val haveTextColor = Color.TRANSPARENT // or whatever you want
editText.addTextChangedListener(object: TextWatcher {
override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) { }
override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) { }
override fun afterTextChanged(s: Editable) {
editText.setBackgroundColor(if (s.isEmpty()) originalColor else haveTextColor)
}
})
You can use the doAfterTextChanged Kotlin extension function here. It can be used to perform some action every time the text changes.
editText.doAfterTextChanged { text ->
editText.setBackgroundColor(if (text.isEmpty()) color1 else color2) // Choose whatever colors you want
}

How can I prevent a user from deleting pre-existing text in an EditText widget?

I have an EditText with some pre-existing text that MUST NOT be deleted. The user should only be allowed to append text in the view.
The solution I have right now can be seen below, where I check if the new length of the EditText is shorter than the original text. If it is, the user has attempted to delete a character so I just repopulate the view with the original text and move the cursor. The problem with this solution is that the user could just enter text that is longer than the original length, then change the position of the cursor to somewhere within the original text, and finally delete characters.
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
val originalText = "Hello"
binding.et.setText(originalText)
binding.et.addTextChangedListener(object : TextWatcher {
override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {}
override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {
s?.let {
if (s.length < originalText.length) {
binding.et.setText(originalText)
binding.et.setSelection(originalText.count())
}
}
}
override fun afterTextChanged(s: Editable?) {}
})
}
What should I do? I'm open to all ideas.
Thanks!
Edit:
I've also tried combining an EditText and a TextView (as some have suggested) but the layout is difficult to achieve because both the original text and the new text can span multiple lines. My solution above contains one word in the original text but it could be many. So I need to cater for this scenario:
...where the white box represents the TextView and the red box represents the EditText.
I don't think Bö macht Blau's answer will work either because the prefix is in its own "column".
I think I will have to use Zain's solution with (startsWith()) or create a custom view (though I've never done one of these before).
Please consider #Gabe Sechan advice in OP comments that the EditText alone is not the optimum solution for that.
In case you still need to use EditText then you can change the condition to check that the returned CharSequence of the EditText watcher starts with the original text using startsWith() method
et.addTextChangedListener(object : TextWatcher {
override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {}
override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {
s?.let {
if (!s.startsWith(originalText)) {
if (s.length <= originalText.length) {
et.setText(originalText)
et.setSelection(originalText.count())
} else {
et.setText(originalText + s.subSequence(originalText.length, s.length))
et.setSelection(s.count())
}
}
}
}
override fun afterTextChanged(s: Editable?) {}
})
You can use a text field, that's a combination of a TextInputEditText and a TextInputLayout. Then you can set a prefix like so:
<com.google.android.material.textfield.TextInputLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:prefixText="#string/prefix_text">
<com.google.android.material.textfield.TextInputEditText
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
</com.google.android.material.textfield.TextInputLayout>
The prefix will be visible as soon as the TextInputEditText gains focus. Since it is not part of the EditText content, it can't be modified by the users.
Useful links:
documentation for TextInputLayout
overview of style options
This check should work:
if (s.substring(0, 4) == originalText) {
// update text
}
But consider looking into InputFilter, as a comment suggested.

How to make a proper binding adapter for 2 way data binding in Android

I am attempting to make a page in my Android app that allows the user to enter a Float value in an EditText and binds that value to my view model. Here is my code for my current binding adapter:
import android.text.SpannableStringBuilder
import android.widget.EditText
import androidx.databinding.BindingAdapter
import androidx.databinding.InverseBindingAdapter
#BindingAdapter("android:text")
fun setText(view: EditText, value: Float?) {
view.text = value?.let { SpannableStringBuilder(value.toString()) } ?: SpannableStringBuilder("")
}
#InverseBindingAdapter(attribute = "android:text", event = "android:textAttrChanged")
fun getTextString(view: EditText): Float? {
val editTextString: String? = view.text?.toString()
return if(editTextString == null || editTextString == "") 0.0f else editTextString.toString().toFloat()
}
Here is the code that I have for my EditText in my layout file:
<com.google.android.material.textfield.TextInputLayout
android:id="#+id/amount_edit_text_outer"
style="#style/Widget.MaterialComponents.TextInputLayout.OutlinedBox"
android:layout_width="#dimen/edit_text_width"
android:layout_height="#dimen/edit_text_height"
android:layout_gravity="center"
android:hint="#string/edit_text_hint"
app:layout_constraintBottom_toTopOf="#+id/fab"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
<com.google.android.material.textfield.TextInputEditText
android:id="#+id/amount_edit_text_inner"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:text="#={viewmodel.liveAmount$app_debug}"
android:inputType="numberDecimal" />
</com.google.android.material.textfield.TextInputLayout>
Finally, my view model live data variable looks like this:
internal val liveAmount = MutableLiveData<Float>(0.0f)
I should also add that when I run this code on my phone, it allows me to enter the first number in the EditText and nothing else. Furthermore, the keyboard does not go away unless I kill the app. Any questions or comments are welcome. Thank you all for any help!
Try changing this:
#InverseBindingAdapter(attribute = "android:text", event = "android:textAttrChanged")
to:
#InverseBindingAdapter(attribute = "android:text")
And add this other method:
// This notifies the data binding system that the attribute value has changed.
#BindingAdapter("android:textAttrChanged")
fun setTextChangedListener(editText: TextInputEditText, listener: InverseBindingListener) {
editText.addTextChangedListener(object: TextWatcher{
override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {
}
override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {
}
override fun afterTextChanged(s: Editable?) {
listener.onChange()
}
})
}
This is not tested, but it should work or at least be close. See here for other similar examples:
https://github.com/gavingt/upcoming-games/blob/adc8b7e67b1e05d7bc9ac6247b82cfa93a43a26f/app/src/main/java/com/gavinsappcreations/upcominggames/utilities/BindingAdapters.kt#L324

Two-way binding Android on edit text

I want to set up the two-way binding for Edit Text. But getting an error so far.
Could not find event 'textChangeAttrChanged' on View type 'android.widget.EditText'
This is a pretty straightforward scenario but never seen a good working example.
Part of binding adapters:
#BindingAdapter("textChange")
#JvmStatic fun setText(view: EditText, value: String) {
if (view.text.toString() != value) {
view.setText(value)
}
}
#BindingAdapter("textChangeAttrChanged")
#JvmStatic fun setTextListener(view: EditText, onTextChange: ITextChange) {
val textWatcher : TextWatcher = object : TextWatcher {
override fun afterTextChanged(s: Editable) {
onTextChange.onTextChanged(view.text.toString())
}
override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) {}
override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) {}
}
view.addTextChangedListener(textWatcher)
}
#InverseBindingAdapter(attribute = "textChange")
#JvmStatic fun getText(editText: EditText): String {
return editText.text.toString()
}
And from XML:
<EditText
android:id="#+id/et_title_input"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:ems="10"
android:hint="#string/input_address"
android:inputType="textPersonName"
android:textColorHint="#color/colorGray"
textChange="#={viewModel.searchKeyword}"
textChangeAttrChanged="#{(text) -> viewModel.onSearchEntered(text)" // adding or removing this line doesn't give a thing
tools:ignore="Autofill" />
This is not the correct way to use inverse databinding for a view model.
First, EditText already supports two-way databinding via the databinding library, so you don't have to do this on your own.
Second, you only need the inverse binding adapter stuff if you want to set up a custom view for databinding. In your case, you just want an existing view that is already set up for databinding to update your view moodel.
By using the "#={viewModel.searchKeyword}" notation, you are indicating that you have a property called "searchKeyword" that has both a getter and a setter and that you want the databinding library to invoke the setter with the value from the view when it changes.
Thus, all you should need to do is implement your logic in your property setter. Something like this:
#Bindable var searchKeyord : String? = null
set(value) {
if (field != value) {
field = value
onSearchEntered(value)
notifyPropertyChanged(BR.searchKeyword)
}
}
Please review the databinding documentation for more info.
Hope that helps!

Realm changing values from edittext

I have the following problem:
For my current Android app project I'm using Realm as a database and im trying to update a string value every time my a certain textfield changes with the value thats been given to the textview. Problem is though, whenever the value of the object gets changed, the value of the textfield buggs out with previously set characters and changes back. The item that I change is also inside my viewholder. Any ideas?
Heres my code
The layout
<com.google.android.material.textfield.TextInputLayout
android:id="#+id/entry_detail_form_8_til_text_field_container"
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_marginTop="8dp"
style="#style/Widget.MaterialComponents.TextInputLayout.OutlinedBox"
app:hintEnabled="true"
app:layout_constraintTop_toBottomOf="#id/entry_detail_form_8_title"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent">
<EditText
android:id="#+id/entry_detail_form_8_et_text_field"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
style="#style/Widget.MaterialComponents.TextInputEditText.OutlinedBox"/>
</com.google.android.material.textfield.TextInputLayout>
and the logic
holder.etTextArea?.addTextChangedListener(object : TextWatcher{
override fun afterTextChanged(p0: Editable?) {
}
override fun beforeTextChanged(p0: CharSequence?, p1: Int, p2: Int, p3: Int) {
}
override fun onTextChanged(p0: CharSequence?, p1: Int, p2: Int, p3: Int) {
realm.executeTransaction {
holder.fieldObject?.value = p0.toString()
}
}
})
Edit: The following actions did not solve the problem:
Extracting the change realm object logic into a callback outside the adapter
Moving the logic into afterTextChanged
Fixed it by disabling autoUpdate in Adapter of my RecyclerView, seems like somehow the value of my edit text was connected with the field i was changing with the new text

Categories

Resources