AutoCompleteTextview with Textwatcher android - android

I have a small issue with AutoCompleteTextview with
textwatcher.
I Have AutoCompleteTextview for Suggestion.The data Comes From API side. With every new word I write in, API is being called and, it gets Response from ApI side and then, The show Dropdown menu for suggestion is shown.So basically that code is in AddTextWatcher Listener.
But when I click on Particular Suggestion, the AddTextwatcher is being Call and it recalls the ApI and the suggestions are shown again!So it is like
-Write in AutoCompleteTextview >> Api is being Called >> Fill in the Data >> Show Suggestions >> and Click On Suggestion >> Show Suggestions again.
Help me with this particular problem.
Thanks in Advance.

Basically its not hard.
This Post save my day.Thanks for this Post.
http://blog.hackerkernel.com/2015/12/30/autocompleteedittext-with-dynamic-data-from-json/

Decclare variable :
private var textWatcher : TextWatcher? = null
private var selectedItemId: String? = null
Initialize the declared variable and apply text watcher as :
textWatcher = object : TextWatcher {
override fun afterTextChanged(char: Editable?) { }
override fun beforeTextChanged(char: CharSequence?, p1: Int, p2: Int, p3: Int) {}
override fun onTextChanged(char: CharSequence?, p1: Int, p2: Int, p3: Int) {
if(selectedItemId!=null){
return
}
callApi()
}
}
When the response is received from API :
val adapter = ArrayAdapter(requireActivity(),
R.layout.custom_spinner_item, R.id.item_text_view, itemList!!)
mBinding.AutoCompleteTextview.setAdapter(adapter )
mBinding.AutoCompleteTextview.error = null
mBinding.AutoCompleteTextview.removeTextChangedListener(textWatcher)
mBinding.AutoCompleteTextview.showDropDown()
mBinding.AutoCompleteTextview.setOnItemClickListener { adapterView, view, position, l ->
selectedItemId = adapter.getItem(position)!!.id!!.toString()
mBinding.AutoCompleteTextview.clearFocus()
}
Final step :
mBinding.AutoCompleteTextview.setOnFocusChangeListener { view, b ->
if (b){
mBinding.AutoCompleteTextview.addTextChangedListener(textWatcher)
}
}

Related

How to return value from function in Kotlin Android

In my application I want used extension function and I want return String!
I set return value from function and I write below codes, but show me empty value!
My Extension function code :
fun Spinner.setupListWithAdapter(list: MutableList<String>): String {
var itemSelected = ""
val adapter = ArrayAdapter(context, android.R.layout.simple_spinner_item, list)
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item)
this.adapter = adapter
this.onItemSelectedListener = object : AdapterView.OnItemSelectedListener {
override fun onItemSelected(p0: AdapterView<*>?, p1: View?, p2: Int, p3: Long) {
itemSelected = list[p2]
Log.e("categoryLog","1 : " + itemSelected)
}
override fun onNothingSelected(p0: AdapterView<*>?) {
}
}
Log.e("categoryLog","2 : " +itemSelected)
return itemSelected
}
When show logs in logcat, first show 2 then show 1 !
Logs :
2022-08-12 14:49:59.261 12074-12074/com.my.app E/categoryLog: 2 :
2022-08-12 14:49:59.310 12074-12074/com.my.app E/categoryLog: 1 : Movies
Why first call log 2 then call log 1 ?!
I used this func in fragment and for this when used this in fragment show me empty value!
How can I fix it?
Change your extension function like below
fun Spinner.setupListWithAdapter(list: MutableList<String>,callback: String -> Unit){
var itemSelected = ""
val adapter = ArrayAdapter(context, android.R.layout.simple_spinner_item, list)
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item)
this.adapter = adapter
this.onItemSelectedListener = object : AdapterView.OnItemSelectedListener {
override fun onItemSelected(p0: AdapterView<*>?, p1: View?, p2: Int, p3: Long) {
itemSelected = list[p2]
callback(itemSelected)
Log.e("categoryLog","1 : " + itemSelected)
}
override fun onNothingSelected(p0: AdapterView<*>?) {
}
}
Log.e("categoryLog","2 : " +itemSelected)
}
Then in Activity or fragment
spinner.setupListWithAdapter(list){
// It will invoked when something is selected from spinner
}
Here's the direct answer to your question about why the logs are not in the order you expected:
Your listener is called repeatedly in the future each time items are selected. The current function that creates this listener doesn't wait for any of that to happen. It sets up the listener and returns right away before any items have been selected. It wouldn't make sense for it to wait, because then your app would be frozen, waiting for this function to return because the user has to select an item before the listener is called. But if your app was frozen, then the user could never make a selection, so it would be frozen forever.
The other answers show how you might fix it, but it depends what you're trying to do. It doesn't logically make sense for this function to return a String, because you don't know how many times in the future the user is going to select an item. It could be zero times, one time, or many times.
Use typealias for callback
typealias OnSelectedCategory = (value: String) -> Unit
Update your method with callback
fun Spinner.setupListWithAdapter(list: MutableList<String>,listener : OnSelectedCategory) {
var itemSelected = ""
val adapter = ArrayAdapter(context, android.R.layout.simple_spinner_item, list)
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item)
this.adapter = adapter
this.onItemSelectedListener = object : AdapterView.OnItemSelectedListener {
override fun onItemSelected(p0: AdapterView<*>?, p1: View?, p2: Int, p3: Long) {
itemSelected = list[p2]
Log.e("categoryLog","1 : " + itemSelected)
listener.invoke(itemSelected)
}
override fun onNothingSelected(p0: AdapterView<*>?) {
}
}
Log.e("categoryLog","2 : " +itemSelected)
}
call with spinner
spinner.setupListWithAdapter(list){ value ->
Log.e("categoryLog","2 : " +value)
}

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" />

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.

Filter list of product in a recyclerview - android

So I have a list of product in my recyclerview. Have a editText for search term .
Each time user type something in the editText field the list must be filtered .
with live search there is no problem and the process work well .
for better ui experience I created a button(btn_search) for the edittext also. But in this case when click on the button the list not filtered and just disappeared .!!!
what is wrong with my code.
Code :
this is function in the adapter that calls datachanged :
ListAdapter :
fun filterList(filtername : List<Model>) {
this.model = filtername
notifyDataSetChanged()
}
activity :
lateinit var editText: EditText
lateinit var model: ArrayList<Model>
lateinit var adapter: ListAdapter
lateinit var button: Button
lateinit var recyclerView: RecyclerView
button = findViewById(R.id.btn_search)
var list = ArrayList<Model>()
list.add(somedata) ...
recyclerView = findViewById(R.id.recyclerView)
model = list
recyclerView.layoutManager = GridLayoutManager(this#MainActivity, 2)
adapter = ListAadapter(model)
recyclerView.adapter = adapter
editText = findViewById(R.id.edit_text_search)
// this is where call btnsearch but the list not filtered and disappeared
button.setOnClickListener {
filter(editText.toString())
}
// with this live search there is no issue and list filtered
/*
editText.addTextChangedListener(object : TextWatcher {
override fun afterTextChanged(text: Editable?) {
filter(text.toString())
}
override fun beforeTextChanged(p0: CharSequence?, p1: Int, p2: Int, p3: Int) {
}
override fun onTextChanged(p0: CharSequence?, p1: Int, p2: Int, p3: Int) {
}
})*/
// this is the function for filter the list
fun filter(text: String) {
val filterNameList = ArrayList<Model>()
model.filterTo(filterNameList) { model ->
model.title.contains(text)
}
adapter.filterList(filterNameList)
}
So any idea where I'm doing wrong ?
Seems like you are not getting the EditText text, you are casting the EditText directly into string in this line
button.setOnClickListener {
filter(editText.toString())
}
So, you need to change it to this
button.setOnClickListener {
filter(editText.text.toString())
}

EditText inTextInputLayout calls onTextChanged twice for first letter only

I am developing an open source text masker. You can click here to see source code.
My problem is that, when I wrap my custom edit text with TextInputLayout, onTextChanged is being triggered twice only for first letter. Then it works as expected.
This "twice call" breaks my logic. Do you guys have any idea what might be the problem? Since it is being used by other developers, I don't want to fix it with hacky solution. I need to find out the problem.
I set text manually after removing text watcher, then I add text watcher again.
Here is my main logic;
This method is being called only once;
private fun initTextWatcher() {
textWatcher = 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) {
masker?.onTextChanged(s, start, count, before)
}
}
addTextChangedListener(textWatcher)
}
And this is how I set my text manually;
private fun setEditTextWithoutTriggerListener(newText: String) {
removeTextChangedListener(textWatcher)
onTextChangedListener?.invoke(newText) // This is a custom listener.
setText(newText)
setSelection(text?.length ?: 0) // This puts cursor at the end of the text.
addTextChangedListener(textWatcher)
}
To handle hint position like TextInputEditText does, I simply copied it's functions into mine.
override fun getHint(): CharSequence? {
val layoutHint = getTextInputLayout()?.hint
return layoutHint ?: super.getHint()
}
override fun onCreateInputConnection(outAttrs: EditorInfo): InputConnection? {
val ic = super.onCreateInputConnection(outAttrs)
if (ic != null && outAttrs.hintText == null) {
outAttrs.hintText = getHintFromLayout()
}
return ic
}
private fun getTextInputLayout(): TextInputLayout? {
var parent = this.parent
while (parent is View) {
if (parent is TextInputLayout) {
return parent
}
parent = parent.getParent()
}
return null
}
private fun getHintFromLayout(): CharSequence? {
val layout = getTextInputLayout()
return layout?.hint
}
And my class extends AppCompatEditText like TextInputEditText does.
If you are calling addTextChangedListener() from onCreate() or init() move the call into onResume() or any other function called later, otherwise onTextChanged() is triggered before the resume

Categories

Resources