Fixed lag when searching in tasks

This commit is contained in:
Akash Yadav
2022-10-26 10:35:19 +05:30
parent ba9c7c9cfb
commit 223e20ac8d
5 changed files with 75 additions and 25 deletions
Binary file not shown.
@@ -17,10 +17,14 @@
package com.itsaky.androidide.adapters
import android.annotation.SuppressLint
import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.DiffUtil.Callback
import androidx.recyclerview.widget.DiffUtil.DiffResult
import androidx.recyclerview.widget.RecyclerView
import com.blankj.utilcode.util.ThreadUtils
import com.itsaky.androidide.progress.ProcessCancelledException
import com.itsaky.androidide.progress.ProgressManager
import com.itsaky.androidide.utils.StopWatch
/**
* Filterable [RecyclerView.Adapter].
@@ -38,20 +42,20 @@ abstract class FilterableRecyclerViewAdapter<V : RecyclerView.ViewHolder, D>(val
*
* @param query The query.
*/
@SuppressLint("NotifyDataSetChanged")
fun filter(query: String?) {
filterThread?.let { ProgressManager.instance.cancel(it) }
filterThread =
FilterThread(this.items, query?.trim(), this::onFilter).apply {
start()
join()
FilterThread(this.items, query?.trim(), this::onFilter) { filtered, result ->
if (result == null) {
this.filtered = this.items
notifyDataSetChanged()
return@FilterThread
}
this.filtered = filtered
result.dispatchUpdatesTo(this)
}
filterThread!!.let {
ThreadUtils.runOnUiThread {
this.filtered = it.filtered
notifyDataSetChanged()
}
}
filterThread?.start()
}
/** Get the list item at given index. */
@@ -74,20 +78,61 @@ abstract class FilterableRecyclerViewAdapter<V : RecyclerView.ViewHolder, D>(val
private class FilterThread<E>(
val items: List<E>,
val query: String?,
val onFilter: (E, String) -> Boolean
val onFilter: (E, String) -> Boolean,
val onFiltered: (List<E>, DiffResult?) -> Unit
) : Thread("TaskFilterThread") {
lateinit var filtered: List<E>
override fun run() {
this.filtered =
if (query.isNullOrBlank()) {
items
} else
items.filter {
ProgressManager.abortIfCancelled()
onFilter(it, query)
try {
doFilter()
} catch (e: ProcessCancelledException) {
// ignored
}
}
private fun doFilter() {
if (query.isNullOrBlank()) {
postFilter(items, null)
return
}
val filtered = items.filter {
ProgressManager.abortIfCancelled()
onFilter(it, query)
}
val result =
DiffUtil.calculateDiff(
object : Callback() {
override fun getOldListSize(): Int {
ProgressManager.abortIfCancelled()
return items.size
}
override fun getNewListSize(): Int {
ProgressManager.abortIfCancelled()
return filtered.size
}
override fun areItemsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean {
ProgressManager.abortIfCancelled()
return items[oldItemPosition] == filtered[newItemPosition]
}
override fun areContentsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean {
ProgressManager.abortIfCancelled()
return items[oldItemPosition] == filtered[newItemPosition]
}
}
)
postFilter(filtered, result)
}
private fun postFilter(filtered: List<E>, result: DiffResult?) {
ThreadUtils.runOnUiThread {
onFiltered(filtered, result)
}
}
}
}
@@ -18,6 +18,7 @@
package com.itsaky.androidide.fragments
import android.os.Bundle
import android.os.Handler
import android.text.Editable
import android.view.LayoutInflater
import android.view.View
@@ -25,6 +26,7 @@ import android.view.ViewGroup
import androidx.fragment.app.viewModels
import androidx.recyclerview.widget.RecyclerView
import androidx.transition.TransitionManager
import com.blankj.utilcode.util.ThreadUtils
import com.google.android.material.bottomsheet.BottomSheetDialogFragment
import com.google.android.material.transition.MaterialSharedAxis
import com.itsaky.androidide.R.string
@@ -68,7 +70,7 @@ class RunTasksDialogFragment : BottomSheetDialogFragment() {
// The minimum amount of time (in milliseconds) the adapter should wait after the query is
// changed before starting any further filter request.
// A too less value here will result in UI lags
private const val SEARCH_DELAY = 500
private const val SEARCH_DELAY = 500L
}
override fun onCreateView(
@@ -95,7 +97,6 @@ class RunTasksDialogFragment : BottomSheetDialogFragment() {
if (adapter !is RunTasksCategoryAdapter) {
return@observeQuery
}
for (index in 0 until viewModel.categories.size) {
val layout =
run.categories.layoutManager
@@ -112,8 +113,12 @@ class RunTasksDialogFragment : BottomSheetDialogFragment() {
run.searchInput.editText?.addTextChangedListener(
object : SingleTextWatcher() {
val searchRunner = Runnable {
viewModel.query = run.searchInput.editText?.text?.toString() ?: ""
}
override fun afterTextChanged(s: Editable?) {
viewModel.query = s?.toString() ?: ""
ThreadUtils.getMainHandler().removeCallbacks(searchRunner)
ThreadUtils.runOnUiThreadDelayed(searchRunner, SEARCH_DELAY)
}
}
)
@@ -50,7 +50,7 @@ class ProgressManager private constructor(){
@JvmName("internalAbortIfCancelled")
private fun abortIfCancelled() {
val checker = threads[Thread.currentThread()]
val checker = threads.remove(Thread.currentThread())
if (checker != null && checker.isCancelled()) {
throw ProcessCancelledException()
}
Binary file not shown.