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()
}
Related
I am creating a news app, I have a recycler view with buttons that have the name of category of news. I want to save the state of recycler view after I destroyed the app. Example if I click in Sport Categories, after I destroy the app and reopen it, the app should have sport category selected. The code its not having a bug but it doesn't save the state. The code it's written in fragment
Some of my code
var isFilterFromCategories = false
class HomeFragment : Fragment(), Callback, HomePageAdapter.Listener,CategoryListener {
private lateinit var binding : FragmentHomeBinding
private lateinit var adapter: HomePageAdapter
private lateinit var menuAdapter: MenuAdapter
private lateinit var currentCategory : String
private val viewModel by activityViewModels<ArticleViewModel>()
private var lastPosition : Int = 0
private var firstTime = true
private fun showPopUp(view: View) {
val popup = PopupMenu(context, view, Gravity.RIGHT)
popup.inflate(R.menu.content_of_dropdown_menu)
popup.setOnMenuItemClickListener(PopupMenu.OnMenuItemClickListener { item: MenuItem? ->
when (item!!.itemId) {
R.id.dm_raportimi -> {
reportArticle()
}
}
true
})
popup.show()
}
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
// Inflate the layout for this fragment
binding = DataBindingUtil.inflate(inflater,R.layout.fragment_home, container, false)
val window = requireActivity().window
window.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
window.statusBarColor = Color.BLACK
return binding.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
val snapHelper = PagerSnapHelper()
snapHelper.attachToRecyclerView(binding.recyclerView)
setDrawerOpeningFunctionality()
initRecyclerView()
initMenuRecyclerView()
viewModel.initializeList()
val pref = requireActivity()!!.getPreferences(Context.MODE_PRIVATE)
lastPosition = pref.getInt("lastpos",0)
topMenuRecyclerView.scrollToPosition(lastPosition)
topMenuRecyclerView.addOnScrollListener(object : RecyclerView.OnScrollListener() {
override fun onScrollStateChanged(recyclerView: RecyclerView, newState: Int) {
lastPosition =
(topMenuRecyclerView.layoutManager as LinearLayoutManager?)!!.findFirstVisibleItemPosition()
}
})
viewModel.getArrayList().observe(requireActivity()){
adapter.setArticles(it)
}
}
override fun onDestroy() {
super.onDestroy()
//save position in Shared Preferences
val pref = requireActivity()!!.getPreferences(Context.MODE_PRIVATE)
val e = pref.edit()
e.putInt("lastpos",lastPosition)
Log.d("lololo",lastPosition.toString())
e.apply()
}
override fun onResume() {
super.onResume()
if (isFilterFromCategories) {
isFilterFromCategories = false
setUpComingFromCategorySelection()
}
}
private fun initRecyclerView(){
adapter = HomePageAdapter(requireContext(), this)
binding.recyclerView.adapter = adapter
}
private fun initMenuRecyclerView(){
menuAdapter = MenuAdapter(requireContext(),this)
binding.topMenuRecyclerView.apply {
adapter = menuAdapter
}
}
private fun openWebLink(url: String){
val webIntent: Intent = Intent(Intent.ACTION_VIEW, Uri.parse(url))
startActivity(webIntent)
}
private fun shareLink(title: String, url: String){
val sharingIntent = Intent.createChooser(Intent().apply {
action = Intent.ACTION_SEND
putExtra(Intent.EXTRA_TEXT, "https://developer.android.com/training/sharing/")
putExtra(Intent.EXTRA_SUBJECT,url)
// (Optional) Here we're setting the title of the content
putExtra(Intent.EXTRA_TITLE, title)
// (Optional) Here we're passing a content URI to an image to be displayed
type = "rext/plain"
flags = Intent.FLAG_GRANT_READ_URI_PERMISSION
}, null)
startActivity(sharingIntent)
}
override fun sourceOfInformationClicked(link: String) {
openWebLink(link)
}
override fun shareLinkInformationClicked(name:String, link:String){
shareLink(name,link)
}
override fun showMoreOnClicked(view: View){
showPopUp(view)
}
private fun reportArticle() {
val action = HomeFragmentDirections.actionHomeFragmentToReportFragment()
findNavController().navigate(action)
}
private fun setDrawerOpeningFunctionality() {
val drawerLayout = requireActivity().findViewById<DrawerLayout>(R.id.drawer_layout)
drawerLayout.setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_CLOSED);
binding.btnNavDraw.setOnClickListener {
with(drawerLayout){
if (!isDrawerOpen(GravityCompat.END)){
openDrawer(GravityCompat.END)
} else {
closeDrawer(GravityCompat.END)
}
}
}
}
override fun changeCategory(category: String){
viewModel.setNews(category)
binding.topMenuRecyclerView.adapter?.notifyDataSetChanged()
}
override fun selectCategoryListener(category: String, pos: Int) {
TODO("Not yet implemented")
}
private fun setUpComingFromCategorySelection(){
val args: HomeFragmentArgs by navArgs()
currentCategory = args.category
menuAdapter.setCategory(currentCategory)
menuAdapter.selected.add(args.position)
binding.topMenuRecyclerView.scrollToPosition(args.position)
viewModel.setNews(currentCategory)
}
}
You should save position on item click.Dont save position on onDestroy
private const val TAG = "DeviceAdapter"
class DeviceAdapter(
private val context: Context
, private val list: MutableList<UsbDevice>
, private val callback: (device: UsbDevice) -> Unit): RecyclerView.Adapter<RecyclerView.ViewHolder>() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
val inflater = LayoutInflater.from(context)
val binding = ItemDeviceBinding.inflate(inflater,parent,false)
return MyViewHolder(binding)
}
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
val device = list[position]
if (holder is MyViewHolder){
holder.binding.textHere.text = device.deviceName
holder.binding.textHere.setOnClickListener {
val pref = context.getSharedPreferences("SHARED_PREFERENCES_KEY",
Context.MODE_PRIVATE)
val editor = pref.edit()
editor.putInt("lastPos",position)
editor.apply()
}
}
}
override fun getItemCount(): Int {
return list.size
}
class MyViewHolder(val binding: ItemDeviceBinding): RecyclerView.ViewHolder(binding.root)
}
I have a RecyclerView and my Fragment. I am getting data from Firebase Realtime Database into a RecyclerView.
I need to make it so that while the data is loading, I see some kind of loading effect. How can i do this?
Code from my Fragment:
private var _binding: FragmentDayDetailBinding? = null
private val binding get() = _binding!!
private var ref: DatabaseReference? = null
private lateinit var adapter: DayDetailAdapter
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View {
_binding = FragmentDayDetailBinding.inflate(inflater, container, false)
setupRecyclerView()
initDatabase()
return binding.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
readFromDatabase()
}
private fun setupRecyclerView() {
adapter = DayDetailAdapter()
binding.recyclerView.layoutManager = LinearLayoutManager(requireContext())
binding.recyclerView.adapter = adapter
}
private fun initDatabase() {
FirebaseApp.initializeApp(requireContext())
ref = FirebaseDatabase.getInstance()
.getReference("IMIT")
.child("groups")
}
private fun readFromDatabase() {
ref?.addValueEventListener(object: ValueEventListener {
override fun onDataChange(snapshot: DataSnapshot) {
if (snapshot.exists()) {
val list = ArrayList<Day>()
for (daySnapshot in snapshot.children) {
val day = daySnapshot.getValue(Day::class.java)
list.add(day!!)
}
adapter.submitList(list)
} else {
binding.apply {
lrDbEmpty.visibility = View.VISIBLE
recyclerView.visibility = View.INVISIBLE
}
}
}
override fun onCancelled(error: DatabaseError) {
}
})
}
override fun onDestroyView() {
super.onDestroyView()
_binding = null
}
Code from RecyclerView Adapter:
class ViewHolder(private val binding: ItemSubjectDetailBinding): RecyclerView.ViewHolder(binding.root) {
fun bind(day: Day) = with(binding) {
tvSubject.text = day.subject
tvInfo.text = "${day.teacher}, ${day.type}"
tvTime.text = day.time
tvAud.text = day.classroom
}
companion object {
fun from(parent: ViewGroup): ViewHolder {
val layoutInflater = LayoutInflater.from(parent.context)
val binding = ItemSubjectDetailBinding.inflate(layoutInflater, parent, false)
return ViewHolder(binding)
}
}
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
return ViewHolder.from(parent)
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
with(holder) {
bind(getItem(position))
}
}
class ItemComparator: DiffUtil.ItemCallback<Day>() {
override fun areItemsTheSame(oldItem: Day, newItem: Day): Boolean {
return oldItem == newItem
}
override fun areContentsTheSame(oldItem: Day, newItem: Day): Boolean {
return oldItem == newItem
}
}
You already in half the way
Create a progressBar in the center of the fragment
<ProgressBar
android:id="#+id/progress_bar"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
/>
Keep it visible until the data fetched (onDataChange Called) then hide the progressBar
override fun onDataChange(snapshot: DataSnapshot) {
binding.progressBar.visibility = View.GONE
if (snapshot.exists()) {
// rest of your code
}
}
I prefer a dynamic add view.
I use the lottie player for the show wait animation. There are two approaches for adding the show wait. In this way, you can add animation or any type of view from the non-activity class. It helps you to better implement in the MVVM form.
This is both methods implementation.
1- Create and remove by Id
2- Create and remove by LiveData
class AddViewNonActivity(
private val viewGroup: ViewGroup
) {
fun addCustomWait(): Int {
val relativeLayout =
RelativeLayout(viewGroup.context)
val relativeParams =
RelativeLayout.LayoutParams(MATCH_PARENT, MATCH_PARENT)
val lottieParams =
RelativeLayout.LayoutParams(600, 600)
relativeLayout.setBackgroundColor(
viewGroup.context.getColor(R.color.wait_transparent)
)
relativeLayout.gravity = CENTER
val lottieLoading = LottieAnimationView(viewGroup.context)
lottieLoading.setAnimation("lottie/space-runner.json")
lottieLoading.repeatCount = INFINITE
lottieLoading.speed = 1f
lottieLoading.playAnimation()
relativeLayout.addView(lottieLoading, lottieParams)
val viewId = View.generateViewId()
viewGroup.addView(relativeLayout, relativeParams)
relativeLayout.id = viewId
relativeLayout.isClickable = true
return viewId
}
fun removeCustomWait(
waitViewId: Int
) {
for (view in viewGroup) {
if (view.id == waitViewId)
viewGroup.removeView(view)
}
}
fun addLiveCustomWait(
lifecycleOwner: LifecycleOwner,
liveData: LiveData<Boolean>) {
var viewId = 0
liveData.observe(lifecycleOwner) {
if (it) {
removeCustomWait(viewId)
viewId = addCustomWait()
} else {
removeCustomWait(viewId)
}
}
}
}
Activity call method
class DynamicViewActivity : AppCompatActivity() {
private lateinit var binding : ActivityDynamicViewBinding
private val liveShowWait =MutableLiveData(false)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityDynamicViewBinding.inflate(layoutInflater)
setContentView(binding.root)
var waitId = 0
initialWait()
binding.btnAddView.setOnClickListener {
waitId = AddViewNonActivity(binding.root).addCustomWait()
}
binding.btnRemoveView.setOnClickListener {
AddViewNonActivity(binding.root).removeCustomWait(waitId)
}
binding.btnLiveAddView.setOnClickListener {
liveShowWait.postValue(true)
}
binding.btnLiveRemoveView.setOnClickListener {
liveShowWait.postValue(false)
}
}
private fun initialWait() =
AddViewNonActivity(binding.root).addLiveCustomWait(this as
LifecycleOwner, liveShowWait)
}
Github link
In my scenario, I have two fragments. In my first Fragment I have a RecyclerView with SelectionTracker. After selecting an item in my first fragment I can navigate to the second fragment. But from my second fragment if I navigate back to the first fragment using back button all my previous selection is lost. How can I prevent this?
I am using Jetpack Navigation Component to handle Fragment navigation.
Here is my fragment
class SelectVideoForCourseFragment : Fragment(R.layout.fragment_select_video_for_course) {
.....
..
private val adapter by lazy { MediaListAdapter() }
private lateinit var selectionTracker: SelectionTracker<String>
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
initializeListeners()
initializeRecyclerView(savedInstanceState)
fetchMedias()
}
override fun onSaveInstanceState(outState: Bundle) {
super.onSaveInstanceState(outState)
selectionTracker.onSaveInstanceState(outState)
}
private fun fetchMedias() {
val mediaRepo = MediaRepo()
mediaRepo.getVideos(contentResolver).observe(viewLifecycleOwner) {
adapter.submitItems(it)
}
}
private fun initializeRecyclerView(savedInstanceState: Bundle?) {
binding.mediaList.layoutManager = GridLayoutManager(requireContext(), 3)
binding.mediaList.addItemDecoration(GridSpacingItemDecoration(3, 5.dp, false))
binding.mediaList.adapter = adapter
selectionTracker = SelectionTracker.Builder(
"tracker-select-video-for-course",
binding.mediaList,
MediaItemKeyProvider(adapter),
MediaItemDetailsLookup(binding.mediaList),
StorageStrategy.createStringStorage()
).withSelectionPredicate(
SelectionPredicates.createSelectSingleAnything()
).build()
if (savedInstanceState != null) {
selectionTracker.onRestoreInstanceState(savedInstanceState)
}
adapter.tracker = selectionTracker
}
......
...
}
My RecyclerView Adapter:
class MediaListAdapter: RecyclerView.Adapter<RecyclerView.ViewHolder>() {
val differ = AsyncListDiffer(this, DIFF_CALLBACK_MEDIA)
var tracker: SelectionTracker<String>? = null
fun submitItems(updatedItems: List<Any>) {
differ.submitList(updatedItems)
}
fun getSelections(): List<MediaEntity> {
....
..
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
return if (viewType == 1) {
val binding = ListItemImageBinding.inflate(
LayoutInflater.from(parent.context),
parent,
false
)
ViewHolderImage(binding)
} else {
val binding = ListItemVideoBinding.inflate(
LayoutInflater.from(parent.context),
parent,
false
)
ViewHolderVideo(binding)
}
}
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
if (holder is ViewHolderImage) {
tracker?.let {
val item = differ.currentList[position] as ImageEntity
holder.bind(item , it.isSelected(item.uri.toString()))
}
} else {
holder as ViewHolderVideo
val item = differ.currentList[position] as VideoEntity
tracker?.let {
holder.bind(item, it.isSelected(item.uri.toString()))
}
}
}
override fun getItemCount(): Int = differ.currentList.size
override fun getItemViewType(position: Int): Int {
return if (differ.currentList[position] is ImageEntity) {
1
} else {
2
}
}
inner class ViewHolderImage(
private val binding: ListItemImageBinding): RecyclerView.ViewHolder(binding.root
) {
fun bind(item: ImageEntity, isSelected: Boolean) {
.....
...
}
fun getItemDetails(): ItemDetailsLookup.ItemDetails<String> =
object : ItemDetailsLookup.ItemDetails<String>() {
override fun getPosition(): Int = absoluteAdapterPosition
override fun getSelectionKey(): String {
val item = differ.currentList[absoluteAdapterPosition] as ImageEntity
return item.uri.toString()
}
override fun inSelectionHotspot(e: MotionEvent): Boolean = true
}
}
inner class ViewHolderVideo(
private val binding: ListItemVideoBinding,
): RecyclerView.ViewHolder(binding.root) {
fun bind(item: VideoEntity, isSelected: Boolean) {
.....
...
}
fun getItemDetails(): ItemDetailsLookup.ItemDetails<String> =
object : ItemDetailsLookup.ItemDetails<String>() {
override fun getPosition(): Int = absoluteAdapterPosition
override fun getSelectionKey(): String {
val item = differ.currentList[absoluteAdapterPosition] as VideoEntity
return item.uri.toString()
}
override fun inSelectionHotspot(e: MotionEvent): Boolean = true
}
}
companion object {
val DIFF_CALLBACK_MEDIA = object: DiffUtil.ItemCallback<Any>() {
.....
...
}
}
}
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 :)
My problem is that when I write MyListAdapter.currentList.isEmpty() the value is true even tho the list is NOT EMPTY! (I can see it in my view and in my database). What am I doing wrong, why is the value true even tho it should be false?
Fragment
#AndroidEntryPoint
class ShoppingCartFragment(
private val cacheMapper: ShopCacheMapper
) : Fragment(R.layout.fragment_shopping_cart), ShoppingCartListAdapter.OnItemClickListener {
private val shoppingCartViewModel: ShoppingCartViewModel by viewModels()
private val shoppingCartBinding: FragmentShoppingCartBinding by viewBinding()
#Inject lateinit var shoppingCartAdapter: ShoppingCartListAdapter
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
bindObjects()
observeList()
hideBtnIfListEmpty()
toast("LISTE IST EMPTY: ${shoppingCartAdapter.currentList.isEmpty()}")
}
private fun observeList() {
shoppingCartViewModel.productList.observe(viewLifecycleOwner) {
shoppingCartAdapter.submitList(it)
}
}
private fun bindObjects() = with(shoppingCartBinding) {
adapter = shoppingCartAdapter.apply { clickHandler(this#ShoppingCartFragment) }
viewModel = shoppingCartViewModel
lifecycleOwner = viewLifecycleOwner
}
override fun forwardCardClick(productCacheEntity: ProductCacheEntity) {
val product = cacheMapper.mapFromEntity(productCacheEntity)
val action = ShoppingCartFragmentDirections.actionShoppingCartFragmentToShopItemFragment(product)
findNavController().navigate(action)
}
override fun fordwardBtnIncreaseClick(id: Int) {
shoppingCartViewModel.increaseProductQuantityById(id)
}
override fun fordwardBtnDecreaseClick(id: Int) {
shoppingCartViewModel.decreaseProductQuantityById(id)
}
override fun forwardBtnDeleteClick(id: Int) {
shoppingCartViewModel.deleteProductById(id)
}
override fun onDestroyView() {
requireView().findViewById<RecyclerView>(R.id.rv_shopping_cart).adapter = null
super.onDestroyView()
}
// here lies the problem
private fun hideBtnIfListEmpty() {
if (shoppingCartAdapter.currentList.isEmpty()) shoppingCartBinding.shoppingCartBtn.root.visibility = View.INVISIBLE
else if (shoppingCartAdapter.currentList.isNotEmpty()) shoppingCartBinding.shoppingCartBtn.root.visibility = View.VISIBLE
}
}
ListAdapter
#FragmentScoped
class ShoppingCartListAdapter #Inject constructor() : ListAdapter<ProductCacheEntity, ShoppingCartListAdapter.ProductViewHolder>(Companion) {
private lateinit var clickListener: OnItemClickListener
companion object: DiffUtil.ItemCallback<ProductCacheEntity>() {
override fun areItemsTheSame(oldItem: ProductCacheEntity, newItem: ProductCacheEntity): Boolean = oldItem.id == newItem.id
override fun areContentsTheSame(oldItem: ProductCacheEntity, newItem: ProductCacheEntity): Boolean = oldItem == newItem
}
inner class ProductViewHolder(val binding: ShoppingCartListItemBinding): RecyclerView.ViewHolder(binding.root)
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ProductViewHolder {
val layoutInflater = LayoutInflater.from(parent.context)
val binding = ShoppingCartListItemBinding.inflate(layoutInflater, parent, false)
return ProductViewHolder(binding).also {
with(binding) {
ivProduct.setOnClickListener { clickListener.forwardCardClick(binding.product!!) }
tvTitle.setOnClickListener { clickListener.forwardCardClick(binding.product!!) }
btnIncrease.setOnClickListener { clickListener.fordwardBtnIncreaseClick(binding.product!!.id) }
btnDecrease.setOnClickListener { clickListener.fordwardBtnDecreaseClick(binding.product!!.id) }
btnDelete.setOnClickListener { clickListener.forwardBtnDeleteClick(binding.product!!.id) }
}
}
}
override fun onBindViewHolder(holder: ProductViewHolder, position: Int) {
val currentProduct = getItem(position) ?: return
holder.binding.product = currentProduct
holder.binding.btnDecrease.isEnabled = currentProduct.quantity > 1
holder.binding.executePendingBindings()
}
interface OnItemClickListener {
fun forwardCardClick(productCacheEntity: ProductCacheEntity)
fun fordwardBtnIncreaseClick(id: Int)
fun fordwardBtnDecreaseClick(id: Int)
fun forwardBtnDeleteClick(id: Int)
}
fun clickHandler(clickEventHandler: OnItemClickListener) {
this.clickListener = clickEventHandler
}
}
Layout
<androidx.recyclerview.widget.RecyclerView
android:id="#+id/rv_shopping_cart"
android:layout_width="match_parent"
android:layout_height="0dp"
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
app:layout_constraintBottom_toTopOf="#id/shopping_cart_btn"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#id/headline"
app:recyclerview_adapter_stableID="#{adapter}"
tools:listitem="#layout/shopping_cart_list_item" />
Bindingadapter (app:recyclerview_adapter_stableID)
#BindingAdapter("recyclerview_adapter_stableID")
fun setRecyclerViewAdapterWithStableId(view: RecyclerView, adapter: RecyclerView.Adapter<*>) {
view.run {
this.setHasFixedSize(true)
this.adapter = adapter
}
}
You are calling ListAdapter.Currentlist.isEmpty() within hideBtnIfListEmpty() too early before making sure that the list is ready and submitted by observeList()
You can call it after you submit the list to the adapter:
private fun observeList() {
shoppingCartViewModel.productList.observe(viewLifecycleOwner) {
shoppingCartAdapter.submitList(it)
hideBtnIfListEmpty()
}
}