I am trying to make it so that a progress bar that I put in my edittext field will spin after the user has typed in text. The progress bar should continue to spin until the user has not typed in any more text for one second. (If the user keeps typing in text, the progress bar should keep spinning.) I am using a textWatcher:
et_username_username.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) {
pb_username_progress_bar.visibility = View.VISIBLE
val updatedText = p0.toString().trim().toLowerCase()
CoroutineScope(IO).launch{
delay(1000)
pb_username_progress_bar.visibitlity = View.INVISIBLE
//updates UI based on firebase database code
}
}
})
However, when I run this, the coroutine just runs on a different thread every time so the progress bar goes away when the user keeps typing for over a second.
How do I make it so that the progress bar will stay there when the user keeps typing, are coroutines the best way or is there another way??
(It should look a lot like instagram when you create a new account, how it has the spinning progress bar when making username)
You can use following code snippet as solution. It will solve your problem.
edit_search.addTextChangedListener(object : TextWatcher {
private var searchFor = ""
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) {
val searchText = s.toString().trim()
if (searchText == searchFor)
return
searchFor = searchText
progress_bar.visibility = View.VISIBLE
CoroutineScope(IO).launch {
delay(1000)
if (searchText != searchFor)
return#launch
progress_bar.visibility = View.INVISIBLE
}
}
})
Happy Coding :)
Related
In my application I want use EditText and I want after user write on keyboard add suffix text into EditText.
I write below codes, but when user write on keyboard application has freeze!
loginPage4SelectTxt.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(p0: Editable?) {
loginPage4SelectTxt.setText("$p0 Kg")
}
})
How can I fix it?
Whenever the contents of loginPage4SelectTxt changes, those *Changed callbacks fire in order. What are you doing in the last one?
You're changing the contents, which makes the whole thing start over, which makes the whole things start over, forever and ever. You have to be careful about making changes to an EditText inside its own TextWatcher, or you can end up in an infinite loop
It is legitimate to make further changes to s from this callback, but be careful not to get yourself into an infinite loop, because any changes you make will cause this method to be called again recursively.
So you need a way to ignore the changes you're causing with your "update", and only respond to events triggered by actual text entry. You can do that with an updating flag (or whatever you want to call it):
loginPage4SelectTxt.addTextChangedListener(object : TextWatcher {
// use this to track whether an update is in progress,
// so we can avoid retriggering it
var updating = false
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(p0: Editable?) {
// skip if an update is happening - this avoids the infinite loop
if (!updating) {
// we're doing an update - set the flag so we don't end up
// back in here
updating = true
// this will trigger the TextWatcher callbacks -immediately-, this code
// won't continue until those are finished
loginPage4SelectTxt.setText("$p0 Kg")
// text is changed and the resulting callbacks have finished - we're done
// updating, so set it to false so the next change will be handled properly
updating = false
}
}
})
I have a search bar, after searching in the search bar, if I click back button the app closes. What I want is if I click back button after searching in search bar the default list must be shown.
activity?.onBackPressedDispatcher?.addCallback(this, object : OnBackPressedCallback(true) {
override fun handleOnBackPressed() {
if(){
}else{
isEnabled = false
activity?.onBackPressed()
}
}
})
searchBar.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) {
val listData = arrayListOf<Restaurant>()
for (i in 0 until restaurantInfoList.size) {
if (restaurantInfoList[i].restaurantName.toLowerCase()
.contains(searchBar.text.toString().toLowerCase()) ||
restaurantInfoList[i].restaurantPrice.contains(searchBar.text.toString()) ||
restaurantInfoList[i].restaurantRating.contains(searchBar.text.toString())
) {
listData.add(restaurantInfoList[i])
}
}
recyclerDashboard.adapter = RestaurantRecycleAdapter(activity as Context, listData)
(recyclerDashboard.adapter as RestaurantRecycleAdapter).notifyDataSetChanged()
}
}
)
The app is closing because you have called the activity's onbackPressed() method. Unless you handle the callback on your own, it will close.
In your backpressed method, check if your search bar is empty and produce the default list in the adapter.
Remove this line in else condition
activity?.onBackPressed()
I'm currently learning android in Kotlin, and I'm trying to create a tip calculator. I have two EditText views, one for the bill amount and tip amount. I also have two TextViews, one for the tip amount and one for the total + tip.
This is part of my code, where I am trying to have the EditText automatically update the TextViews upon user input.
billInput.addTextChangedListener(object: TextWatcher) {
override fun afterTextChanged(s: Editable) {
totalAmt.text = billInput + tipInput
}
override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) {
}
override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) {
}
}
For now, I am trying to have the bill + tip amount be added and update the TextView. I got the error "Expecting a class body" next to the first line. Am I using addTextChangedListener incorrectly or is something wrong with my code? I know there are probably more efficient ways to do this, but I was assuming I could just use addTextChangedListener for both EditText views. So, to clear things up: I want to input my bill amount and tip amount, and automatically update "Tip amount:" and "Total:" .
Edit*** : Would it make sense to do the numeric calculations (tip and total amount) under onTextChanged? That way as the input is received, the calculations are processed and afterTextChanged is responsible for displaying the final results?
You are adding EditText + EditText, should add values instead
billInput.addTextChangedListener(object: TextWatcher) {
override fun afterTextChanged(s: Editable) {
totalAmt.text = "${billInput.text.toString().toInt() + tipInput.text.toString().toInt()}"
}
override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) {
}
override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) {
}
}
Hello my friend you can set addTextChangedListener for both EditTexts and put same code for both of them because one person cant simultaneously type in both EditTexts.
do the code like below.
bill Input Edittext
billInput!!.addTextChangedListener(object : TextWatcher {
override fun afterTextChanged(editable: Editable) {
//code after changed
val bill = editable.toString().toInt()
val tip = tipInput!!.text.toString().toInt()
total.text = ((bill + tip).toString())
}
override fun beforeTextChanged(charSequence: CharSequence, i: Int, i1: Int, i2: Int) {
//code before text change
}
override fun onTextChanged(charSequence: CharSequence, i: Int, i1: Int, i2: Int) {
//code during change
}
})
tip Input EditText
tipInput!!.addTextChangedListener(object : TextWatcher {
override fun afterTextChanged(editable: Editable) {
//code after changed
val bill = editable.toString().toInt()
val tip = billInput!!.text.toString().toInt()
total.text = ((bill + tip).toString())
}
override fun beforeTextChanged(charSequence: CharSequence, i: Int, i1: Int, i2: Int) {
//code before text change
}
override fun onTextChanged(charSequence: CharSequence, i: Int, i1: Int, i2: Int) {
//code during change
}
})
I am using a tag system similar to SO, and instead of forcing the user to find the dash in their keyboard I want space to automatically be translated to a dash.
I am trying to implement it with this textWatcher, but the app doesn't let me type the space bar (it kinda flashes but nothing happens.
imageTagsInput.addTextChangedListener(object : TextWatcher {
override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {
imageTagsInput.removeTextChangedListener(this)
imageTagsInput.setText(imageTagsInput.text.toString().replace(" ","-"))
imageTagsInput.addTextChangedListener(this)
imageTagsInput.setSelection(imageTagsInput.length())
}
override fun afterTextChanged(s: Editable?) {
}
override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {
}
})
And this is the xml of the EditText:
android:digits="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789- "
Add something like this in afterTextChanged
String result = s.toString().replaceAll(" ", "-");
if (!s.toString().equals(result)) {
ed.setText(result);
ed.setSelection(result.length());
// alert the user
}
For more info see this answer
I have a custom adapter, and filter that I am currently implementing to filter a recycler view based on a simple substring search on my recycler view entries. Here is my adapter NotifyChanged() function, which updates the RecylerView, and my custom filter() function. Everything works great, except for the auto scrolling afterwards.
private fun notifyChanged() {
val result = DiffUtil.calculateDiff(object : DiffUtil.Callback() {
override fun getOldListSize(): Int {
return objects.size
}
override fun getNewListSize(): Int {
return temp.size
}
override fun areItemsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean {
return this#DiffRecyclerViewAdapter.areItemsTheSame(objects[oldItemPosition], temp[newItemPosition])
}
override fun areContentsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean {
return this#DiffRecyclerViewAdapter.areContentsTheSame(objects[oldItemPosition], temp[newItemPosition])
}
})
objects.clear()
objects.addAll(temp)
result.dispatchUpdatesTo(this)
}
fun filter(text : String){
val ob = original_objects as ArrayList<Category>
val filtered_categories = ArrayList<T>() as ArrayList<Category>
for (category in ob){
//val temp_category = category
val list_of_subcategories = ArrayList<T>() as ArrayList<Category>
for (subcategory in category.categories){
val name_of_category = subcategory.name.toLowerCase()
if (name_of_category.contains(text)){
list_of_subcategories?.add(subcategory)
}
}
if (list_of_subcategories.size > 0){
val newCategory = Category(category.id,category.name,category.description,category.videos,list_of_subcategories)
filtered_categories.add(newCategory)
}
}
temp = filtered_categories as MutableList<T>
notifyChanged()
}
In my SearchActivity.kt I have the following listener:
searchEditText.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) {
adapter.filter(s.toString())
recyclerView.scrollToPosition(0)
}
})
I was looking through the source code of DiffUtil and notifyDataSetChanged() to see how scrolling after filtering works. But didn't have much luck. The whole problem is that after I search text the RecyclerView is filtered fine. But will scroll to inconsistent locations. I want it to scroll back to the top every single time, but this isn't happening. EVEN WITH scrollToPosition(0) it will USUALLY scroll to the top, but not always.
I thought scrolling to the top was typically automatic in this case. I'm curious as to what the best practice is in updating and scrolling.
It needs some time to update the data on recyclerview. Mean while you are trying to scroll which doesn't work in most of the cases. PReferably use a postDelayed with 200 or 300 milliseconds before scrolling
Ex:
new Handler.postDelayed(new Runnable(){
#Override
public void run(){
recyclerView.scrollToPosition(0)
}
}, 300);