I want to implement search in my recyclerview. The data I am retrieving is from firebase (firestore). When I click on the search icon and type any text, my application crashes and I am not able to figure out what could be the reason. Can anyone tell me what is wrong with my code?
class ActivityStudentList : AppCompatActivity() {
private lateinit var studentRecyclerView: RecyclerView
private lateinit var recyclerViewAdapter: RecyclerViewAdapter
private val db = FirebaseFirestore.getInstance()
private lateinit var studentArrayList: ArrayList<StudentRecyclerViewDataClass>
private lateinit var editStudentFab: ExtendedFloatingActionButton
private lateinit var addStudentFab: ExtendedFloatingActionButton
private lateinit var removeStudentFab: ExtendedFloatingActionButton
private lateinit var tempArrayList: ArrayList<StudentRecyclerViewDataClass>
private lateinit var newArrayList: ArrayList<StudentRecyclerViewDataClass>
private var clicked = false
private lateinit var obstructor: RelativeLayout
private val fromBottom: Animation by lazy { AnimationUtils.loadAnimation(this,R.anim.from_bottom_anim) }
private val toBottom: Animation by lazy { AnimationUtils.loadAnimation(this,R.anim.to_bottom_anim) }
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_student_list)
supportActionBar?.setDisplayHomeAsUpEnabled(false)
actionBar?.setDisplayShowTitleEnabled(false)
supportActionBar?.setDisplayShowTitleEnabled(false)
editStudentFab = findViewById(R.id.editStudentFab)
addStudentFab = findViewById(R.id.addStudentFab)
removeStudentFab = findViewById(R.id.removeStudentFab)
obstructor = findViewById(R.id.obstructor)
studentRecyclerView = findViewById(R.id.studentRecyclerView)
studentRecyclerView.layoutManager = LinearLayoutManager(this)
studentRecyclerView.setHasFixedSize(true)
studentArrayList = arrayListOf<StudentRecyclerViewDataClass>()
recyclerViewAdapter = RecyclerViewAdapter(studentArrayList)
studentRecyclerView.adapter = recyclerViewAdapter
studentRecyclerView.addOnScrollListener(object : RecyclerView.OnScrollListener() {
override fun onScrolled(studentRecyclerView: RecyclerView, dx: Int, dy: Int) {
if (dy > 0) editStudentFab.hide() else if (dy < 0) editStudentFab.show()
}
})
getStudentData()
editStudentFab.setOnClickListener {
onEditButtonClicked()
}
addStudentFab.setOnClickListener {
startActivity(Intent(this,ActivityAddStudent::class.java))
}
removeStudentFab.setOnClickListener {
startActivity(Intent(this,ActivityAddStudent::class.java))
}
}
override fun onCreateOptionsMenu(menu: Menu?): Boolean {
menuInflater.inflate(R.menu.student_recycler_view_search, menu)
val search = menu?.findItem(R.id.student_recycler_view_search)
val searchView = search?.actionView as? SearchView
searchView?.isSubmitButtonEnabled = true
searchView?.setOnQueryTextListener(object: SearchView.OnQueryTextListener{
override fun onQueryTextSubmit(query: String?): Boolean {
TODO("Not yet implemented")
}
#SuppressLint("NotifyDataSetChanged")
override fun onQueryTextChange(newText: String?): Boolean {
tempArrayList.clear()
val searchText = newText!!.toLowerCase(Locale.getDefault())
if(searchText.isNotEmpty()){
newArrayList.forEach{
if(it.Name!!.toLowerCase(Locale.getDefault()).contains(searchText)){
tempArrayList.add(it)
}
}
studentRecyclerView.adapter!!.notifyDataSetChanged()
}else{
tempArrayList.clear()
tempArrayList.addAll(newArrayList)
studentRecyclerView.adapter!!.notifyDataSetChanged()
}
return false
}
})
return super.onCreateOptionsMenu(menu)
}
private fun onEditButtonClicked() {
setVisibility(clicked)
setAnimation(clicked)
clicked = !clicked
}
#SuppressLint("SetTextI18n")
private fun setAnimation(clicked: Boolean) {
obstructor = findViewById(R.id.obstructor)
addStudentFab = findViewById(R.id.addStudentFab)
removeStudentFab = findViewById(R.id.removeStudentFab)
editStudentFab = findViewById(R.id.editStudentFab)
if(!clicked){
obstructor.visibility = View.VISIBLE
editStudentFab.text = "Close"
editStudentFab.setIconResource(R.drawable.ic_outline_close_24)
addStudentFab.startAnimation(fromBottom)
removeStudentFab.startAnimation(fromBottom)
}else{
obstructor.visibility = View.INVISIBLE
editStudentFab.text = "Edit List"
editStudentFab.setIconResource(R.drawable.ic_outline_edit_24)
addStudentFab.startAnimation(toBottom)
removeStudentFab.startAnimation(toBottom)
}
}
private fun setVisibility(clicked: Boolean) {
addStudentFab = findViewById(R.id.addStudentFab)
removeStudentFab = findViewById(R.id.removeStudentFab)
if(!clicked){
addStudentFab.visibility = View.VISIBLE
removeStudentFab.visibility = View.VISIBLE
}else{
addStudentFab.visibility = View.INVISIBLE
removeStudentFab.visibility = View.INVISIBLE
}
}
private fun getStudentData() {
db.collection("StudentInfo").addSnapshotListener(object: EventListener<QuerySnapshot>{
#SuppressLint("NotifyDataSetChanged")
override fun onEvent(value: QuerySnapshot?, error: FirebaseFirestoreException?) {
if(error != null){
Log.e("Firestore Error",error.message.toString())
return
}
for(dc : DocumentChange in value?.documentChanges!!){
if(dc.type == DocumentChange.Type.ADDED){
studentArrayList.add(dc.document.toObject(StudentRecyclerViewDataClass::class.java))
}
}
recyclerViewAdapter.notifyDataSetChanged()
}
})
}
}
RecyclerView Adapter code:
class RecyclerViewAdapter(private var studentList : ArrayList<StudentRecyclerViewDataClass>) : RecyclerView.Adapter<RecyclerViewAdapter.RecyclerViewHolder>() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerViewHolder {
val itemView = LayoutInflater.from(parent.context).inflate(R.layout.recycler_view_student_list_item,parent,false)
return RecyclerViewHolder(itemView)
}
override fun onBindViewHolder(holder: RecyclerViewHolder, position: Int) {
val currentItem = studentList[position]
holder.name.text = currentItem.Name
holder.SRN.text = currentItem.SRN
holder.phone.text = currentItem.Phone
holder.age.text = currentItem.Age
}
override fun getItemCount(): Int {
return studentList.size
}
class RecyclerViewHolder(itemView : View ) : RecyclerView.ViewHolder(itemView){
val name : TextView = itemView.findViewById(R.id.tv_name)
val SRN : TextView = itemView.findViewById(R.id.tv_srn)
val phone : TextView = itemView.findViewById(R.id.tv_phone)
val age : TextView = itemView.findViewById(R.id.tv_age)
}
}
Related
I've got a recyclerView with checkBoxes and I want to select all of them at once by pressing a button
I've try this so far...
This is the Adapter -
class AdapterNfs (context: MainActivity, private val listener: OnItemClickListener) :
RecyclerView.Adapter<AdapterNfs.NfViewHolder>() {
private val context: Context
private var list = emptyList<ReceiverPayment>()
private var status: Boolean = false
init {
this.context = context
}
fun setAllChecked(isChecked: Boolean){
status = isChecked
Log.d("***log", status.toString())
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): NfViewHolder {
val binding = ItemNotasFiscaisBinding.inflate(LayoutInflater.from(parent.context), parent, false)
return NfViewHolder(binding)
}
override fun onBindViewHolder(holder: NfViewHolder, position: Int){
holder.binding.apply {
textNFnumber.text = list[position].id
textNFvalue.text = list[position].totalValue
nfStatus.text = list[position].paid.toString()
}
holder.binding.checkBoxNf.isChecked = status
}
override fun getItemCount() = list.size
inner class NfViewHolder(var binding: ItemNotasFiscaisBinding) : RecyclerView.ViewHolder(binding.root), View.OnClickListener{
init{
binding.checkBoxNf.setOnClickListener(this)
}
override fun onClick(view: View?) {
val checkbox = view as CheckBox
val position : Int = adapterPosition
val data = list
if (position != RecyclerView.NO_POSITION) {
listener.onItemClick(position, checkbox.isChecked, data)
}
}
}
fun setData(newList: List<ReceiverPayment>) {
val nfDiffUtil = DiffUtilGeneric(list, newList)
val nfResult = DiffUtil.calculateDiff(nfDiffUtil)
this.list = newList
nfResult.dispatchUpdatesTo(this)
}
interface OnItemClickListener {
fun onItemClick(
position: Int,
checked: Boolean,
data: List<ReceiverPayment>
)
}
}
and this is the Main -
class MainActivity() : AppCompatActivity(), AdapterNfs.OnItemClickListener {
private val myAdapter = AdapterNfs(this, this)
private lateinit var binding : ActivityMainBinding
private lateinit var mainViewModel: MainViewModel
private lateinit var recyclerView: RecyclerView
private var valueTotal = 00.00
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityMainBinding.inflate(layoutInflater)
mainViewModel = MainViewModel()
setupRecyclerView()
getAllNfs()
setContentView(binding.root)
binding.selectAllCheckBoxes.setOnClickListener {
setAllCheckBoxes(true)
}
}
override fun onItemClick(position: Int, checked: Boolean, data: List<ReceiverPayment>) {
val totalValueSelected = binding.totalValue
val nfSelected = data[position].totalValue.toString().toDouble()
if (checked) {
valueTotal += nfSelected
totalValueSelected.text = valueTotal.toString()
} else {
valueTotal -= nfSelected
totalValueSelected.text = valueTotal.toString()
}
}
private fun setupRecyclerView(){
recyclerView = binding.recyclerViewNfs
binding.recyclerViewNfs.layoutManager = LinearLayoutManager(this)
binding.recyclerViewNfs.itemAnimator = DefaultItemAnimator()
binding.recyclerViewNfs.addItemDecoration(
DividerItemDecoration(
this,
LinearLayoutManager.VERTICAL
)
)
binding.recyclerViewNfs.adapter = myAdapter
}
private fun getAllNfs(){
mainViewModel.allNfs.observe(this#MainActivity){response ->
response.let {
myAdapter.setData(it[0].receiverPayments as List<ReceiverPayment>)
}
}
}
private fun setAllCheckBoxes(isChecked: Boolean){
myAdapter.setAllChecked(isChecked)
}
}
In your adapter , call notifyDataSetChanged() after change the status
fun setAllChecked(isChecked: Boolean){
status = isChecked
Log.d("***log", status.toString())
notifyDataSetChanged()
}
I want to show progressBar from activity (in SubitemAdapter.kt) when elements in recyclerView are being loaded (about 150 to populate) but now this progressBar not shows at all. Here is my code:
CurrencyListFragment.kt
class CurrencyListFragment : Fragment(), MainContract.View {
companion object {
private val TAG = CurrencyListFragment::class.qualifiedName
}
private val restModel: RestModel = RestModel()
private val handler: Handler = Handler(Looper.getMainLooper())
private lateinit var mainPresenter: MainPresenter
private lateinit var itemAdapter: ItemAdapter
private lateinit var _layoutManager: LinearLayoutManager
private lateinit var onChangeFragment: OnChangeFragment
private lateinit var currentDate: String
private var isLoading: Boolean = false
private var apiResponseList: MutableList<ApiResponse> = arrayListOf()
private var listSize: Int = 0
override fun onAttach(context: Context) {
super.onAttach(context)
try {
if (activity is OnChangeFragment) onChangeFragment = activity as OnChangeFragment
} catch (error: ClassCastException) {
error.message?.let { Log.e(TAG, it) }
}
}
// #formatter:off
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
return inflater.inflate(R.layout.currency_list_fragment, container, false)
}
// #formatter:on
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
_layoutManager = LinearLayoutManager(activity)
mainPresenter = MainPresenter(this, restModel, SharedPreferencesModel(activity as Activity))
currentDate = mainPresenter.convertCurrentDate()
if (mainPresenter.checkIfSuchDateExistsinSp(currentDate)) {
Log.i(TAG, "Date $currentDate already exists in SharedPreferences")
mainPresenter.processDateWithoutMakingACall(currentDate)
} else {
mainPresenter.makeACall(currentDate)
Log.i(TAG, "Date $currentDate does not exist in SharedPreferences. Retrofit call made")
}
mainPresenter.saveNumberOfMinusDaysIntoSp(0)
addScrollerListener()
}
override fun showProgressBarOnLoadingCurrencies() {
progress_bar.visibility = View.VISIBLE
}
override fun hideProgressBarOnFinishedLoadingCurrencies() {
progress_bar.visibility = View.GONE
}
override fun setRecyclerViewStateToLoading() {
if (apiResponseList.size > 0) {
apiResponseList.add(ApiResponse("", "", listOf(Currency("", 0f)), true))
itemAdapter.notifyItemInserted(apiResponseList.size - 1)
}
}
override fun removeRecyclerViewStetOfLoading() {
if (apiResponseList.size > 1) {
apiResponseList.removeAt(apiResponseList.size - 1)
listSize = apiResponseList.size
itemAdapter.notifyItemRemoved(listSize)
}
isLoading = false
}
override fun getApiResponseList(): List<ApiResponse> {
return apiResponseList
}
override fun showLogAboutExistingDateInSp(date: String) {
Log.i(TAG, "Date $date already exists in SharedPreferences (new element)")
}
override fun showLogAboutNotExistingDateInSp(date: String) {
Log.i(TAG, "Date $date does not exist in SharedPreferences. Retrofit call made (new element)")
}
override fun assignResponseToRecyclerview(apiResponse: ApiResponse?) {
rv_item.apply {
layoutManager = _layoutManager
apiResponseList.add(apiResponse!!)
itemAdapter = activity?.let { ItemAdapter(apiResponseList, it) }!!
adapter = itemAdapter
}
}
private fun addScrollerListener() {
rv_item.addOnScrollListener(object : RecyclerView.OnScrollListener() {
override fun onScrollStateChanged(rvItem: RecyclerView, newState: Int) {
super.onScrollStateChanged(rvItem, newState)
mainPresenter.processRvitemOnScroll(isLoading, rvItem, newState)
}
})
}
private fun loadMore() {
setRecyclerViewStateToLoading()
var numberOfDays = mainPresenter.getNumberOfMinusDays()
numberOfDays++
mainPresenter.saveNumberOfMinusDaysIntoSp(numberOfDays)
val dateMinusXDays = mainPresenter.currentDateMinusXDaysToStr(numberOfDays)
val nextLimit = listSize + 1
for (i in listSize until nextLimit) {
if (mainPresenter.checkIfSuchDateExistsinSp(dateMinusXDays)) {
Log.i(TAG, "Date $dateMinusXDays already exists in SharedPreferences (new element)")
handler.postDelayed({
mainPresenter.processDateWithoutMakingACall(dateMinusXDays)
}, 2000)
} else {
Log.i(TAG, "Date $dateMinusXDays does not exist in SharedPreferences. Retrofit call made (new element)")
mainPresenter.makeACall(dateMinusXDays)
}
}
itemAdapter.notifyDataSetChanged()
}
override fun notifyChangedItemAdapter() {
itemAdapter.notifyDataSetChanged()
}
override fun onDestroy() {
super.onDestroy()
restModel.cancelJob()
}
}
ItemAdapter.kt
class ItemAdapter(private var items: MutableList<ApiResponse>, private val activity: Activity) : RecyclerView.Adapter<RecyclerView.ViewHolder>() {
companion object {
private const val VIEW_TYPE_DATA = 0
private const val VIEW_TYPE_PROGRESS = 1
}
override fun onCreateViewHolder(parent: ViewGroup, p1: Int): RecyclerView.ViewHolder {
return when (p1) {
VIEW_TYPE_DATA -> {
val view = LayoutInflater.from(parent.context).inflate(R.layout.item, parent, false)
DataViewHolder(view, activity)
}
VIEW_TYPE_PROGRESS -> {
val view = LayoutInflater.from(parent.context).inflate(R.layout.progress_bar_layout, parent, false)
ProgressViewHolder(view)
}
else -> throw IllegalArgumentException("Different View type")
}
}
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
if (holder is DataViewHolder)
holder.bind(items[position])
}
override fun getItemCount() = items.size
override fun getItemViewType(position: Int): Int {
val viewtype = items[position]
return when (viewtype.isLoading) {//if data is load, returns PROGRESSBAR viewtype.
true -> VIEW_TYPE_PROGRESS
false -> VIEW_TYPE_DATA
}
}
class DataViewHolder(view: View, activity: Activity) : RecyclerView.ViewHolder(view) {
private var isRvSubitemVisible = false
private val tvDate = view.tv_date
private val rvSubitem = view.rv_subitem
private val activity = activity
fun bind(apiResponse: ApiResponse) {
tvDate.text = String.format(itemView.context.getString(R.string.day_x), apiResponse.date)
tvDate.setOnClickListener {
if (isRvSubitemVisible) {
rvSubitem.visibility = View.GONE
isRvSubitemVisible = false
} else {
rvSubitem.visibility = View.VISIBLE
isRvSubitemVisible = true
}
}
rvSubitem.apply {
layoutManager = LinearLayoutManager(itemView.context)
adapter = SubitemAdapter(apiResponse.rates, apiResponse.date, activity)
}
}
}
inner class ProgressViewHolder(view: View) : RecyclerView.ViewHolder(view)
}
SubitemAdapter.kt
class SubitemAdapter(private val subitems: List<Currency>, private val day: String, private val activity: Activity) : RecyclerView.Adapter<SubitemAdapter.SubitemViewHolder>() {
override fun onCreateViewHolder(parent: ViewGroup, p1: Int): SubitemViewHolder {
val view = LayoutInflater.from(parent.context).inflate(R.layout.subitem, parent, false)
return SubitemViewHolder(view, day, activity)
}
override fun onBindViewHolder(holder: SubitemViewHolder, position: Int) {
if(position < subitems.size - 1) {
activity.progress_bar.visibility = View.VISIBLE
}
else
activity.progress_bar.visibility = View.GONE
holder.bind(subitems[position], position)
}
override fun getItemCount() = subitems.size
class SubitemViewHolder(view: View, day: String, activity: Activity) : RecyclerView.ViewHolder(view) {
private val subitemRootView = view.subitem_root
private val tvCurrencyName = view.tv_currency_name
private val tvCurrencyValue = view.tv_currency_value
private val day = day
private val activity = activity
fun bind(currency: Currency, position: Int) {
subitemRootView.setOnClickListener { v ->
activity as OnChangeFragment
activity.changeFragment(SpecificCurrencyFragment(), ChangeFragmentData(hashMapOf(currency.currencyName to currency.currencyValue.toString()), day))
}
tvCurrencyName.text = currency.currencyName
tvCurrencyValue.text = currency.currencyValue.toString()
}
}
}
Here is I think everything to help me. But if you need something else more just aks.
Any help will bve really appreciated. Thank you in advance!
You are already passing a List<Currency> which are the items to be loaded inside your recyclerview. You should not be checking this inside onBindViewHolder since you already have those items when you passed them as an argument to the recyclerview adapter
Instead, when you are passing the List<Currency> to your adapter, you must update the progressbar at that time from activity itself. You can edit your question and add code for your Activity if this didn't help you, I'll try to answer you :)
I have an application with 2 fragments, both have lists that are being filled from the same adapter.
The first one works correctly, but the second one -
class CountryBordersFragment : Fragment(R.layout.fragment_country_borders) {
private lateinit var selectedCountry: String
private lateinit var countriesViewModel: CountriesListViewModel
private lateinit var countriesAdapter: CountriesListAdapter
private var countriesList = mutableListOf<CountryEntity>()
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
countriesViewModel = ViewModelProvider(this).get(CountriesListViewModel::class.java)
initData()
initClickListener()
}
private fun initClickListener() {
backButton.setOnClickListener {
requireActivity().onBackPressed()
}
}
private fun initData() {
countriesAdapter = CountriesListAdapter(null)
countriesAdapter.submitList(countriesList)
countriesRecyclerView.setHasFixedSize(true)
countriesRecyclerView.layoutManager = LinearLayoutManager(context)
countriesRecyclerView.adapter = countriesAdapter
arguments?.let {
selectedCountry = it.getString(getString(R.string.countries_list_fragment_selected_country))!!
}
countryName.text = selectedCountry
countriesViewModel.getCountryBorders(selectedCountry).observeOnce(requireActivity(), Observer { countryBorder ->
if (countryBorder.neighborCountries.isEmpty()) {
bordersWith.text = getString(R.string.country_border_fragment_country_does_not_have_borders)
return#Observer
}
countriesViewModel.getCountryByCioc(countryBorder.neighborCountries).observe(requireActivity(), Observer { countryEntityList ->
countriesAdapter.submitList(countryEntityList)
})
})
}
}
Does not fill the adapter at all. It just does not display any list whatsoever.
For sure something is missing because I am able to fill my adapter correctly at the first fragment but coming to this one the list does not pop up.
Here is my ListAdapter implementation -
class CountriesListAdapter(private val callback: CountryViewHolder.OnCountryClickListener?)
: ListAdapter<CountryEntity, CountryViewHolder>(CountriesDiffCallback()) {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): CountryViewHolder {
val view = LayoutInflater.from(App.context!!).inflate(R.layout.country_viewholder, parent, false)
return CountryViewHolder(view)
}
override fun onBindViewHolder(holder: CountryViewHolder, position: Int) {
holder.bind(getItem(position), callback)
}
class CountriesDiffCallback : DiffUtil.ItemCallback<CountryEntity>() {
override fun areItemsTheSame(oldItem: CountryEntity, newItem: CountryEntity): Boolean {
return oldItem.countryName == newItem.countryName
}
override fun areContentsTheSame(oldItem: CountryEntity, newItem: CountryEntity): Boolean {
return oldItem == newItem
}
}
}
and my ViewHolder and model -
class CountryViewHolder(view: View) : RecyclerView.ViewHolder(view) {
private val rootLayout: LinearLayout = view.country_viewholder_root_layout
private val nativeName: TextView = view.country_viewholder_native_name
private val countryName: TextView = view.country_viewholder_country_name
private val area: TextView = view.country_viewholder_area
private val countryImage: ImageView = view.country_viewholder_country_image
fun bind(model: CountryEntity, callback: OnCountryClickListener?) {
nativeName.text = App.context!!.getString(R.string.country_view_holder_native_name).plus(" ${model.countryName}")
countryName.text = App.context!!.getString(R.string.country_view_holder_country_name).plus(" ${model.nativeName}")
area.text = App.context!!.getString(R.string.country_view_holder_country_area).plus(" ${model.area}")
// Glide.with(App.context!!).load("https://www.talkwalker.com/images/2020/blog-headers/image-analysis.png").into(countryImage)
Picasso.get().load(model.imageUri).into(countryImage)
rootLayout.setOnClickListener {
callback?.onCountryClicked(model.countryName)
}
}
interface OnCountryClickListener {
fun onCountryClicked(countryName: String)
}
}
#Entity(tableName = countriesTable, primaryKeys = ["countryName"])
class CountryEntity(
val countryName: String,
val nativeName: String,
val area: Double,
val cioc: String? = null,
val imageUri : String? = null
) {
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (javaClass != other?.javaClass) return false
other as CountryEntity
if (countryName != other.countryName) return false
if (nativeName != other.nativeName) return false
if (area != other.area) return false
if (cioc != other.cioc) return false
if (imageUri != other.imageUri) return false
return true
}
override fun hashCode(): Int {
var result = countryName.hashCode()
result = 31 * result + nativeName.hashCode()
result = 31 * result + area.hashCode()
result = 31 * result + (cioc?.hashCode() ?: 0)
result = 31 * result + (imageUri?.hashCode() ?: 0)
return result
}
}
class CountryViewHolder(view: View) : RecyclerView.ViewHolder(view) {
private val rootLayout: LinearLayout = view.country_viewholder_root_layout
private val nativeName: TextView = view.country_viewholder_native_name
private val countryName: TextView = view.country_viewholder_country_name
private val area: TextView = view.country_viewholder_area
private val countryImage: ImageView = view.country_viewholder_country_image
fun bind(model: CountryEntity, callback: OnCountryClickListener?) {
nativeName.text = App.context!!.getString(R.string.country_view_holder_native_name).plus(" ${model.countryName}")
countryName.text = App.context!!.getString(R.string.country_view_holder_country_name).plus(" ${model.nativeName}")
area.text = App.context!!.getString(R.string.country_view_holder_country_area).plus(" ${model.area}")
// Glide.with(App.context!!).load("https://www.talkwalker.com/images/2020/blog-headers/image-analysis.png").into(countryImage)
Picasso.get().load(model.imageUri).into(countryImage)
rootLayout.setOnClickListener {
callback?.onCountryClicked(model.countryName)
}
}
interface OnCountryClickListener {
fun onCountryClicked(countryName: String)
}
}
What is it that I am missing? Just started working with ListAdapter from normal RecyclerView.Adapter.
Looks like you are missing - notifyDataSetChanged()
Just After -
countriesAdapter.submitList(countryEntityList)
Add -
countriesAdapter.notifyDataSetChanged()
As below Gif.
Why images in Works tag of BottomNavigationView's Profile will disappear after switch between BottomNavigationView. But those images will appear after I enter to Home's category item of BottomNavigationView ??
MainActivity.kt
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
navController = nav_host_fragment.findNavController()
bottom_nav_view.setupWithNavController(navController)
}
WorksFragment.kt
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
uid = FirebaseAuth.getInstance().currentUser?.uid
val worksFragmentViewModel= ViewModelProvider(this).get(WorksFragmentViewModel::class.java)
worksFragmentViewModel.getSelfThumbnailPhoto().observe(viewLifecycleOwner, Observer {
works_fragment_recycyler_view.apply {
setHasFixedSize(true)
layoutManager = GridLayoutManager(context, 3)
adapter = ThumbnailWorksAdapter(it).apply {
notifyDataSetChanged()
}
}
})
}
WorksFragmentViewModel.kt
class WorksFragmentViewModel : ViewModel() {
private var worksFragmentReposity = WorksFragmentReposity()
fun getSelfThumbnailPhoto() = worksFragmentReposity.getSelfThumbnailPhoto()
}
WorksFragmentReposity.kt
class WorksFragmentReposity {
private var selfThumbnailLiveData = SelfThumbnailLiveData()
fun getSelfThumbnailPhoto():LiveData<List<UploadedImages>> = selfThumbnailLiveData
}
SelfThumbnailLiveData.kt
class SelfThumbnailLiveData : LiveData<List<UploadedImages>>(), EventListener<QuerySnapshot> {
private val TAG= SelfThumbnailLiveData::class.java.simpleName
private lateinit var listenerRegistration: ListenerRegistration
private var isRegistered :Boolean = false
private val uid = FirebaseAuth.getInstance().currentUser?.uid
private val query = Firebase.firestore.collection("uploadedImages")
.whereEqualTo("uid", uid)
.orderBy("timestamp", Query.Direction.DESCENDING)
override fun onActive() {
super.onActive()
listenerRegistration = query.addSnapshotListener(this)
isRegistered = true
}
override fun onInactive() {
super.onInactive()
if (isRegistered) {
listenerRegistration.remove()
}
}
override fun onEvent(querySnapshot: QuerySnapshot?, firebaseFirestoreException: FirebaseFirestoreException?) {
Log.d(TAG, ": ${firebaseFirestoreException?.message}");
querySnapshot?.let {
val uploadedImagesList = mutableListOf<UploadedImages>()
it.documents.forEach { documentSnapshot ->
val uploadedImages = documentSnapshot.toObject(UploadedImages::class.java)
?: UploadedImages()
uploadedImagesList.add(uploadedImages)
}
Log.d(TAG, "uploadedImagesList: $uploadedImagesList");
value = uploadedImagesList
}
}
}
TabFragmentPagerAdapter.kt
class TabFragmentPagerAdapter(context: Context,fm: FragmentManager) : FragmentPagerAdapter(fm, BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT) {
private val TAB_TITLES = arrayOf("Works","User Info")
private val fragments:List<Fragment> = listOf(
WorksFragment(),
UserInfoFragment()
)
override fun getItem(position: Int): Fragment {
return fragments[position]
}
override fun getCount(): Int {
return fragments.count()
}
override fun getPageTitle(position: Int): CharSequence? {
return TAB_TITLES[position]
}
}
ThumbnailWorksAdapter.kt
class ThumbnailWorksAdapter(private val uploadImagesList: List<UploadedImages>) : RecyclerView.Adapter<ThumbnailWorksAdapter.ThumbnailWorksHolder>() {
inner class ThumbnailWorksHolder(itemView: View) : RecyclerView.ViewHolder(itemView)
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ThumbnailWorksHolder {
val view = LayoutInflater.from(parent.context)
.inflate(R.layout.thumbnail_works_holder_images, parent, false)
return ThumbnailWorksHolder(view)
}
override fun getItemCount(): Int {
return uploadImagesList.size
}
override fun onBindViewHolder(holder: ThumbnailWorksHolder, position: Int) {
val uploadedImages = uploadImagesList[position]
holder.itemView.apply {
Picasso.get()
.load(uploadedImages.downloadImagesUriList[0])
.centerCrop(Gravity.CENTER_HORIZONTAL)
.resize(100,100)
.into(thumbnail_works_images)
}
}
}
ProfileFragment.kt
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
//Bind tabLayout with viewPager
val tabFragmentPagerAdapter = TabFragmentPagerAdapter(requireContext(), parentFragmentManager).apply {
notifyDataSetChanged()
}
profileViewPager.adapter = tabFragmentPagerAdapter
tabLayout.setupWithViewPager(profileViewPager)
}
Someone had helped me solve the problem.
ProfileFragment.kt
val tabFragmentPagerAdapter = TabFragmentPagerAdapter(requireContext(), childFragmentManager).apply {
notifyDataSetChanged()
}
The data in the RecyclerView is called the first time without issues. However when i refresh the data, for some reason all the items goes blank.
The MainActivity is this
class BusinessActivity : AppCompatActivity() {
private val businessViewModel: BusinessViewModel by viewModel()
private val imageLoader: ImageLoader by inject()
private lateinit var staggeredGridLayoutManager: StaggeredGridLayoutManager
private lateinit var skeleton: Skeleton
private val adapter: BusinessAdapter by lazy { BusinessAdapter(imageLoader, businessViewModel) }
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_business)
initToolbar()
skeleton = findViewById<SkeletonLayout>(R.id.skeletonLayout)
staggeredGridLayoutManager = StaggeredGridLayoutManager(2, StaggeredGridLayoutManager.VERTICAL)
staggeredGridLayoutManager.gapStrategy = StaggeredGridLayoutManager.GAP_HANDLING_MOVE_ITEMS_BETWEEN_SPANS
recycler_view.apply {
layoutManager = staggeredGridLayoutManager
adapter = this#BusinessActivity.adapter
setHasFixedSize(true)
}
setupSkeleton()
initializeObserverBusiness()
refreshBusiness.setOnRefreshListener {
refreshBusiness.isRefreshing = true
skeleton.showSkeleton()
businessViewModel.retrieveBusiness()
}
}
private fun initToolbar() {
setSupportActionBar(toolbar)
supportActionBar?.title = getString(R.string.app_name)
this.setSystemBarColor(this)
}
private fun setupSkeleton(){
skeleton = recycler_view.applySkeleton(R.layout.business_card, 6)
skeleton.showSkeleton()
}
private fun initializeObserverBusiness(){
businessViewModel.uiState.observe(this, Observer {
val dataState = it ?: return#Observer
if (!dataState.showProgress){
refreshBusiness.isRefreshing = false
skeleton.showOriginal()
}
if (dataState.business != null && !dataState.business.consumed){
dataState.business.consume()?.let { business ->
adapter.submitList(business)
}
}
if (dataState.error != null && !dataState.error.consumed){
dataState.error.consume()?.let { error ->
Toast.makeText(this, resources.getString(error), Toast.LENGTH_LONG).show()
}
}
})
}
}
and the Adapter for the RecyclerView, im currently using DiffCallback and ListAdapter due to a better performance.
class BusinessAdapter(var imageLoader: ImageLoader, var viewModel: BusinessViewModel) : ListAdapter<Business, BusinessViewHolder>(DIFF_CALLBACK){
companion object{
private val DIFF_CALLBACK = object : DiffUtil.ItemCallback<Business>() {
override fun areItemsTheSame(oldItem: Business, newItem: Business) = oldItem.id == newItem.id
override fun areContentsTheSame(oldItem: Business, newItem: Business) = oldItem == newItem
}
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) = BusinessViewHolder.create(parent)
override fun onBindViewHolder(holder: BusinessViewHolder, position: Int) {
holder.bind(getItem(position), imageLoader, viewModel)
}
}
and the ViewHolder for the Adapter
class BusinessViewHolder constructor(override val containerView: View) : RecyclerView.ViewHolder(containerView), LayoutContainer {
fun bind(business: Business, imageLoader: ImageLoader, viewModel: BusinessViewModel) {
businessImage?.let { imageLoader.load("${BuildConfig.MY_URL}/gallery/${business.images[0]}", it) }
ownerBusiness.text = business.owner
businessName.text = business.name
cardBusiness.setOnClickListener {
viewModel.callDetailBusiness(business.id)
}
}
companion object {
fun create(parent: ViewGroup): BusinessViewHolder {
return BusinessViewHolder(LayoutInflater.from(parent.context).inflate(R.layout.business_card, parent, false))
}
}
}
and the ViewModel
class BusinessViewModel (private val businessRepository: BusinessRepository): ViewModel() {
private val _uiState = MutableLiveData<BusinessDataState>()
val uiState: LiveData<BusinessDataState> get() = _uiState
val _showDetailBusiness = MutableLiveData<Int?>()
val showDetailBusiness: LiveData<Int?> get() = _showDetailBusiness
init {
retrieveBusiness()
}
fun retrieveBusiness(){
viewModelScope.launch {
runCatching {
emitUiState(showProgress = true)
businessRepository.retrieveBusiness()
}.onSuccess {
emitUiState(business = Event(it))
}.onFailure {
emitUiState(error = Event(R.string.internet_failure_error))
}
}
}
fun callDetailBusiness(businessId: Int) {
_showDetailBusiness.value = businessId
}
private fun emitUiState(showProgress: Boolean = false, business: Event<List<Business>>? = null, error: Event<Int>? = null){
val dataState = BusinessDataState(showProgress, business, error)
_uiState.value = dataState
}
data class BusinessDataState(val showProgress: Boolean, val business: Event<List<Business>>?, val error: Event<Int>?)
}
When the data is loaded for the first time i see this.
however when i apply the SwipeRefresh. I receive the data.
D/OkHttp: [{"id":18,"name":"Whatsup","owner":"Mi
Soledad","category":"ToDo",
but the RecyclerView won't attach the new information...