I am struggling with setting up my recycle view with a search view, well not really because my search view does work with my recycle view. The filter works and the items are displayed liek i want them to after i entered my search query. But when i click a list item to show the details and in the detail view press the back button, the list won't show me my items, the list is empty. I think it as someting to do with my filtering setup because when i don`t set the OnQueryTextListener it just works fine. Maybe someone has a hint for me what i am missing. I have a tabbed layout with 2 different fragments which both have a recycle view:
class WorkoutListFragment : Fragment() {
private var adapter: WorkoutListTabAdapter? = null
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
return inflater.inflate(R.layout.fragment_workout_list, container, false)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
adapter = WorkoutListTabAdapter(childFragmentManager)
viewPager.adapter = adapter
workoutListTabs.setupWithViewPager(viewPager);
}
}
I only show 1 List Fragment to keep it more clear
class WorkoutCustomListFragment : Fragment() {
private lateinit var mAdapter: WorkoutListAdapter
private lateinit var listViewModel: ListViewModel
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
listViewModel = ViewModelProviders.of(this.activity!!).get(ListViewModel::class.java)
val dataBinding: FragmentCustomWorkoutListBinding =
DataBindingUtil.inflate(
inflater,
R.layout.fragment_custom_workout_list,
container,
false
)
val view: View = dataBinding.root
dataBinding.viewmodel = listViewModel
return view
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
setupRecycleView()
listViewModel.getWorkouts().observe(this, Observer { workouts ->
mAdapter.setData(workouts)
})
listViewModel.openWorkoutDetail.observe(this, EventObserver { workoutId ->
val action =
WorkoutListFragmentDirections.actionWorkoutListFragmentToWorkoutDetailFragment(
workoutId
)
findNavController().navigate(action)
})
listViewModel.openCreateWorkoutEvent.observe(this, EventObserver {
val action =
WorkoutListFragmentDirections.actionWorkoutListFragmentToWorkoutCreateFragment(0)
findNavController().navigate(action)
})
setUpSwipeHandler(mAdapter)
customListSearchView.setOnQueryTextListener(object : SearchView.OnQueryTextListener {
override fun onQueryTextSubmit(p0: String?): Boolean {
mAdapter.filter.filter(p0)
return false
}
override fun onQueryTextChange(p0: String?): Boolean {
mAdapter.filter.filter(p0)
return false
}
})
}
private fun setupRecycleView() {
mAdapter = WorkoutListAdapter(listViewModel)
workoutList.adapter = mAdapter
workoutList.layoutManager = LinearLayoutManager(activity)
}
private fun setUpSwipeHandler(
adapter: WorkoutListAdapter
) {
ItemTouchHelper(object : ItemTouchHelper.SimpleCallback(
0,
ItemTouchHelper.RIGHT
) {
override fun onMove(
recyclerView: RecyclerView,
viewHolder: RecyclerView.ViewHolder,
target: RecyclerView.ViewHolder
): Boolean {
return false
}
override fun onSwiped(viewHolder: RecyclerView.ViewHolder, direction: Int) {
val workoutToDelete = adapter.getWorkout(viewHolder.adapterPosition)
listViewModel.delete(workoutToDelete.id)
Toast.makeText(
activity,
"Workout ${workoutToDelete.title} wurde gelöscht",
Toast.LENGTH_SHORT
).show()
adapter.removeItemFromList(workoutToDelete.id)
adapter.notifyItemRemoved(viewHolder.adapterPosition)
adapter.notifyDataSetChanged()
}
}).attachToRecyclerView(workoutList)
}
}
And my Adapter
class WorkoutListAdapter(private val viewModel: ListViewModel) :
RecyclerView.Adapter<WorkoutListAdapter.ViewHolder>(), Filterable {
private var mWorkouts = ArrayList<Workout>()
private var mWorkoutsFull = ArrayList<Workout>()
override fun getItemCount(): Int {
return mWorkouts.size
}
fun setData(workouts: List<Workout>?) {
mWorkouts = workouts as ArrayList<Workout>
mWorkoutsFull.addAll(workouts)
notifyDataSetChanged()
}
inner class ViewHolder(private val binding: WorkoutlistWorkoutItemBinding) :
RecyclerView.ViewHolder(binding.root) {
fun bind(workout: Workout) {
binding.workout = workout
binding.viewModel = viewModel
binding.executePendingBindings()
}
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
val layoutInflater: LayoutInflater = LayoutInflater.from(parent.context)
val itemBinding: WorkoutlistWorkoutItemBinding =
WorkoutlistWorkoutItemBinding.inflate(layoutInflater, parent, false)
return ViewHolder(itemBinding)
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
val item = mWorkouts[position]
holder.bind(item)
}
fun getWorkout(position: Int): Workout {
return mWorkouts[position]
}
override fun getFilter(): Filter {
return workoutListFilter
}
fun removeItemFromList(id: Int) {
val workout = mWorkouts.find { it.id == id }
val index = mWorkouts.indexOf(workout)
mWorkouts.removeAt(index)
}
private val workoutListFilter: Filter = object : Filter() {
override fun performFiltering(constraint: CharSequence?): FilterResults {
val filterList = ArrayList<Workout>()
if (constraint!!.isEmpty() || constraint == null) {
filterList.addAll(mWorkoutsFull)
} else {
val searchString = constraint.toString().toLowerCase().trim()
for (workout in mWorkoutsFull) {
if (workout.title.toLowerCase().contains(searchString)) {
filterList.add(workout)
}
}
}
val results = FilterResults()
results.values = filterList
return results
}
override fun publishResults(p0: CharSequence?, filterResult: FilterResults?) {
mWorkouts.clear()
mWorkouts.addAll(filterResult!!.values as ArrayList<Workout>)
notifyDataSetChanged()
}
}
}
And at last my viewmodel (maybe it helps) i am working with live data
class ListViewModel @Inject constructor(
private var workoutRepository: WorkoutRepository,
private var finishedWorkoutRepository: FinishedWorkoutRepository
) : ViewModel() {
private val _openWorkoutDetailEvent = MutableLiveData<Event<Int>>()
val openWorkoutDetail: LiveData<Event<Int>> = _openWorkoutDetailEvent
private val _openCreateWorkoutEvent = MutableLiveData<Event<Int>>()
val openCreateWorkoutEvent: LiveData<Event<Int>> = _openCreateWorkoutEvent
fun getStandardWorkouts(): LiveData<List<Workout>> {
return workoutRepository.getStandardWorkouts()
}
fun getWorkouts(): LiveData<List<Workout>> {
return workoutRepository.getWorkouts()
}
fun openWorkoutDetail(workoutId: Int) {
_openWorkoutDetailEvent.value = Event(workoutId)
}
fun openCreateWorkout() {
_openCreateWorkoutEvent.value = Event(0)
}
fun delete(workoutId: Int) {
workoutRepository.delete(workoutId)
}
}
Best regards
You're clearing list mWorkouts list in publishResults without checking if there are some results found or not, add a check as :
if (filterResults.values != null && filterResults.count > 0) {
mWorkouts.clear()
mWorkouts.addAll(filterResult!!.values as ArrayList<Workout>)
notifyDataSetChanged()
}