I am developing tvshows app where I am implementing following logic user search tvshows and filtered result has to show in recyclerview but I want to implement filtering functionality in viewmodel
how can I achieve that
below interface class
interface ApiInterface {
#GET("search/shows")
suspend fun searchShows( #Query("q") query: String): Call<TvMazeResponse>
}
below TvRepository.kt
class TvRepository(private val apiInterface: ApiInterface) {
suspend fun getShows() = apiInterface.searchShows("")
}
below adapter class
class TvAdapter : RecyclerView.Adapter<TvAdapter.ViewHolder>(), Filterable {
lateinit var tvMazeList: MutableList<TvMazeResponse>
lateinit var filterResult: ArrayList<TvMazeResponse>
override fun getItemCount(): Int =
filterResult.size
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) = ViewHolder(
LayoutInflater.from(parent.context).inflate(
R.layout.tv_item, parent,
false
)
)
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
holder.bind(filterResult[position])
}
fun addData(list: List<TvMazeResponse>) {
tvMazeList = list as MutableList<TvMazeResponse>
filterResult = tvMazeList as ArrayList<TvMazeResponse>
notifyDataSetChanged()
}
override fun getFilter(): Filter {
return object : Filter() {
override fun performFiltering(constraint: CharSequence?): FilterResults {
val charString = constraint?.toString() ?: ""
if (charString.isEmpty()) filterResult =
tvMazeList as ArrayList<TvMazeResponse> else {
val filteredList = ArrayList<TvMazeResponse>()
tvMazeList
.filter {
(it.name.contains(constraint!!)) or
(it.language.contains(constraint))
}
.forEach { filteredList.add(it) }
filterResult = filteredList
}
return FilterResults().apply { values = filterResult }
}
override fun publishResults(constraint: CharSequence?, results: FilterResults?) {
filterResult = if (results?.values == null)
ArrayList()
else
results.values as ArrayList<TvMazeResponse>
notifyDataSetChanged()
}
}
}
class ViewHolder(view: View) : RecyclerView.ViewHolder(view) {
fun bind(result: TvMazeResponse) {
with(itemView) {
Picasso.get().load(result.image.medium).into(imageView)
}
}
}
}
below Constants.kt
object Constants {
const val BASE_URL = "https://api.tvmaze.com/"
}
below TvMazeResponse.kt
data class TvMazeResponse(
#SerializedName("averageRuntime")
val averageRuntime: Int,
#SerializedName("dvdCountry")
val dvdCountry: Any,
#SerializedName("externals")
val externals: Externals,
#SerializedName("genres")
val genres: List<String>,
#SerializedName("id")
val id: Int,
#SerializedName("image")
val image: Image,
#SerializedName("language")
val language: String,
#SerializedName("_links")
val links: Links,
#SerializedName("name")
val name: String,
#SerializedName("network")
val network: Network,
#SerializedName("officialSite")
val officialSite: String,
#SerializedName("premiered")
val premiered: String,
#SerializedName("rating")
val rating: Rating,
#SerializedName("runtime")
val runtime: Int,
#SerializedName("schedule")
val schedule: Schedule,
#SerializedName("status")
val status: String,
#SerializedName("summary")
val summary: String,
#SerializedName("type")
val type: String,
#SerializedName("updated")
val updated: Int,
#SerializedName("url")
val url: String,
#SerializedName("webChannel")
val webChannel: Any,
#SerializedName("weight")
val weight: Int
)
below TvViewModel.kt
class TvViewModel(apiInterface: ApiInterface) : ViewModel() {
}
I want to implement filter and search function in viewmodel how can I achieve that any help and tips greatly appreciated
In TvRepository change the getShows function to
suspend fun getShows(searchString:String) = apiInterface.searchShows(searchString)
Then in the ViewModel change the constructor to get an instance of the TVRepository and call API as shown below
class TvViewModel( tvRepository: TvRepository) : ViewModel() {
fun getShows(searchParameter:String){
viewModelScope.launch(Dispatchers.IO){
val response= tvRepository.getShows().awaitResponse()
if(response.isSuccessful{
//api success you can get result from response.body
}
else{
//api failed
}
}
}
}
Related
Hello Im working on project using Kotlin (for the first time). My problem is i don't know how to get data "fullname" inside "data" list where one of the data inside the list also getting use for the condition, the name is "Role" to my RecyclerView in my Activity.
So i need show list "fullname" inside my RecyclerView where the "Role" is "Patient".
Here is the DataFileRecord:
data class DataFileRecord(
#field:SerializedName("total")
val total: Int? = null,
#field:SerializedName("data")
val data: List<DataItem>? = null,
#field:SerializedName("offset")
val offset: Int? = null,
#field:SerializedName("limit")
val limit: Int? = null,
#field:SerializedName("status")
val status: Int? = null
)
//get fullname
data class DataItem(
#field:SerializedName("updateDate")
val updateDate: String? = null,
#field:SerializedName("departement")
val departement: Departement? = null,
#field:SerializedName("isBlock")
val isBlock: String? = null,
#field:SerializedName("role")
val role: Role? = null,
#field:SerializedName("fullname")
val fullname: String? = null,
#field:SerializedName("id")
val id: String? = null,
#field:SerializedName("username")
val username: String? = null
)
//get role = patient
data class Role(
#field:SerializedName("name")
val name: String? = null,
#field:SerializedName("id")
val id: String? = null
)
data class Departement(
#field:SerializedName("name")
val name: String? = null,
#field:SerializedName("id")
val id: String? = null
)
Here is my Adapter:
class PatientAdapter(val context: List<DataItem>) : RecyclerView.Adapter<PatientAdapter.MyViewHolder>() {
var patientList: List<DataItem> = listOf()
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyViewHolder {
val view = LayoutInflater.from(parent.context).inflate(R.layout.item_patient,parent,false)
return MyViewHolder(view)
}
override fun getItemCount(): Int {
return patientList.size
}
override fun onBindViewHolder(holder: MyViewHolder, position: Int){
holder.patientName.text = patientList.get(position).fullname
}
#SuppressLint("NotifyDataSetChanged")
fun setPatientListItems(patientList: List<DataItem>){
this.patientList=patientList
notifyDataSetChanged()
}
class MyViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
val patientName: TextView = itemView.findViewById(R.id.itemPatientName)
}
}
And here is where i stuck with my Call api in my activity:
//this 4 lines are inside oncreate
patientRecyclerView.setHasFixedSize(true)
linearLayoutManager = LinearLayoutManager(this)
patientRecyclerView.layoutManager = linearLayoutManager
patientRecyclerView.adapter = recyclerAdapter
fun getPatient(apiKey: String, apiSecret: String, token: String){
val retrofit = RetrofitClient.getClient()
retrofit.addGetPatient(apiKey,apiSecret,token)
.enqueue(object : Callback<DataFileRecord>{
override fun onResponse(call: Call<DataFileRecord>, response: Response<DataFileRecord>) {
TODO("Not yet implemented")
val dataPatient = response.body()
if (response.isSuccessful){
---------------- HERE WHERE I STUCK --------------------
}
}
override fun onFailure(call: Call<DataFileRecord>, t: Throwable) {
TODO("Not yet implemented")
}
})
}
Fragment into set list in adapter Code below :
binding.gvFolder.layoutManager = layoutManager
binding.gvFolder.adapter = AdapterGalleryPhotosFolder(this#GalleryImageFolderActivity,al_images,activityAddress)
below my Model class code:
data class ModelGalleryImagesFolder(val name:String, var al_imagepath: ArrayList<String>)
and below my full adapter code:
class AdapterGalleryPhotosFolder(
var context: Context,
var arrayList: ArrayList<ModelGalleryImagesFolder>,
var activityAddress: String?
):RecyclerView.Adapter<RecyclerView.ViewHolder>(){
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
return MyViewHodler(AdapterPhotosfolderBinding.inflate(LayoutInflater.from(context),parent,false))
}
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
(holder as MyViewHodler)
val modelImages = arrayList[position]
holder.binding.tvFolder.text = modelImages.name
holder.binding.tvFolder2.text = arrayList[position].al_imagepath.size.toString()+" photo"
Glide.with(context).load(arrayList[position].al_imagepath[0])
.diskCacheStrategy(DiskCacheStrategy.NONE)
.skipMemoryCache(true)
.into(holder.binding.ivImage)
holder.binding.secondlayout.setOnClickListener {
val intent = Intent(context, GalleryImageFolderIntoImagesActivity::class.java)
intent.putExtra("position", position)
intent.putExtra("value", activityAddress)
context.startActivity(intent)
(context as Activity).finish()
}
}
override fun getItemCount(): Int {
return arrayList.size
}
class MyViewHodler(var binding: AdapterPhotosfolderBinding) : RecyclerView.ViewHolder(binding.root)
}
Got the answer.. just put this inside response= isSuccessfull
namePatient?.data?.filter { it.role?.name=="Pasien" }.let { if (it != null) { dataP.addAll(it) } }
reciAdapter.notifyDataSetChanged()
So "dataP" is my lateinit var MutableList, and "reciAdapter" is my Recycler Adapter Class
I'm trying to have item removed from my list but I get following error
Unresolved reference. None of the following candidates is applicable because of receiver type mismatch:
public inline fun <T> MutableCollection<out TypeVariable(T)>.remove(element: TypeVariable(T)): Boolean defined in kotlin.collections
public inline fun <T> MutableList<TypeVariable(T)>.remove(index: Int): TypeVariable(T) defined in kotlin.collections
public inline fun <K, V> MutableMap<out TypeVariable(K), TypeVariable(V)>.remove(key: TypeVariable(K)): TypeVariable(V)? defined in kotlin.collections
Code
class EducationsAdapter(val context: Context?, private var educationList: MyEducations) : RecyclerView.Adapter<EducationsAdapter.EducationsAdapterViewHolder>() {
override fun getItemCount()= educationList.data.size
// other functions...
override fun onBindViewHolder(holder: EducationsAdapter.EducationsAdapterViewHolder, position: Int)
{
holder.educationDelete.setOnClickListener {
deleteMyEducations(currentItem.id, position)
}
}
//delete
private fun deleteMyEducations(id: String, position: Int) {
// ".remove" is returning error above
educationList.remove(position)
notifyDataSetChanged()
}
}
Any suggestions?
Update
My MyEducations class (rendering data coming from server)
data class MyEducations(
val data: List<Education>,
val message: String
) { }
data class Education(
val id: String,
val start: String,
val end: String,
val title: String,
val body: String,
val user: User,
val created_at: String,
val updated_at: String,
) {}
Update 2
I've made following changes
// add
val list = mutableListOf<MyEducations>()
private fun deleteMyEducations(id: String, position: Int) {
//changed to
list.remove(educationList.data[position])
notifyDataSetChanged()
}
What it does is make flashing remove of the item (meaning: for less than a second my item removes and back again to the list)!
Solved
//changed my list to `ArrayList<Education>`
class EducationsAdapter(val context: Context?, private var educationList: ArrayList<Education>) : RecyclerView.Adapter<EducationsAdapter.EducationsAdapterViewHolder>() {
// and my delete code to
private fun deleteMyEducations(id: String, position: Int) {
educationList.removeAt(position)
notifyItemRemoved(position)
notifyItemRangeChanged(position, educationList.size)
notifyDataSetChanged()
}
}
Also I had to change my class to ArrayList as well
data class MyEducations(
val data: ArrayList<Education>,
val message: String
) { }
Try With
val list = mutableListOf<MyEducations>()
private fun deleteMyEducations(id: String, position: Int) {
list.removeAt(position)
notifyDataSetChanged()
}
You should be able to delete with something like:
class EducationListAdapter: RecyclerView.Adapter<EducationListAdapter.EducationViewHolder>() {
var educationList: MutableList<Education> = mutableListOf()
override fun onBindViewHolder(holder: EducationViewHolder, position: Int) {
val current = educationList.toList()
holder.view.setOnClickListener {
educationList.remove(current[position])
notifyItemRemoved(position)
}
}
}
So I have a RecyclerView which data is populated with data from some Data Class. I want to Apply a search item function for this recyclerview. Back when I don't use Data Classes, I followed this tutorial (The tutorial is in Java).
Now that I'm using a data class since I fetch the data that populate the recyclerview from an API endpoint, I'm quite confused on how to apply the search function in the current recyclerview.
(I fetch the data using library Retrofit if you wondering.)
This is the code snippet from the fragment:
RefundListFragment.kt
private fun fetchRefundListData() {
NetworkConfig().getRefundListDetailService().getRefundList().enqueue(object :
Callback<RefundListPOJODataClass> {
override fun onFailure(call: Call<RefundListPOJODataClass>, t: Throwable) {
...
...
...
}
override fun onResponse(
call: Call<RefundListPOJODataClass>,
response: Response<RefundListPOJODataClass>
) {
binding.refundProgressBar.visibility = View.GONE
binding.rvRefundList.adapter =
response.body()?.let { RefundListAdapter(it, this#RefundListFragment) }
fun filterString(text: String) {
val filteredList: List<RefundListPOJODataClass> = ArrayList()
for (item in response.body()?.data!!) {
if (item != null) {
if (item.buyerName?.toLowerCase()?.contains(text.toLowerCase())!!) {
filteredList.add(item)
}
}
}
adapter.filterList(filteredList)
}
binding.refundListSearchBar.addTextChangedListener(object : TextWatcher {
override fun afterTextChanged(s: Editable?) {
filterString(s.toString())
}
override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {
}
override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {
}
})
}
})
}
However it returned an error:
Unresolved reference: add
This is the recyclerview Adapter:
RefundListAdapter.kt
class RefundListItemHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
private val refundedOrderId: TextView = itemView.refundedOrderId
private val orderDateTime: TextView = itemView.orderDateTime
private val refundedCustomerName: TextView = itemView.refundedCustomerName
fun bind(
refundHistory: RefundListPOJODataClassDataItem,
clickListener: RefundListOnItemClickListener
) {
refundedOrderId.text = refundHistory.orderId
orderDateTime.text = refundHistory.orderDate
refundedCustomerName.text = refundHistory.buyerName
itemView.setOnClickListener {
clickListener.onItemClicked(refundHistory)
}
}
}
class RefundListAdapter(
private var refundList: RefundListPOJODataClass,
private val itemClickListener: RefundListOnItemClickListener
) : RecyclerView.Adapter<RefundListItemHolder>() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RefundListItemHolder {
val v = LayoutInflater.from(parent.context)
.inflate(R.layout.refund_list_item_layout, parent, false)
return RefundListItemHolder(v)
}
override fun getItemCount(): Int {
if (refundList.data != null) {
return refundList.data!!.size
} else {
return 0
}
}
override fun onBindViewHolder(holder: RefundListItemHolder, position: Int) {
val refundHistory = refundList.data?.get(position)
if (refundHistory != null) {
holder.bind(refundHistory, itemClickListener)
}
}
fun filterList(filteredList: List<RefundListPOJODataClass>) { //This is the function I called in the fragment
refundList = filteredList.get(0)
notifyDataSetChanged()
}
}
interface RefundListOnItemClickListener {
fun onItemClicked(refundHistory: RefundListPOJODataClassDataItem)
}
And this is how the data class looks like
RefundListPOJODataClass.kt
data class RefundListPOJODataClass(
#field:SerializedName("data")
val data: List<RefundListPOJODataClassDataItem?>? = null,
#field:SerializedName("error")
val error: Error? = null
)
data class RefundListPOJODataClassError(
#field:SerializedName("msg")
val msg: String? = null,
#field:SerializedName("code")
val code: Int? = null,
#field:SerializedName("status")
val status: Boolean? = null
)
data class RefundListPOJODataClassDataItem(
#field:SerializedName("order_date")
val orderDate: String? = null,
#field:SerializedName("buyer_name")
val buyerName: String? = null,
#field:SerializedName("phone_number")
val phoneNumber: String? = null,
#field:SerializedName("status_refund")
val statusRefund: String? = null,
#field:SerializedName("order_id")
val orderId: String? = null,
#field:SerializedName("refund")
val refund: Int? = null,
#field:SerializedName("refund_date")
val refundDate: String? = null
)
I want to search the recyclerview item by the attribute buyerName. How can I achieve it ? What should i change in my code? If there's any detail I missed to tell, Just let me know.
Your filtered list is of type List, which is immutable.
Change it to array list or MutableList:
val filteredList: ArrayList<RefundListPOJODataClass> = ArrayList()
hello guys I am using a gson api with a Recyclerview and I got this error with the adapter and it says that the
expression 'myAdapter' of type 'myAdapter' cannot be invoked as a function. the function 'invoked' is not found
and when I run the code without the data it gives me this error:
lateinit property myAdapter has not been initialized
my activities and classes
private val datalist: MutableList<Sura> = mutableListOf()
private lateinit var myAdapter: MyAdapter
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_quran_a_p_i)
myAdapter = myAdapter(datalist) // here is the error
apiQuranRecyclerView.layoutManager = LinearLayoutManager(this)
apiQuranRecyclerView.addItemDecoration(DividerItemDecoration(this, OrientationHelper.VERTICAL))
apiQuranRecyclerView.adapter = myAdapter
AndroidNetworking.initialize(this)
AndroidNetworking.get("http://api.islamhouse.com/v1/mykey/quran/get-category/462298/ar/json/")
.build()
.getAsObject(Surasnames::class.java, object : ParsedRequestListener<Surasnames>{
override fun onResponse(response: Surasnames) {
datalist.addAll(response.suras)
myAdapter.notifyDataSetChanged()
}
override fun onError(anError: ANError?) {
}
})
}
my Adapter
class MyAdapter (private val datalist: MutableList<Sura>): RecyclerView.Adapter<myHolder>() {
private lateinit var context: Context
override fun onCreateViewHolder(parent: ViewGroup, p1: Int): myHolder {
context = parent.context
return myHolder(LayoutInflater.from(context).inflate(R.layout.api_quran_view, parent, false))
}
override fun getItemCount(): Int = datalist.size
override fun onBindViewHolder(holder: myHolder, position: Int) {
val data = datalist[position]
val apisuraname = holder.itemView.apiSuraNames
val surasnames = "${data.id} ${data.title}"
apisuraname.text = surasnames
holder.itemView.setOnClickListener {
Toast.makeText(context, surasnames, Toast.LENGTH_SHORT).show()
}
}
my Holder
class myHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
}
my Models
data class Sura(
#SerializedName("add_date")
val addDate: Int,
#SerializedName("api_url")
val apiUrl: String,
#SerializedName("id")
val id: Int,
#SerializedName("title")
val title: String,
#SerializedName("type")
val type: String
)
the second one
data class Surasnames(
#SerializedName("add_date")
val addDate: Int,
#SerializedName("description")
val description: String,
#SerializedName("id")
val id: Int,
#SerializedName("locales")
val locales: List<Any>,
#SerializedName("suras")
val suras: List<Sura>,
#SerializedName("title")
val title: String)
thanks in advance
You need to call the constructor. Change the line
myAdapter = myAdapter(datalist)
to
myAdapter = MyAdapter(datalist)
I am trying to use Groupie to create a recyclerview with HeaderItems. I have Group of Data like this
class Group(
val id: String = generateId(),
val name: String? = null,
val entries: List<Entry>? = null
) : Item(), Parcelable {
override fun bind(viewHolder: GroupieViewHolder, position: Int) {
viewHolder.apply {
itemView.tvGroupName.text = name
}
}
override fun getLayout() = R.layout.group_single_item
constructor(source: Parcel) : this(
source.readString(),
source.readString(),
source.createTypedArrayList(Entry.CREATOR)
)
override fun describeContents() = 0
override fun writeToParcel(dest: Parcel, flags: Int) = with(dest) {
writeString(id)
writeString(name)
writeTypedList(entries)
}
companion object {
private fun generateId(): String {
return UUID.randomUUID().toString()
}
#JvmField
val CREATOR: Parcelable.Creator<Group> = object : Parcelable.Creator<Group> {
override fun createFromParcel(source: Parcel): Group = Group(source)
override fun newArray(size: Int): Array<Group?> = arrayOfNulls(size)
}
}
}
Every group has a list of entries
data class Entry(val id: Long=0, val name: String) : Parcelable {
constructor(parcel: Parcel) : this(
parcel.readLong(),
parcel.readString()
) {
}
override fun writeToParcel(parcel: Parcel, flags: Int) {
parcel.writeLong(id)
parcel.writeString(name)
}
override fun describeContents(): Int {
return 0
}
companion object CREATOR : Parcelable.Creator<Entry> {
override fun createFromParcel(parcel: Parcel): Entry {
return Entry(parcel)
}
override fun newArray(size: Int): Array<Entry?> {
return arrayOfNulls(size)
}
}
}
So I am trying to show a list of Groups along with their respective Entries. So I will be showing a Group with its name and the list of entries. So I thought of using Groupie for this one.
This is what I have been trying
val linearLayoutManager = LinearLayoutManager(this, RecyclerView.VERTICAL, false)
val groups = intent.getParcelableArrayListExtra<Group>("groups")
val groupAdapter = GroupAdapter<GroupieViewHolder>().apply {
val section = Section(Group())
section.setHeader(Group())
section.addAll(groups)
this.add(section)
}
recyclerViewGroups.apply {
layoutManager = linearLayoutManager
adapter = groupAdapter
}
But I am not quite sure, how to add the Group along with its Entries. Any help would be appreciated. Thanks
First you need to create item classes for your groups (possibly header and entry).
Follow instructions in this section.
E.g. those could be:
class HeaderItem(private val groupName: String) : Item() {
//... to be implemented
}
and
class EntryItem(private val entryName: String) : Item() {
//... to be implemented
}
and then use them in your adapter (needs to be tested, I'm writing this off the top of my head):
val groupAdapter = GroupAdapter<GroupieViewHolder>().apply {
groups.forEach { group ->
val section = Section()
section.setHeader(HeaderItem(group.name))
section.addAll(group.entries.map{ it -> EntryItem(it.name) })
this.add(section)
}
}