代码之家  ›  专栏  ›  技术社区  ›  M Rajoy

一直调用AutoCompleteTextView的PerformFilter

  •  6
  • M Rajoy  · 技术社区  · 7 年前

    我的autocompletetextview有以下适配器:

    class CityAutocompleteAdapter(context: Context?)
        : ArrayAdapter<City>(context, R.layout.spinner_city),
            Filterable {
    
        init {
    
        }
    
        private var resultList: List<City> = arrayListOf()
    
        override fun getCount(): Int {
            return resultList.size
        }
    
    
        override fun getItem(position: Int): City = resultList.get(position)
    
        override fun getItemId(position: Int): Long = position.toLong()
    
    
        override fun getView(position: Int, convertView: View?, parent: ViewGroup?): View {
    
            var inflater = LayoutInflater.from(context)
    
    
            val city = getItem(position)
            var convertView = inflater.inflate(R.layout.spinner_city, parent, false)
    
            var name: TextView = convertView.findViewById(R.id.name)
            var country: TextView = convertView.findViewById(R.id.subtitle)
            var flag: ImageView = convertView.findViewById(R.id.flag)
    
            name.text = city.name
    
            GlideApp.with(context)
                    .load(city.flag)
                    .into(flag)
    
            country.text = city.country
    
    
            return convertView
        }
    
        override fun getFilter(): Filter {
            var filter = object : Filter() {
                override fun performFiltering(constraint: CharSequence?): FilterResults {
                    Timber.w("Filtering")
                    var filterResults = FilterResults()
                    if (constraint != null) {
                        var locations = findCities(constraint.toString())
                        filterResults.values = locations
                        filterResults.count = locations.size
                    }
                    return filterResults
                }
    
                override fun publishResults(constraint: CharSequence?, results: FilterResults?) {
                    if (results != null && results.count > 0) {
                        resultList = results.values as (List<City>)
                        notifyDataSetChanged()
                    } else {
                        notifyDataSetInvalidated()
                    }
                }
    
            }
    
            return filter
        }
    
        /**
         * Returns a search result for the given book title.
         */
        private fun findCities(bookTitle: String): List<City> {
            return synchronizer.waitForAPI(bookTitle)
        }
    
        private val synchronizer = Synchronizer()
    
        private inner class Synchronizer {
            internal var result: List<City>? = null
    
            private val lock = java.lang.Object()
    
            internal fun waitForAPI(constraint: CharSequence): List<City> = synchronized(lock) {
                var repository: CityRepository = CityRepositoryImpl()
                repository.getCitiesByName(constraint.toString())
                        .subscribeOn(Schedulers.io())
                        .observeOn(AndroidSchedulers.mainThread())
                        .subscribeBy(onError = {
    
                        }, onSuccess = {
    //                        Timber.w("Results for $constraint are $it")
                            result = it
                            notifyAPIDone(it)
                        })
    
                // At this point, control returns here, and the network request is in-progress in a different thread.
                try {
                    // wait() is a Java IPC technique that will block execution until another
                    // thread calls the same object's notify() method.
                    lock.wait()
                    // When we get here, we know that someone else has just called notify()
                    // on this object, and therefore this.result should be set.
                } catch (e: InterruptedException) {
                }
    
                return this.result ?: emptyList()
            }
    
    
            internal fun notifyAPIDone(result: List<City>) = synchronized(lock) {
                this.result = result
                // API result is received on a different thread, via the API callback.
                // notify() will wake up the other calling thread, allowing it to continue
                // execution in the performFiltering() method, as usual.
                lock.notify()
            }
        }
    
    }
    

    正如你所看到的,我在performfiltering行上放了一个日志行,得到的结果是,在我键入(并停止键入)之后,在我的logcat上不停地显示:

    07-11 13:41:56.469 22176-22259/com.myapp.android.dev.debug W/CityAutocompleteAdapter$getFilter$filter: Filtering
    07-11 13:41:57.221 22176-22259/com.myapp.android.dev.debug W/CityAutocompleteAdapter$getFilter$filter: Filtering
    07-11 13:41:57.975 22176-22259/com.myapp.android.dev.debug W/CityAutocompleteAdapter$getFilter$filter: Filtering
    07-11 13:41:58.728 22176-22259/com.myapp.android.dev.debug W/CityAutocompleteAdapter$getFilter$filter: Filtering
    07-11 13:41:59.481 22176-22259/com.myapp.android.dev.debug W/CityAutocompleteAdapter$getFilter$filter: Filtering
    07-11 13:42:00.232 22176-22259/com.myapp.android.dev.debug W/CityAutocompleteAdapter$getFilter$filter: Filtering
    07-11 13:42:00.985 22176-22259/com.myapp.android.dev.debug W/CityAutocompleteAdapter$getFilter$filter: Filtering
    07-11 13:42:01.738 22176-22259/com.myapp.android.dev.debug W/CityAutocompleteAdapter$getFilter$filter: Filtering
    07-11 13:42:02.491 22176-22259/com.myapp.android.dev.debug W/CityAutocompleteAdapter$getFilter$filter: Filtering
    07-11 13:42:03.243 22176-22259/com.myapp.android.dev.debug W/CityAutocompleteAdapter$getFilter$filter: Filtering
    07-11 13:42:03.995 22176-22259/com.myapp.android.dev.debug W/CityAutocompleteAdapter$getFilter$filter: Filtering
    

    即使当我关闭应用程序(onpause)时,也会执行此操作!这怎么可能?如果我在那里有一个api调用,它将不停地执行请求,直到应用程序从内存中清除。

    编辑:

    使用此适配器的片段:

    class CitySearchFragment : BaseStepImportFragment<ImportViewModel>() {
        override fun backButtonPressed() {
            goBack()
        }
    
        override val viewModelClass: Class<ImportViewModel> = ImportViewModel::class.java
        override val layoutId: Int = R.layout.fragment_import_notes
    
        override fun inject() {
    
            var component = (activity as TextImportActivity).component
            component.inject(this)
        }
    
        override fun loadUp() {
            Timber.e("Saying hello within $this, viewModel $viewModel")
            setHasOptionsMenu(true)
            city_search_combo.threshold = AUTOCOMPLETE_MIN_THRESHOLD
            city_search_combo.setAdapter(CityAutocompleteAdapter(context))
            city_search_combo.setOnItemClickListener { adapterView, view, i, l ->
                val item = adapterView.getItemAtPosition(i) as City
                city_search_combo.setText(item.name)
            }
    
    
        }
    
    
        override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
            // Inflate the menu; this adds items to the action bar if it is present.
            inflater.inflate(R.menu.textcopy_menu, menu)
        }
    
        override fun onOptionsItemSelected(item: MenuItem): Boolean {
            when (item.itemId) {
                R.id.text_copy_done -> {
                    viewModel.sendText(city_search_combo.text.toString(), notes_text.text.toString())
                    city_search_combo.clearFocus()
                    notes_text.clearFocus()
                    goNext()
                }
            }
    
            return super.onOptionsItemSelected(item)
        }
    
        companion object {
            val AUTOCOMPLETE_MIN_THRESHOLD = 3
            fun newInstance() = CitySearchFragment()
        }
    }
    
    1 回复  |  直到 7 年前
        1
  •  2
  •   M Rajoy    7 年前

    我自己修好的,就是用这个:

    class DelayAutoCompleteTextView @JvmOverloads constructor(
            context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0
    ) : AutoCompleteCombo(context, attrs, android.support.v7.appcompat.R.attr.autoCompleteTextViewStyle) {
    
    
        var autoCompleteDelay: Long = DEFAULT_AUTOCOMPLETE_DELAY
    
        var progressIndicator: ProgressBar? = null
    
    
        val delayHandler: Handler = DelayHandler(this)
    
        internal class DelayHandler(val textView: DelayAutoCompleteTextView) : Handler() {
            override fun handleMessage(msg: Message?) {
                var obj = msg?.obj
    
                var text: String = ""
                if (obj is SpannableStringBuilder) {
                    text = obj.toString()
                } else if (obj is String) {
                    text = obj
                }
                textView.performFiltering(text, msg?.arg1?:0)
            }
        }
    
        companion object {
            val MESSAGE_TEXT_CHANGES = 100
            val DEFAULT_AUTOCOMPLETE_DELAY: Long = 750
    
        }
    
        fun setLoadingIndicator(progressBar: ProgressBar) {
            var intrinsicsWidth = ContextCompat.getDrawable(context, R.drawable.ic_dropdown)?.intrinsicWidth
                    ?: 0
            var paddingRight = progressBar.paddingRight + intrinsicsWidth
            progressBar.setPadding(progressBar.paddingLeft,
                    progressBar.paddingTop,
                    paddingRight,
                    progressBar.paddingBottom)
            progressIndicator = progressBar
        }
    
        override fun performFiltering(text: CharSequence?, keyCode: Int) {
            super.performFiltering(text, keyCode)
            progressIndicator?.visibility = View.VISIBLE
            delayHandler.removeMessages(MESSAGE_TEXT_CHANGES)
            delayHandler.sendMessageDelayed(delayHandler.obtainMessage(MESSAGE_TEXT_CHANGES, text), autoCompleteDelay)
        }
    
    
        override fun onFilterComplete(count: Int) {
            progressIndicator?.visibility = View.GONE
            super.onFilterComplete(count)
        }
    
    
        fun setDelay(millis: Long) {
            autoCompleteDelay = millis
        }
    }
    

    它在后台线程中一次又一次地发布要筛选的消息,以便延迟请求。

    推荐文章