On a Fragment for user logging, I have two EditText widgets - one for email and the other for password. When the user has saved its credentials via SharedPreferences, then these are obtained to fill the input fields programmatically via setText() like this:
// obtain the credentials
val email : String? = loginFragmentViewModel.retrieveEmailFromSharedPreferences()
val password: String ? = loginFragmentViewModel.retrivePasswordFromSharedPreferences()
email?.let { emailString->
password?.let{ pw ->
// fill the EditText inputs programmatically
binding.apply{
loginEmail.setText(emailString)
loginPassword.setText(pw)
}
// validate the input data
loginFragmentViewModel.loginDataChanged(
binding.loginEmail.text.toString(),
binding.loginPassword.text.toString()
)
}
}
The loginDataChanged() method is implemented on the ViewModel side. It validates the given inputs (here: email & password). Here the relevant parts:
private val _loginState = MutableLiveData<LoginFormState>()
val loginFormState : LiveData<LoginFormState> = _loginState
fun loginDataChanged(email: String, password: String){
if(!isEmailValid(email)){
_loginState.value = LoginFormState(emailError = R.string.invalid_email)
}
else if(!isPasswordValid(password)){
_loginState.value = LoginFormState(passwordError = R.string.invalid_password)
}
else{
_loginState.value = LoginFormState(isDataValid = true)
}
}
As you can see, based on the validation result, we update the LiveData variable which leads us back to the LoginFragment side:
// listen for type errors made by the user during the login
loginFormState.observe(viewLifecycleOwner, Observer { loginFormState ->
...
...
// notify the user that the given email is wrong
loginFormState.emailError?.let{
binding.loginEmail.error = getString(it)
}
// notify the user that the given password is wrong
loginFormState.passwordError?.let{
binding.loginPassword.error = getString(it)
}
})
As you can see, when there is any error with an input, I let the user know about via the error property of EditText widget. They show an exclamation mark, notifying the user that the input is not correct.
What my problem is:
But that does not work when the inputs are set programmatically like you have seen above. After setting them like this, you can still see the exclamation mark on the right side of the EditText widgets ALTHOUGH the given email and password are correct. When the correct email and password are obtained from SharedPreferences and the EditText fields are filled, then it still looks like as they are wrong. But they are not.
So, how can I solve this problem?
Note: I obtain the SharedPreferences so that the user does not have to re-type them.
Related
I am getting an an error when I try to display 2 input views.
They are captured in one fragment screen and then I try to display them on a second fragment screen.
The navigation works but they display as resources?... i do not even know what it is called.
This is the setOnClickListener that calls up that screen. I suspect it has something to go with safeArgs
binding.buttonConfirm.setOnClickListener{
val username = binding.editTextUsername.toString()
val password = binding.editTextPassword.toString()
val action = LoginFragmentDirections.actionLoginFragmentToWelcomeFragment(username, password) // this is generated class due to nav graph
findNavController().navigate(action)
}
in Debugger i added some watches
I am a newbie to Android, and to stackoverflow as a poster.
Thanks for any help
You're calling toString() on the EditText and not actually getting the text the user has entered - to do that, you need to call getText() which in Kotlin code, you can do via the text property:
val username = binding.editTextUsername.text.toString()
val password = binding.editTextPassword.text.toString()
val action = LoginFragmentDirections.actionLoginFragmentToWelcomeFragment(username, password) // this is generated class due to nav graph
findNavController().navigate(action)
You are taking the view itself instead of its value. To do so, grab the value, not the view.
binding.buttonConfirm.setOnClickListener{
val username = binding.editTextUsername.text.toString()
val password = binding.editTextPassword.text.toString()
val action = LoginFragmentDirections.actionLoginFragmentToWelcomeFragment(username, password) // this is generated class due to nav graph
findNavController().navigate(action)
}
Alternatively, you can pass data between destinations with Bundle objects.
Create a Bundle object and pass it to the destination using navigate(), as shown below:
val bundle = Bundle()
bundle.putString("username", binding.editTextUserName.text.toString())
bundle.putString("password", binding.editTextPassword.text.toString())
view.findNavController().navigate(R.id.actionLoginFragmentToWelcomeFragment, bundle)
In your receiving destination’s code, use the getArguments() method to retrieve the Bundle and use its contents:
binding.textViewUserName.text = arguments?.getString("username")
binding.textViewPassword.text =arguments?.getString("password")
Is there a way to use EditText to underline errors in the text as the user enters it? Or an alternative control that allows that kind of formatting?
I'm trying to process text as a user is entering it. For example, if the user enters
The current time is 80:30 at night.
I'd like to underline 80:30 so that it's obvious right away that they've made an error.
I'd even settle for a way to show a little caret or something underneath so that they can spot the location of the error.
This is what I settled on.
checkText() gets called from doAfterTextChanged and when focus is lost, to clear the existing formatting.
The ViewModel then parses the text; it receives addValidityStyleToRange as a parameter and uses it to apply formatting as it goes.
private fun checkText() {
clearValidityStyles()
viewModel.parseText(binding.edittext.text.toString(), ::addValidityStyleToRange)
}
/**
* Clears the styles used for valid and invalid regions of edittext.
*/
private fun clearValidityStyles() {
val text = binding.edittext.text
text.getSpans(0, text.length, UnderlineSpan::class.java).forEach { text.removeSpan(it) }
text.getSpans(0, text.length, StyleSpan::class.java).forEach { text.removeSpan(it) }
}
/**
* Applies the valid or invalid style to a range of edittext.
* Passed as a callback to the ViewModel that validates the input.
*/
private fun addValidityStyleToRange(range : IntRange, isValid : Boolean) {
val text = binding.edittext.text
val style = if (isValid) StyleSpan(Typeface.BOLD_ITALIC) else UnderlineSpan()
text.setSpan(style, range.first, range.last, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)
}
I have a class AccessibilityDataService and when there is a certain event starts an activity
When onAccessibilityEvent is called it takes data and sends data to the interactor(in the code below)
I use AccessibilityEvent.TYPE_VIEW_TEXT_CHANGED
It's taking data line by line and sends it to the interactor but I want the whole data to be sent at once:
#Inject lateinit var interactor: InteractorAccessibilityData
override fun onAccessibilityEvent(event: AccessibilityEvent) {
when (event.eventType) {
AccessibilityEvent.TYPE_VIEW_TEXT_CHANGED -> {
val data = event.text.toString()
if (data != "[]") {
interactor.setDataKey("${getDateTime()} |(TEXT)| $data")
Log.i(TAG, "${getDateTime()} |(TEXT)| $data")
}
}
}
}
and code for interactor is here:
override fun setDataKey(data: String) {
if (firebase.getUser()!=null) firebase.getDatabaseReference(KEYS).child(DATA).push().child(TEXT).setValue(data)
}
and the data saved in Firbase is like -> This is how data looks in firebase :
Image
But I want that data in only this form -> This is how I want data in Firebase:
Image
The onAccessibilityEvent(AccessibilityEvent event) method is triggered any time an "event" takes place. If the event is of type TYPE_VIEW_TEXT_CHANGED, it means that it:
Represents the event of changing the text of an EditText.
So every time you change the content of the EditText by typing a new character, this method fires, in terms it calls "setDataKey()", which actually writes the newly typed character to the database. That's the reason why you have a record in the database for each and every character you type.
If you want to have a single record that contains all the data that you have typed in the EditText, then you should add a button, attach a click listener, on once the button is clicked, get the content of the EditText and write it to Firebase. There is no need to listen for changes.
Maybe try storing the inputs in a variable and send it to firebase ONLY when next interaction takes place, for example: EditText is not focused or when some button is pressed - only then send the string from variable to firebase
I don't know, I'm just trying to use some logic
Something like this probably ->
var tempData: String
if (data != "[]") {
tempData = tempData + data
Log.i(TAG, "${getDateTime()} |(TEXT)| $temData")}
put your interactor.setDataKey("${getDateTime()} |(TEXT)| $data")
out of the If
and after user clicks some button or stop focusing EditText, then add your
data = tempData
interactor.setDataKey("${getDateTime()} |(TEXT)| $data")
Or, in your case, keep storing data to variable and send data to firebase after 1 minute of interaction with keyboard or if "enter" is pressed (if it's possible), it's not perfect, but it will look better then t.th.thi.this
I have a LiveData<List> which needs to return accounts based on search query, if no query return all accounts. Here is my code, I have used in my ViewModel class, but upon initialisation no accounts are shown, only when i change searchTerm does it start to show results, and on making search term empty also shows results.
val searchTerm = MutableLiveData<String>()
val accounts : LiveData<List<AccountModel>> = Transformations.switchMap(searchTerm){
if(it.isNullOrEmpty()){
Transformations.map(accountRepository.accountDAO.getAccounts()){
it.toDomainModel()
}
}else{
Transformations.map(accountRepository.accountDAO.getSearchedList(it)){
it.toDomainModel()
}
}
}
Can someone guide me with what am I doing wrong here.
Thanks
You should use initial value. Your switchMap isn't triggered before your searhTerm gets the first value.
val searchTerm = MutableLiveData<String>("")
I have a basic survey app that emails results upon hitting the submit button at the end. In the string that is built it calls the result of the checked radio button to put into the email, but it's showing as an integer instead of the button text.
Currently:
val id = group1.checkedRadioButtonId
returns 213120809 or 2131230810 depending on which response is checked
I've tried to add getString() to it so like so:
val id = group1.checkedRadioButtonId.getString()
but that doesn't seem to do anything. Does anyone know if there's another command I should be using? This is in Kotlin.
There is no property to get selected button text.
You can get it like this
val radioButtonText = when (group1.checkedRadioButtonId) {
R.id.radioButton1 -> radioButton1.text
else -> radioButton2.text
}