How to validate EditText input in custom view - android

I'm trying to make a custom view of an EditText with a TextView under it which says the error of the validation.
My idea is to make a validation of min and max length in my custom view and make visible or change text the TextView if the edittext input passes this validations or not, but I'm new at custom views and don't have any idea of how to reach it. I didn't find any EditText custom view like this.
Can someone help me?

#1 if you want only limit your edit text
get in your layout and set android:maxLength="5"
<EditText
android:id="#+id/you_ed"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:ems="10"
android:inputType="textPersonName"
android:maxLength="5"
/>
#2 if you want check edit text with onClicklistener
button.setOnClickListener {
if (binding.youEd.text.length>5){
Toast.makeText(this, "error The input text is more than 5", Toast.LENGTH_SHORT).show()
}else{
//Execute the code if the input is correct
}
}
#3 If you want to check the input text momentarily (entry of each character from the user)
you_ed.addTextChangedListener(object :TextWatcher{
override fun beforeTextChanged(p0: CharSequence?, p1: Int, p2: Int, p3: Int) {
}
override fun onTextChanged(p0: CharSequence?, p1: Int, p2: Int, p3: Int) {
}
override fun afterTextChanged(text: Editable?) {
if (text!!.length>5){
Toast.makeText(this#IndexActivity, "error The input text is more than 5", Toast.LENGTH_SHORT).show()
}else{
//Execute the code if the input is correct
}
}
})

Related

How to use Inverse Binding Adapter in Kotlin for converting Lowercase text to Uppercase?

I am creating an android application where I want to use a feature in which a text that we have entered into an editText field can be converted into uppercase at runtime in that particular editText field only.
I have tried with this code
editText.addTextChangedListener(object : TextWatcher {
override fun afterTextChanged(s: Editable?) {
}
override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {
}
override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {
this.text.toString().uppercase()
}
})
But it can be easily done by the concept of Inverse Binding Adapter in android. I have tried to implement it with reference of
https://developer.android.com/reference/android/databinding/InverseBindingAdapter
It is not working for me in my project. Can You explain me with step by step explanation?
Yess This Method of addTextChangedListener is available but we have to implement this method for each and every Edittext we want to convert to Upper case. So you heard correct about the InverseBinding Adapter. In InverserBinding Adapter we have to create this method for one time and you can use it any number of time.
I have implemented this using BindingAdapter and InverseBinding Adapter.In one Kotlin File, Write this two functions as follows.
Function Code
#BindingAdapter(value = ["setText", "custom:AttrChanged"], requireAll = false)
fun EditText.updateText(text: String?, listener: InverseBindingListener) {
if (this.text.toString() != text) {
this.setText(text)
}
this.doOnTextChanged { _: CharSequence?, _: Int?, _: Int?, _: Int? ->
listener.onChange()
}
}
#InverseBindingAdapter(attribute = "setText", event = "custom:AttrChanged")
fun EditText.getUpdatedText(): String? {
return this.text.toString().uppercase()
}
For Upper Case I have created one uppercase variable of MutableLiveData of type String
var uppercase = MutableLiveData("")
Now in XML i have set that property as follow:
<androidx.appcompat.widget.AppCompatEditText
android:id="#+id/edit_txt"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="30dp"
setText="#={viewModels.uppercase}"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#id/btn_login_data_binding" />

How to forbid "00" or "0123-like" start of input in Android EditText, in Kotlin?

I have an EditText object with the numberDecimal input type. The input manipulation (e.g., calculation) is working ok, but inputs starting with "00" and "0123-like" (zero followed by a number, not the dot) are currently permitted.
How can I forbid inputs starting with "00" or "0123-like" (zero followed by a number, not the dot), in Kotlin?
Aftering googling and reading many similar questions and answers, I've tried TextWatcher and InputFilter, but can't really figure out a solution.
You can use a TextWatcher and regular expression if the modified string is in the format 0, followed by a number, you can remove the last inserted digit and return to a string containing only the initial 0.
editText.addTextChangedListener(object : TextWatcher {
override fun afterTextChanged(s: Editable?) {
val newText = s.toString().trim()
// Check if the text is 0 followed by another digit, in that case remove the last inserted value
if (newText.length > 1 && newText.matches("^0[\\d]+".toRegex())) {
// Unwanted expression: remove last inserted char and reset the cursor position to show after the first character
editText.setText("0")
editText.setSelection(1)
}
}
override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {}
override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {}
})
EDIT
If you need to apply the same TextWatcher to multiple EditText you could define your CustomTextWatcher class:
class CustomTextWatcher(
private val editText: EditText
) : 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?) {
val newText = s.toString().trim()
// Check if the text is 0 followed by another digit, in that case remove the last inserted value
if (newText.length > 1 && newText.matches("^0[\\d]+".toRegex())) {
// Unwanted expression: remove last inserted char and reset the cursor position to show after the first character
editText.setText("0")
editText.setSelection(1)
}
}
}
And then add it to your EditTexts with:
editText1.addTextChangedListener(CustomTextWatcher(editText1));
editText2.addTextChangedListener(CustomTextWatcher(editText2));
...

Make few prefilled characters uneditable in EditText using Android

I am working on EditText where I am displaying pre filled country code in start lets say "+971", what I want is that if user trying to click and edit in EditText, he should not edit or delete "+971" and can only be able to put number after that like "+971 123445579" and if trying to delete number then "+971" should not get delete.
My code is given below, please guide me how can I achieve this. Thanks
private fun setupTextChangeListener() {
edit_text.textWatcherListener = object : TextWatcher {
override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {
}
override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {
setDatePickerIconOnDemand()
}
override fun afterTextChanged(s: Editable?) {
}
}
}
// In this method I am putting Start Drawable in EditText:
private fun setDatePickerIconOnDemand() {
edit_text.setDrawableStart(context.drawable(R.drawable.ic_earh_arrow_down_default))
edit_text.setDrawable()
}
You can add following lines of code in on onTextChanged callback
if (edit_text.length() < 5 || !edit_text.text!!.startsWith("+971 ")) {
edit_text.setText("+971 ")
edit_text.setSelection(edit_text.length())
}
What it will do is: It will check onTextChanged 'if your current text in edit text is only your country code, Set the country code in your editText and move cursor at the end.

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.

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