I'm trying to add searchview on recyclerview but its not working even not showing any error. I ready many answers on stackoverflow but they are not worked for me.please tell me what is mistake in my code. when i debug my code then searched values are showing in result variable but not showing in recyclerview. i want to search with 1) Country Name 2) capital 3) and id can some help me
my adapter is here
class ProfileListAdapter(var profiles:ArrayList<Profile>):RecyclerView.Adapter<ProfileListAdapter.ProfileViewHolder>(),Filterable {
fun updateProfile(newProfiles:List<Profile>){
profiles.clear()
profiles.addAll(newProfiles)
notifyDataSetChanged()
}
var profileFilterList = ArrayList<Profile>()
init {
profileFilterList = profiles
}
class ProfileViewHolder(view: View):RecyclerView.ViewHolder(view){
private var id = view.findViewById<TextView>(R.id.tv_id)
private var name = view.findViewById<TextView>(R.id.tv_name)
private var fatherName = view.findViewById<TextView>(R.id.tv_father_name)
private var profileImage = view.findViewById<ImageView>(R.id.iv_profile_image)
private var progressDrawable = getProgressDrawable(view.context)
fun bind(profile: Profile){
id.text = profile.id.toString()
name.text = profile.name
fatherName.text = profile.fatherName
profileImage.loadImage(profile.profilePicture,progressDrawable)
}
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) = ProfileViewHolder(
LayoutInflater.from(parent.context).inflate(R.layout.rv_dummy_items,parent,false)
)
override fun onBindViewHolder(holder: ProfileViewHolder, position: Int) {
holder.bind(profiles[position])
}
override fun getItemCount() = profiles.size
// Search items
override fun getFilter(): Filter {
return object : Filter() {
override fun performFiltering(constraint: CharSequence?): FilterResults {
val charString = constraint?.toString() ?: ""
if (charString.isEmpty()){
profiles.also { profileFilterList = it }
} else {
val filteredList = ArrayList<Profile>()
profiles
.filter {
(it.name.contains(constraint!!)) or (it.fatherName.contains(constraint))
}
.forEach { filteredList.add(it) }
profileFilterList = filteredList
}
return FilterResults().apply { values = profileFilterList }
}
override fun publishResults(constraint: CharSequence?, results: FilterResults?) {
profileFilterList = if (results?.values == null) ArrayList()
else
results.values as ArrayList<Profile>
notifyDataSetChanged()
}
}
}
}
my main activity is here
class MainActivity : AppCompatActivity() {
lateinit var binding: ActivityMainBinding
lateinit var viewModel: ListViewModel
var profileAdapter = ProfileListAdapter(arrayListOf())
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityMainBinding.inflate(layoutInflater)
val view = binding.root
setContentView(view)
//here I'm calling search profile function
searchProfile()
//
viewModel = ViewModelProviders.of(this).get(ListViewModel::class.java)
viewModel.refresh()
binding.rvProfileList.apply {
layoutManager = LinearLayoutManager(context)
adapter = profileAdapter
}
observeViewModel()
filterButtons()
}
fun observeViewModel() {
viewModel.profiles.observe(this, Observer { profiles: List<Profile>? ->
profiles?.let {
binding.rvProfileList.visibility = View.VISIBLE
profileAdapter.updateProfile(it)
}
})
viewModel.profileLoadingError.observe(this, Observer { isError: Boolean? ->
isError?.let {
binding.listError.visibility = if (it) View.VISIBLE else View.GONE
}
})
viewModel.loading.observe(this, Observer { isLoading ->
isLoading?.let {
binding.loadingView.visibility = if (it) View.VISIBLE else View.GONE
}
})
}
//search function
fun searchProfile(){
binding.searchView.setOnQueryTextListener(object: SearchView.OnQueryTextListener{
override fun onQueryTextSubmit(query: String?): Boolean {
profileAdapter.filter.filter(query)
return false
}
override fun onQueryTextChange(newText: String?): Boolean {
profileAdapter.filter.filter(newText)
return false
}
})
}
}
Your code quality is really bad. I read repeatly to understand what you mean.
Firstly, you are filtering twice in OnQueryTextListener. Instead you can filter once only. Change your searchProfile function to
fun searchProfile(){
binding.searchView.setOnQueryTextListener(object: SearchView.OnQueryTextListener{
override fun onQueryTextSubmit(query: String?): Boolean {
return false
}
override fun onQueryTextChange(newText: String?): Boolean {
profileAdapter.filter.filter(newText)
return false
}
})
}
Probably, your failure is because of reassigning your profiles and profileFilterList lists.
In few times, you are assingning profiles to profileFilterList, not copying! Both of two lists have same references. So if one of them changes, other list changes directly.
Change your Adapter init block from
init {
profileFilterList = profiles
}
to
init {
profileFilterList = ArrayList(profiles)
}
Change your getFilter function from
override fun getFilter(): Filter {
return object : Filter() {
override fun performFiltering(constraint: CharSequence?): FilterResults {
val charString = constraint?.toString() ?: ""
if (charString.isEmpty()){
profiles.also { profileFilterList = it }
} else {
val filteredList = ArrayList<Profile>()
profiles
.filter {
(it.name.contains(constraint!!)) or (it.fatherName.contains(constraint))
}
.forEach { filteredList.add(it) }
profileFilterList = filteredList
}
return FilterResults().apply { values = profileFilterList }
}
override fun publishResults(constraint: CharSequence?, results: FilterResults?) {
profileFilterList = if (results?.values == null) ArrayList()
else
results.values as ArrayList<Profile>
notifyDataSetChanged()
}
}
}
to
override fun getFilter(): Filter {
return object : Filter() {
override fun performFiltering(constraint: CharSequence?): FilterResults {
val filteredList = mutableListOf<Profile>()
if (constraint == null || constraint.isEmpty()) {
filteredList.addAll(profiles)
} else {
filteredList.addAll(
profiles.filter { (it.name.contains(constraint!!)) or (it.fatherName.contains(constraint)) } )
}
return FilterResults().apply { values = filteredList }
}
override fun publishResults(constraint: CharSequence?, results: FilterResults?) {
profileFilterList.clear()
results?.values?.let {
profileFilterList.addAll(it as MutableList<Profile>)
}
notifyDataSetChanged()
}
}
}
I edit in your adapter and filter works fine with me my code after modification
class ProfileListAdapter():RecyclerView.Adapter<ProfileListAdapter.ProfileViewHolder>(),Filterable {
var profileList: ArrayList<Profile> = ArrayList()
var profileListFiltered: ArrayList<Profile> = ArrayList()
fun updateProfile(newProfiles:List<Profile>){
profileList = ArrayList(newProfiles)
profileListFiltered = profileList
notifyDataSetChanged()
}
class ProfileViewHolder(view: View):RecyclerView.ViewHolder(view){
private var id = view.findViewById<TextView>(R.id.tv_id)
private var name = view.findViewById<TextView>(R.id.tv_name)
private var fatherName = view.findViewById<TextView>(R.id.tv_father_name)
private var profileImage = view.findViewById<ImageView>(R.id.iv_profile_image)
private var progressDrawable = getProgressDrawable(view.context)
fun bind(profile: Profile){
id.text = profile.id.toString()
name.text = profile.name
fatherName.text = profile.fatherName
profileImage.loadImage(profile.profilePicture,progressDrawable)
}
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) =
ProfileViewHolder(
LayoutInflater.from(parent.context).inflate(R.layout.rv_dummy_items,parent,false)
)
override fun onBindViewHolder(holder: ProfileViewHolder, position: Int)
{
holder.bind(profileListFiltered[position])
}
override fun getItemCount() = profileListFiltered.size
// Search items
override fun getFilter(): Filter {
return object : Filter() {
override fun performFiltering(constraint: CharSequence?): FilterResults
{
val charString = constraint?.toString() ?: ""
if (charString.isEmpty()){
profileListFiltered = profileList
} else {
val filteredList = ArrayList<Profile>()
profileList
.filter {
(it.name.contains(constraint!!)) or
(it.fatherName.contains(constraint))
}
.forEach { filteredList.add(it) }
profileFilterList = filteredList
}
return FilterResults().apply { values = profileFilterList }
}
override fun publishResults(constraint: CharSequence?, results:
FilterResults?) {
profileFilterList = if (results?.values == null) ArrayList()
else
results.values as ArrayList<Profile>
notifyDataSetChanged()
}
}
}
}
in your activity just declare adapter without parameters constructor after get data using function inside adapter updateProfile then will work fine with you ISA
Edit for your adapter
class ProfileListAdapter(var
profiles:ArrayList<Profile>)
:RecyclerView.Adapter<ProfileListAdapter.Profile
ViewHolder>(),Filterable {
fun updateProfile(newProfiles:List<Profile>){
profiles.clear()
profiles.addAll(newProfiles)
notifyDataSetChanged()
}
var profileFilterList = ArrayList<Profile>()
init {
profileFilterList = profiles
}
class ProfileViewHolder(view: View):RecyclerView.ViewHolder(view){
private var id = view.findViewById<TextView>(R.id.tv_id)
private var name = view.findViewById<TextView>(R.id.tv_name)
private var fatherName = view.findViewById<TextView>
(R.id.tv_father_name)
private var profileImage = view.findViewById<ImageView>
(R.id.iv_profile_image)
private var progressDrawable = getProgressDrawable(view.context)
fun bind(profile: Profile){
id.text = profile.id.toString()
name.text = profile.name
fatherName.text = profile.fatherName
profileImage.loadImage(profile.profilePicture,progressDrawable)
}
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) =
ProfileViewHolder(
LayoutInflater.from(parent.context)
.inflate(R.layout.rv_dummy_items,parent,false)
)
override fun onBindViewHolder(holder: ProfileViewHolder, position:
Int)
{
holder.bind(profileFilterList[position])
}
override fun getItemCount() = profileFilterList.size
// Search items
override fun getFilter(): Filter {
return object : Filter() {
override fun performFiltering(constraint: CharSequence?):
FilterResults {
val charString = constraint?.toString() ?: ""
if (charString.isEmpty()){
profileFilterList = profiles
} else {
val filteredList = ArrayList<Profile>()
profiles
.filter {
(it.name.contains(constraint!!)) or
(it.fatherName.contains(constraint))
}
.forEach { filteredList.add(it) }
profileFilterList = filteredList
}
return FilterResults().apply { values = profileFilterList }
}
override fun publishResults(constraint: CharSequence?, results:
FilterResults?) {
profileFilterList = if (results?.values == null) ArrayList()
else
results.values as ArrayList<Profile>
notifyDataSetChanged()
}
}
}
}
Related
I started the debugger and my breakpoints show that on the first startup the dataset is being transmitted as the adapter is being initialized, but as soon as I call the filter it doesn't have any data to work with and my recyclerview just stays as it was before.
All this code worked before, I just translated it from Java to Kotlin. I must have messed something up, but I can't find it.
Adapter Class
class ClothingListAdapterKt(
private val dataSetIn: MutableList<Clothing>,
private val listener: ClothingListAdapterKt.OnItemClickListener,
private val context: Context
) :
RecyclerView.Adapter<ClothingListAdapterKt.ViewHolder>(), Filterable {
private var lastPosition = -1
var dataSet = mutableListOf<Clothing>()
var dataSetFiltered = mutableListOf<Clothing>()
init {
dataSet = dataSetIn
dataSetFiltered = dataSet
}
override fun onBindViewHolder(viewHolder: ClothingListAdapterKt.ViewHolder, position: Int) {
val currentClothing: Clothing = dataSetFiltered[position]
// Get element from your dataset at this position and replace the
// contents of the view with that element
//SETTING MY VIEWS, ONLY COMMENTED OUT FOR THIS QUESTION
}
setAnimation(viewHolder.itemView, position);
}
override fun getFilter(): Filter {
return object : Filter() {
override fun performFiltering(constraint: CharSequence?): FilterResults? {
val charString: String = constraint.toString()
if (charString.isEmpty()) {
dataSetFiltered.addAll(dataSet)
} else {
val dataSetTemp: MutableList<Clothing> = mutableListOf()
for (row in dataSet) {
if (row.name.lowercase().contains(charString.lowercase())) {
dataSetTemp.add(row)
}
}
dataSetFiltered = dataSetTemp
}
val filterResults = FilterResults()
filterResults.values = dataSetFiltered
return filterResults
}
override fun publishResults(constraint: CharSequence?, results: FilterResults?) {
dataSetFiltered = results?.values as MutableList<Clothing>
notifyDataSetChanged()
}
}
}
Adapter Init
recylcerViewClothing.adapter = ClothingListAdapterKt(clothing, listener, this.requireContext())
This is how I call the filter
adapter.filter.filter(searchTerm) //searchTerm is a String passed by the constructor of the function it sits in
this is my filter and It works properly
private val filter = object : Filter() {
override fun performFiltering(constraint: CharSequence?): FilterResults {
val result = FilterResults()
val suggestions: MutableList<ShopAddressProvince> = mutableListOf()
if (constraint != null) {
suggestions.clear()
val filterPattern = constraint.toString().lowercase()
for (item in list) {
if (item.text.lowercase().contains(filterPattern)) {
suggestions.add(item)
}
}
result.values = suggestions
result.count = suggestions.size
}
return result
}
and check publish results
override fun publishResults(constraint: CharSequence?, results: FilterResults?) {
clear()
if (results != null && results.count > 0) {
addAll(results.values as MutableList<ShopAddressProvince>)
} else {
addAll(list)
}
notifyDataSetChanged()
}
I had to remade ArrayAdapter with filter feature to RecyclerView Adapter because of nested scrolling issues. But after I remade it, RecyclerView is empty, but adapter.itemCount is returning right value. Problem is that after I call adapter.notifyDataSetChanged(), it is not calling onCreateViewHolder or onBindViewHolder function.
class ZoneViewHolder(val root: ViewGroup): RecyclerView.ViewHolder(root) {
fun bind(
zone: FlightZone,
manageZoneCheck: (View)-> Unit,
maybeAutoselectZone: (View) -> Unit){
root.setOnClickListener {
manageZoneCheck(root.find(R.id.check))
}
root.find<View>(R.id.check).also { check->
check.setOnClickListener {
manageZoneCheck(check)
}
//autoselect zone if it was previously picked (after search reset)
maybeAutoselectZone(check)
}
root.findText(R.id.zoneNum).text = zone.name
root.findText(R.id.zoneName).apply {
isSelected = true
text = zone.radarInfo
}
root.findText(R.id.separator).setVisibleNotGone(zone.radarInfo != null)
}
}
class ZoneListAdapter(
private val ctx: Context,
private val inflater: LayoutInflater,
private val zoneView: Int,
private val zoneList: List<FlightZone>,
private var list: MutableList<FlightZone>,
private val pickedZones: List<FlightZone>,
private val onZoneChecked: (FlightZone, Boolean) -> Unit,
private val onAllZonesChecked: (Boolean) -> Unit,
private val onFilterDone: (List<FlightZone>) -> Unit
): RecyclerView.Adapter<ZoneViewHolder>(), Filterable{
override fun getItemCount() = list.size
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ZoneViewHolder {
return ZoneViewHolder(inflater.inflate(zoneView, parent, false) as ViewGroup)
}
override fun onBindViewHolder(vh: ZoneViewHolder, position: Int) {
val zone = list[position]
App.log("ZoneListAdapter: onBindView: ${list.size}")
vh.bind(
zone,
manageZoneCheck = { check->
manageZoneCheck(check, zone)
},
maybeAutoselectZone = {check->
App.log("ZoneListAdapter: alreadyPicked: ${pickedZones.size}")
if (pickedZones.find { it.id == zone.id } != null) check.callOnClick()
}
)
}
/**
* Custom Filter implementation for custom suggestions we provide.
*/
data class ZoneFilterable(val zone: FlightZone, val prefixLength: Int)
internal var tempItems: MutableList<FlightZone> = zoneList.toMutableList()
internal var suggestions: MutableList<ZoneFilterable> = mutableListOf()
internal val sharedCounterLock = Semaphore(1)
private var filter = object : Filter(){
override fun performFiltering(constraint: CharSequence?): FilterResults {
return if (constraint != null) {
sharedCounterLock.acquire()
suggestions.clear()
filterByName(constraint)
suggestions.sortedByDescending { it.prefixLength }
val filterResults = FilterResults()
filterResults.values = suggestions
filterResults.count = suggestions.size
sharedCounterLock.release()
filterResults
} else {
FilterResults()
}
}
private fun filterByName(constraint: CharSequence?){
tempItems.forEach {
val zoneName = it.name.toLowerCase(Locale.getDefault())
val zoneNameNonAccent = zoneName.removeDiacritics()
val query = constraint.toString().toLowerCase(Locale.getDefault())
val queryNonAccent = query.removeDiacritics()
App.log("FlightZonePicker filter zone name non-accent: $zoneNameNonAccent")
if (zoneNameNonAccent.contains(queryNonAccent)) {
val prefix = zoneName.commonPrefixWith(query)
suggestions.add(ZoneFilterable(it, prefix.length))
}
}
}
override fun publishResults(constraint: CharSequence?, results: FilterResults) {
if (results.count > 0) {
list.clear()
val filterList = results.values as? List<Any?>
var resultList = mutableListOf<FlightZone>()
GlobalScope.launch(Dispatchers.Main) {
val resultListAsync = async {
withContext(Dispatchers.Default) {
val list = mutableListOf<FlightZone>()
sharedCounterLock.acquire()
val iter = filterList?.iterator()
iter?.let {
while (iter.hasNext()) {
val item = iter.next()
(item as ZoneFilterable).let { (zoneEntity) -> list.add(zoneEntity) }
}
}
sharedCounterLock.release()
return#withContext list
}
}
resultList = resultListAsync.await().take(10).toMutableList()
App.log("ZoneListAdapter: Results: ${resultList.size}")
list.addAll(resultList)
onFilterDone.invoke(resultList)
}
}
}
}
init {
tempItems = zoneList.toMutableList()
}
override fun getFilter(): Filter {
return filter
}
}
MainClass:
zoneListRecycleView = findViewById(R.id.zoneResults)
zoneListAdapter = ZoneListAdapter(
ctx,
f.layoutInflater,
R.layout.zone_pick_item,
zoneList,
filteredZones,
pickedZones,
onZoneChecked = {
zone, isPicked ->
pickedZones.apply {
if (isPicked){
if (find { it.id == zone.id } == null) add(zone)
} else {
if (find { it.id == zone.id } != null) remove(zone)
}
}
zoneListAdapter.notifyDataSetChanged()
},
onAllZonesChecked = {ischecked->
pickedZones.apply {
clear()
if(ischecked) addAll(zoneList)
}
zoneListAdapter.notifyDataSetChanged()
},
::onFilterResult
)
zoneListRecycleView.adapter = zoneListAdapter
private fun onFilterResult(list: List<FlightZone>){
filteredZones = list.toMutableList()
zoneListAdapter.notifyDataSetChanged()
App.log("ZonelistAdapter: count = ${zoneListAdapter.itemCount}")
}
UPDATE:
I changed Adapter to ListAdapter and I changed publishResults function to
resultList = resultListAsync.await().take(10).toMutableList()
onFilterDone.invoke(resultList)
And inside my parent class I call:
private fun onFilterResult(list: List<FlightZone>){
filteredZones = list.toMutableList()
zoneListAdapter.submitList(filteredZones)
App.log("ZonelistAdapter: filteredList = ${filteredZones.size}")
App.log("ZonelistAdapter: count = ${zoneListAdapter.itemCount}")
}
Log:
app: ZonelistAdapter: filteredList = 10
app: ZonelistAdapter: count = 0
There is still some issue with adapter. So filter feature is working as intended but adapter and recyclerview is not displaying items for some reason.
I'm having two fragments
1.UploadFragment - to upload data to firestore
2.FetchFragemnt - fetch data from firestore and display it in recyclerView
these two fragments are used in a NavigationDrawer.
My problem is in fetchFragment initially recyclerView displays datas that are fetched from firestore.
but when I navigate to UploadFragment and return back to FetchFragment RecyclerView is not loaded.
Also, onCreateViewHolder and onBindViewHolder are not called when I navigate to UploadFragment and return back to FetchFragment.
Please someone help.
FetchFragment code :
class FetchFragment : Fragment(R.layout.fragment_fetch) {
private lateinit var binding : FragmentFetchBinding
private lateinit var adapter: PersonsAdapter
private lateinit var personsList: MutableList<PersonsDb>
private val personCollectionRef = Firebase.firestore.collection("persons")
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
binding = FragmentFetchBinding.bind(view)
personsList = mutableListOf()
adapter = PersonsAdapter(personsList){item ->
Intent(requireContext(),UpdateActivity::class.java).also{
it.putExtra("EXTRA_DETAILS",item.id)
startActivity(it)
}
}
binding.rvPersons.adapter = adapter
binding.rvPersons.layoutManager = LinearLayoutManager(requireContext())
fetchPPerson()
binding.svFilter.setOnQueryTextListener(object : SearchView.OnQueryTextListener {
override fun onQueryTextSubmit(p0: String?): Boolean {
//Performs search when user hit the search button on the keyboard
// adapter.getFilter().filter(p0);
return false
}
override fun onQueryTextChange(p0: String?): Boolean {
//Start filtering the list as user start entering the characters
adapter.getFilter().filter(p0);
return false
}
})
}
private fun fetchPPerson() = CoroutineScope(Dispatchers.IO).launch{
try{
val querySnapshot = personCollectionRef.get().await()
personsList.clear()
for(document in querySnapshot.documents){
val person = document.toObject<Person>()
personsList.add(
PersonsDb(document.id,
person?.name.toString(),
person?.age.toString().toInt(),
Date(person?.dob.toString())
))
}
withContext(Dispatchers.Main){
adapter.notifyDataSetChanged()
}
}catch (e:Exception){
withContext(Dispatchers.Main){
Toast.makeText(requireContext(),e.message, Toast.LENGTH_LONG).show()
Log.d("Fetch Error", e.message)
}
}
}
}
My Recycler Adapter :
class PersonsAdapter (
var persons : List<PersonsDb>,
private val listener: (PersonsDb) -> Unit
):RecyclerView.Adapter<PersonsAdapter.PersonsViewHolder>(),Filterable{
var personsList = persons
inner class PersonsViewHolder(val binding : ItemPersonBinding):
RecyclerView.ViewHolder(binding.root)
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): PersonsViewHolder {
val binding = ItemPersonBinding.inflate(LayoutInflater.from(parent.context),parent,false)
return PersonsViewHolder(binding)
}
override fun getItemCount(): Int {
return persons.size
}
override fun getFilter(): Filter {
return filter
}
override fun onBindViewHolder(holder: PersonsViewHolder, position: Int) {
val item = persons[position]
with(holder){
with(persons[position]){
binding.tvName.text = this.name
binding.tvAge.text = this.age.toString()
// binding.tvDob.text = this.dob.toString()
binding.tvDob.text = updateDateInView(this.dob)
}
}
holder.itemView.setOnClickListener { listener(item) }
}
private fun updateDateInView(cal : Date) : String{
val myFormat = "dd/MM/yyyy" // mention the format you need
val sdf = SimpleDateFormat(myFormat, Locale.US)
// binding.dpDob.setText(sdf.format(cal.getTime()))
return sdf.format(cal.getTime())
}
private val filter: Filter = object : Filter() {
override fun performFiltering(constraint: CharSequence): FilterResults {
var filteredList: MutableList<PersonsDb> = arrayListOf()
if (constraint.isEmpty()) {
filteredList.addAll(personsList)
} else {
val filterPattern = constraint.toString().toLowerCase().trim { it <= ' ' }
for (item in 0..persons.size -1) {
if (persons[item].name.toLowerCase().contains(filterPattern)
|| persons[item].age.toString().contains(filterPattern)) {
filteredList.add(persons[item])
}
}
}
val results = FilterResults()
results.count = filteredList.size
results.values = filteredList
// Log.d("Filter Values", results.values.toString())
return results
}
override fun publishResults(charSequence: CharSequence, filterResults: FilterResults) {
persons = if(filterResults == null || filterResults.values == null){
personsList
}
else
filterResults.values as List<PersonsDb>
notifyDataSetChanged()
}
}
}
Can you check Item count( log item count ) . That way you will know if data exists
for recyclerview when returning to FetchFragment . I suspect empty data could be reason.
I am currently learning Android programming and currently trying to make autocompletetextview by following the tutorial from the following link https://medium.com/#droidbyme/autocomplete-textview-in-android-a1bf5fc112f6 on this link I managed to bring up suggestions from the retrofit response but when I rewrote the suggestion it didn't appear.
this is from my activity
var adapter = SuggestionAdapter(this#Formpengiriman_Activity, R.layout.custom_row, kecamatanModel.getProducts()) autoCompleteTextView.setAdapter(adapter) autoCompleteTextView.setThreshold(1)
this is my kecamatanModel
class KecamatanModel {
private val kecamatan: ArrayList<Kecamatan>
init {
this.kecamatan = ArrayList<Kecamatan>()
var call: Call<List<GetKecamatan>> = ApiClient.getClient.showkecamatan()
call.enqueue(object : Callback<List<GetKecamatan>> {
override fun onFailure(call: Call<List<GetKecamatan>>, t: Throwable) {
}
override fun onResponse(
call: Call<List<GetKecamatan>>,
response: Response<List<GetKecamatan>>
) {
if(response.isSuccessful){
var json = response.body()
for(i in json?.indices!!){
kecamatan.add(Kecamatan(json.get(i).getIDKECAMATAN().toString(), json.get(i).getNAMAKECAMATAN().toString(), json.get(i).getNAMAPROVINSI().toString(), json.get(i).getNAMAKOTA().toString()))
}
}
}
})
}
fun getProducts(): ArrayList<Kecamatan> {
return this.kecamatan
} }
this is my SuggestionAdapter
class SuggestionAdapter(
internal val context: Context,
private val resourceId: Int,
items: ArrayList<Kecamatan>
) : ArrayAdapter<Kecamatan>(context, resourceId, items) {
private val items: List<Kecamatan>
private var tempItems: List<Kecamatan>
private var suggestions: MutableList<Kecamatan>
private val fruitFilter = object : Filter() {
override fun convertResultToString(resultValue: Any): CharSequence {
val fruit = resultValue as Kecamatan
return fruit.getNama().toString()
}
override fun performFiltering(charSequence: CharSequence?): Filter.FilterResults {
suggestions = ArrayList()
tempItems = ArrayList(items)
if (charSequence != null) {
suggestions.clear()
for (fruit in tempItems) {
if (fruit.getNama()?.toLowerCase()?.startsWith(charSequence.toString().toLowerCase())!!) {
suggestions.add(fruit)
}
}
val filterResults = Filter.FilterResults()
filterResults.values = suggestions
filterResults.count = suggestions.size
return filterResults
} else {
return Filter.FilterResults()
}
}
override fun publishResults(
charSequence: CharSequence?,
filterResults: Filter.FilterResults
) {
val tempValues = filterResults.values as ArrayList<Kecamatan>
if (filterResults != null && filterResults.count > 0) {
clear()
for (fruitObj in tempValues) {
add(fruitObj)
}
notifyDataSetChanged()
} else {
clear()
notifyDataSetChanged()
}
}
}
init {
this.items = items
tempItems = ArrayList(items)
suggestions = ArrayList()
for (i in 0 until items.size) {
suggestions!!.add(items[i])
}
}
override fun getView(position: Int, convertView: View?, parent: ViewGroup): View {
var view = convertView
try {
if (convertView == null) {
val inflater = LayoutInflater.from(context)
view = inflater.inflate(R.layout.custom_row, parent, false)
}
val fruit = getItem(position)
Log.d("kecamatan", fruit?.getNama())
val name = view?.findViewById(R.id.tv1) as TextView
name.setText(fruit!!.getprov()+", "+fruit!!.getkota()+", "+fruit!!.getNama())
} catch (e: Exception) {
e.printStackTrace()
}
return view!!
}
#Nullable
override fun getItem(position: Int): Kecamatan? {
return items[position]
}
override fun getCount(): Int {
return items.size
}
override fun getItemId(position: Int): Long {
return position.toLong()
}
override fun getFilter(): Filter {
return fruitFilter
}}
I have two screens first one has recycler view list of data and searchView above it that's filter data in this recycler, the view Model code of the first fragment
class MscInspectionViewModel(val activity: LaunchActivity, val mRootView: MscInspectFragment) :
BaseViewModel(),
SwipeRefreshLayout.OnRefreshListener {
val toolBarTitle: MutableLiveData<String> = MutableLiveData()
private val getDataError = MutableLiveData<Boolean>()
var listType = MutableLiveData<Int>()
val hint = MutableLiveData<String>()
private var isRefreshing: Boolean = false
private var mSharedPreferences: SharedPreferences? = null
val dataListAdapter = ContainersUnderCheckAdapter(activity)
val backClickListener = View.OnClickListener { activity.supportFragmentManager.popBackStack() }
val filterDataByTab = object : TabLayout.OnTabSelectedListener {
override fun onTabReselected(tab: TabLayout.Tab?) {
}
override fun onTabUnselected(tab: TabLayout.Tab?) {
}
override fun onTabSelected(tab: TabLayout.Tab?) {
when (tab!!.text) {
activity.resources.getString(R.string.cidPending) -> {
listType.value = 0
getPendingData()
}
activity.resources.getString(R.string.cidDone) -> {
listType.value = 1
getDoneData()
}
}
}
}
val filterData = object : SearchView.OnQueryTextListener {
override fun onQueryTextSubmit(query: String): Boolean {
if (query.length > 2) {
val mQuery = Utility(activity).switchArabicNumerals(query)
dataListAdapter.getFilter(3, listType.value!!).filter(mQuery)
} else {
errorMessage.value = activity.resources.getString(R.string.addCorrectNumber)
}
return true
}
override fun onQueryTextChange(newText: String): Boolean {
if (newText.length > 2) {
val mQuery = Utility(activity).switchArabicNumerals(newText)
dataListAdapter.getFilter(3, listType.value!!).filter(mQuery)
}
return false;
}
}
val closeImgListener = View.OnClickListener {
mRootView.svSearchMSC.setQuery("", true)
if (listType.value == 1) {
dataListAdapter.getFilter(1, listType.value!!).filter("ANY")
} else if (listType.value == 0) {
dataListAdapter.getFilter(2, listType.value!!).filter("PENDING")
}
}
init {
listType.value = 0
mSharedPreferences = getDefaultSharedPreferences(activity.applicationContext)
toolBarTitle.value = activity.resources.getString(R.string.mscInspectTitle)
hint.value = activity.resources.getString(R.string.msc_search)
getData()
}
fun getData() {
onRetrievePostListStart()
subscription = apiAccount.getContainersUnderCheck(
"getContainersUnderCheck",
mSharedPreferences!!.getString(Constants.CFID, "")!!,
mSharedPreferences!!.getString(Constants.CFTOKEN, "")!!
)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.doOnSubscribe {}
.doOnTerminate {}
.subscribe({ result ->
result?.let {
if (result.ResponseCode != null && result.ResponseCode.trim() != "000") {
onRetrievePostListError(result.ResponseMessage)
} else {
result.ContainersData?.let { it1 -> onRetrievePostListSuccess(it1) }
}
}
}, { throwable ->
android.util.Log.e("getDataInquiry", throwable.message!!)
onRetrievePostListError(activity.resources.getString(R.string.general_error))
})
}
private fun getPendingData() {
val query = mRootView.svSearchMSC.query.toString()
if (query == "") {
dataListAdapter.getFilter(2, listType.value!!).filter("PENDING")
} else {
if (query.length > 2) {
dataListAdapter.getFilter(3, listType.value!!).filter(query)
} else {
errorMessage.value = activity.resources.getString(R.string.addCorrectNumber)
}
}
}
private fun getDoneData() {
val query = mRootView.svSearchMSC.query.toString()
if (query == "") {
dataListAdapter.getFilter(1, listType.value!!).filter("ANY")
} else {
if (query.length > 2) {
dataListAdapter.getFilter(3, listType.value!!).filter(query)
} else {
errorMessage.value = activity.resources.getString(R.string.addCorrectNumber)
}
}
}
private fun onRetrievePostListStart() {
loading.value = true
}
private fun onRetrievePostListFinish() {
loading.value = false
isRefreshing = false
}
private fun onRetrievePostListSuccess(containersData: List<ContainersData>) {
onRetrievePostListFinish()
dataListAdapter.updateInquiryAdapter(containersData as ArrayList<ContainersData>)
if (listType.value == 1) {
dataListAdapter.getFilter(1, listType.value!!).filter("ANY")
} else if (listType.value == 0) {
dataListAdapter.getFilter(2, listType.value!!).filter("PENDING")
}
}
private fun onRetrievePostListError(message: String?) {
onRetrievePostListFinish()
getDataError.value = true
errorMessage.value = message
}
override fun onCleared() {
super.onCleared()
subscription.dispose()
}
override fun onRefresh() {
isRefreshing = true
getData()
}
}
adapter is :
class ContainersUnderCheckAdapter(val activity: LaunchActivity) :
RecyclerView.Adapter<ContainersUnderCheckAdapter.ViewHolder>() {
private lateinit var mDataSet: ArrayList<ContainersData>
private lateinit var mDataSetFiltered: ArrayList<ContainersData>
fun updateInquiryAdapter(dataSet: ArrayList<ContainersData>) {
mDataSet = ArrayList()
mDataSet.clear()
mDataSet.addAll(dataSet)
mDataSetFiltered = mDataSet
getFilter(2, 1).filter("PENDING")
// notifyDataSetChanged()
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
val binding: ContianerItemFieldLayoutBinding = DataBindingUtil
.inflate(
LayoutInflater.from(parent.context),
R.layout.contianer_item_field_layout,
parent,
false
)
return ViewHolder(binding, activity)
}
override fun getItemCount(): Int {
return if (::mDataSetFiltered.isInitialized) mDataSetFiltered.size else 0
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
holder.bind(mDataSetFiltered[position])
}
operator fun get(position: Int): ContainersData {
return mDataSetFiltered.get(position)
}
/**
* #filterType :
* IF 1 : filter on Data Type RJCTD + APPROVED
* 2 : filter on Data Type PENDING
* 3 :
*/
fun getFilter(filterType: Int, listType: Int): Filter {
return object : Filter() {
override fun performFiltering(charSequence: CharSequence): FilterResults {
val charString = charSequence.toString()
mDataSetFiltered = if (charString.isEmpty()) {
mDataSet
} else {
val filteredList = ArrayList<ContainersData>()
for (row in mDataSet) {
when (filterType) {
1 -> {
if (row.status == "RJCTD" || row.status == "APPROVED") {
filteredList.add(row)
}
}
2 -> {
if (row.status == charString) {
filteredList.add(row)
}
}
3 -> {
when (listType) {
0 -> {
if ((row.CID!!.contains(charString.toUpperCase(Locale.ROOT)) || row.TN!!.contains(
charSequence
) || row.PN!!.contains(charSequence)) && row.status == "PENDING"
) {
filteredList.add(row)
}
}
1 -> {
if ((row.CID!!.contains(charString.toUpperCase(Locale.ROOT)) || row.TN!!.contains(
charSequence
) || row.PN!!.contains(charSequence)) && row.status != "PENDING"
) {
filteredList.add(row)
}
}
}
}
}
}
filteredList
}
val filterResults = FilterResults()
filterResults.values = mDataSetFiltered
return filterResults
}
override fun publishResults(
charSequence: CharSequence,
filterResults: FilterResults
) {
if (::mDataSetFiltered.isInitialized) {
mDataSetFiltered = try {
filterResults.values as ArrayList<ContainersData>
} catch (e: Exception) {
Log.e("mDataSetFiltered",e.message!!)
ArrayList()
}
when (filterType) {
1->{
mDataSetFiltered.sortWith(Comparator { p0, p1 -> p1!!.UpdateDate.compareTo(p0!!.UpdateDate) })
}
2->{
mDataSetFiltered.sortWith(Comparator { p0, p1 -> p0!!.ID!!.compareTo(p1.ID!!) })
}
}
}
// refresh the list with filtered data
notifyDataSetChanged()
}
}
}
class ViewHolder(
private val binding: ContianerItemFieldLayoutBinding,
val activity: LaunchActivity
) : RecyclerView.ViewHolder(binding.root) {
private val viewModel = MscInspectionListViewModel(activity)
fun bind(data: ContainersData) {
viewModel.bind(data)
binding.viewModel = viewModel
}
}
}
any data in this recycler on click go to fragment has tow recycler first one to show data, the second one to pick Images
the second-page code
class MSCDataFragment : Fragment() {
lateinit var rootView: View
lateinit var activity: LaunchActivity
lateinit var utility: Utility
lateinit var loadingView: LoadingView
private lateinit var viewModel: MSCDataViewModel
private lateinit var binding: FragmentMscdataBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
if (getActivity() != null) {
activity = getActivity() as LaunchActivity
utility = Utility(activity)
loadingView = LoadingView(activity)
}
}
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
binding = DataBindingUtil.inflate(inflater, R.layout.fragment_mscdata, container, false)
rootView = binding.root
initial()
return rootView
}
private fun initial() {
viewModel = ViewModelProvider(
this, ViewModelFactory(
activity,
arguments!!.getSerializable("Data") as ContainersData
)
).get(MSCDataViewModel::class.java)
binding.viewModel = viewModel
// binding.imgList.layoutManager = GridLayoutManager(activity, 3)
binding.containerInfo.layoutManager = LinearLayoutManager(activity)
binding.openCIDNotValid.typeface =
Typeface.createFromAsset(activity.assets, "Bahij_Janna-Regular.ttf")
binding.openCIDNotValid.setOnCheckedChangeListener(viewModel.onOpenCidNotValidListener)
viewModel.loading.observe(this, Observer { loading ->
loading?.let {
if (it) {
loadingView.show()
} else {
loadingView.dismiss()
}
}
})
viewModel.errorMessage.observe(this, Observer { msg ->
msg?.let {
utility.ShowToast(msg)
}
})
viewModel.imagesAdapters2.observe(this, Observer { msg ->
msg?.let {
binding.imgList.apply {
layoutManager = GridLayoutManager(activity, 3)
adapter = it
}
}
})
rootView.toolbar_Back.setOnClickListener(viewModel.backClickListener)
binding.btnAddImages.setOnClickListener(viewModel.pickImages)
binding.successContianer.setOnClickListener(viewModel.correctContainer)
binding.damagedContianer.setOnClickListener(viewModel.wrongContainer)
}
}
the view model is :
class MSCDataViewModel(val activity: LaunchActivity, val containersData: ContainersData) :
BaseViewModel(), GetImagesListener {
#Inject
lateinit var restApiAccount: RestApiAccount
val toolBarTitle: MutableLiveData<String> = MutableLiveData()
val ButtonText: MutableLiveData<String> = MutableLiveData()
var openCIDNotValidVisibility = MutableLiveData<Int>()
private val getDataError = MutableLiveData<Boolean>()
val btnImagesVisibility = MutableLiveData<Int>()
var imgeNoteVisibility = MutableLiveData<Int>()
var successVisibility = MutableLiveData<Int>()
var damagedVisibility = MutableLiveData<Int>()
var notesVisibility = MutableLiveData<Int>()
val btnVisibility = MutableLiveData<Int>()
var canNotOpen = MutableLiveData<Int>()
private val images = ArrayList<Image>()
var utility = Utility(activity)
private var CURRENTINDEX = 0
private var mSharedPreferences: SharedPreferences? = null
val DataListAdapter = ContainerDataAdapter(activity)
var imagesAdapter = ContainerImagesAdapter(activity, containersData.status!!, ArrayList())
val imagesAdapters2 = MutableLiveData<ContainerImagesAdapter2>()
val userInfo: UserInfo
val backClickListener = View.OnClickListener { activity.supportFragmentManager.popBackStack() }
val pickImages = View.OnClickListener {
pickImages()
}
val correctContainer = View.OnClickListener {}
val onOpenCidNotValidListener =
CompoundButton.OnCheckedChangeListener { buttonView, isChecked ->
if (isChecked) {
successVisibility.value = View.GONE
canNotOpen.value = 1
} else {
canNotOpen.value = 0
successVisibility.value = View.VISIBLE
}
}
val wrongContainer = View.OnClickListener {}
var mscNotes: ObservableField<String> = ObservableField("")
init {
canNotOpen.value = 0
mSharedPreferences =
PreferenceManager.getDefaultSharedPreferences(activity.applicationContext)
toolBarTitle.value = containersData.CID
ButtonText.value = activity.resources.getString(R.string.cleanContianer)
userInfo = utility.readObjectFromSharedPreferences(
mSharedPreferences,
Constants.USER_INFO_KEY,
UserInfo::class.java
) as UserInfo
openCIDNotValidVisibility.value = View.GONE
fillData()
}
private fun fillData() {
val data: LinkedHashMap<String, String> = containersData.data!!
val captionsMap = utility.readObjectFromSharedPreferences(
mSharedPreferences, Constants.CAPTIONS_MAP_KEY,
HashMap::class.java
) as HashMap<String, String>
if (containersData.data.size > 0) {
val list = ArrayList<KeyValueModel>()
for (inside in data.keys) {
val ky = captionsMap[inside]
val value = data[inside].toString()
ky?.let { KeyValueModel(it, value) }?.let { list.add(it) }
}
DataListAdapter.updateInquiryAdapter(list)
} else {
errorMessage.value = activity.resources.getString(R.string.no_data)
}
if (containersData.ImageList != null && containersData.ImageList.isNotEmpty()) {
imagesAdapter.updateContainerImagesAdapter(containersData.ImageList)
}
}
private fun pickImages() {
activity.setCallBack(this)
val pictureDialog: AlertDialog
val builder = activity.let { AlertDialog.Builder(it) }
val dialogView = View.inflate(activity, R.layout.choose_camera_method, null)
builder.setView(dialogView)
val nafithPopupContainer = dialogView.findViewById<RelativeLayout>(R.id.RLTitle)
nafithPopupContainer.setBackgroundColor(
ContextCompat.getColor(
activity,
R.color.mainColor
)
)
val popUpGallery = dialogView.findViewById<LinearLayout>(R.id.PopupGellary)
val popUpCamera = dialogView.findViewById<LinearLayout>(R.id.PopupCamera)
pictureDialog = builder.create()
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
Objects.requireNonNull<Window>(pictureDialog.window)
.setLayout(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.WRAP_CONTENT
)
} else {
if (pictureDialog.window != null) {
pictureDialog.window!!.setLayout(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.WRAP_CONTENT
)
}
}
popUpGallery.setOnClickListener {
fromGallery()
pictureDialog.dismiss()
}
popUpCamera.setOnClickListener {
fromCamera()
pictureDialog.dismiss()
}
val popupClose = dialogView.findViewById<ImageView>(R.id.popupClose)
popupClose.setOnClickListener { pictureDialog.dismiss() }
pictureDialog.show()
}
private fun fromGallery() {
ImagePicker.create(activity)
.toolbarImageTitle(activity.resources.getString(R.string.get_image))
.toolbarArrowColor(ContextCompat.getColor(activity, R.color.colorWhite))
.showCamera(false)
.limit(6)
.start()
}
private fun fromCamera() {
ImagePicker.cameraOnly().start(activity)
}
override fun onGetImage(image: Image) {
imgeNoteVisibility.value = View.GONE
imagesAdapter.updateContainerImagesAdapter(image)
images.add(image)
}
override fun addingImagesDone(mImages: MutableList<Image>) {
images.clear()
images.addAll(mImages)
imgeNoteVisibility.value = View.GONE
val listString :ArrayList<String> = ArrayList()
for (i in mImages.indices){
listString.add(mImages[i].path)
}
imagesAdapters2.value = ContainerImagesAdapter2(activity,containersData.status!!,listString)
imagesAdapters2.value!!.notifyItemRangeChanged(0,listString.size)
}
override fun onImgDelete(image: String) {
var x = 0
try {
for (i in 0 until images.size) {
x = i
if (images[i].path == image) {
images.remove(images[i])
}
}
} catch (e: Exception) {
Log.e("errorImages", e.message!!)
Log.e("xx", x.toString())
}
}
private fun onRetrievePostListStart() {
loading.value = true
}
private fun onRetrievePostListFinish() {
loading.value = false
}
private fun onRetrievePostListSuccess(msg: String?) {
onRetrievePostListFinish()
}
private fun onRetrievePostListError(message: String?) {
onRetrievePostListFinish()
getDataError.value = true
errorMessage.value = message
}
}
Adapter code is :
class ContainerImagesAdapter2() : RecyclerView.Adapter<ContainerImagesAdapter2.ViewHolder>() {
var status: String = ""
lateinit var activity: LaunchActivity
lateinit var utility: Utility
constructor(
mActivity: LaunchActivity,
mStatus: String,
pathsList: ArrayList<String>
) : this() {
activity = mActivity
pathsDataSet = pathsList
status = mStatus
utility = Utility(activity)
}
private var pathsDataSet: ArrayList<String> = ArrayList()
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
val binding: ContianerImageFieldBinding = DataBindingUtil
.inflate(
LayoutInflater.from(parent.context),
R.layout.contianer_image_field,
parent,
false
)
return ViewHolder(binding, activity)
}
override fun getItemCount(): Int {
return pathsDataSet.size
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
holder.bindPath(pathsDataSet[position], position)
}
inner class ViewHolder(
private val binding: ContianerImageFieldBinding,
val activity: LaunchActivity
) : RecyclerView.ViewHolder(binding.root) {
private val viewModel = MscImagesListViewModel(activity)
fun bindPath(data: String, position: Int) {
viewModel.bindPath(data)
binding.viewModel = viewModel
if (status != "PENDING") {
binding.closeImg.visibility = View.GONE
}
binding.closeImg.setOnClickListener {}
binding.mainImg.setOnClickListener {
val fragment = FullImageFragment()
val bundle = Bundle()
val list = ArrayList<String>()
for (item in 0 until pathsDataSet.size) {
list.add(pathsDataSet[item])
}
bundle.putSerializable("ImageList", list)
bundle.putInt("Position", position)
fragment.arguments = bundle
activity.supportFragmentManager.beginTransaction()
.replace(R.id.fragment_container, fragment).addToBackStack(fragment.tag)
.commit()
}
}
}
}
if you filter data using search view in the first-page and pick images in the second page , list of picked images doesn't appear, if you going to the second page without filtering data everything ok
solve Problem found
Just Update constraint-layout library in gradle dependencies to version '2.0.0-beta4'