Fragment is not associated with a fragment manager - android

This question has been asked multiple times, but the proposed solutions do not function in my specific case.
When the onProjectImageViewClick function is executed I receive the following error:
java.lang.IllegalStateException: Fragment HomeFragment{191080d} (9545b64c-bf0a-4464-8a71-b8c474526181)} not associated with a fragment manager.
One of the proposed solutions was to use the code below to navigate, but since my fragment is detached I can't navigate to the other fragment.
view?.post {
findNavController().navigate(R.id.fragment_project_info)
}
Also, when I troubleshoot using println(Homefragment().isAdded), it returns false, therefore I can't use the solution written above.
Before implementing the firebase database (only using placeholders for my recyclerview), I could navigate to my fragment_project_info.
My Fragment:
class HomeFragment : Fragment(), HomeProjectListAdapter.OnItemClickListener {
private lateinit var auth: FirebaseAuth
private lateinit var databaseReference: DatabaseReference
lateinit var listener: ValueEventListener
var recentPostRef = FirebaseDatabase.getInstance().getReference("projects").limitToLast(50)
val TAG = "loadPostData Result"
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
return inflater.inflate(R.layout.fragment_home, container, false)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
auth = Firebase.auth
}
override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
postQueryValueListener()
}
override fun onStart() {
super.onStart()
val currentUser = auth.currentUser
updateUI(currentUser)
}
override fun onProjectImageViewClick(position: Int, projectData: ProjectData) {
findNavController().navigate(R.id.fragment_project_info)
}
private fun updateUI(currentUser: FirebaseUser?){
if(currentUser == null){
upload_button.visibility = View.GONE
projects_button.visibility = View.GONE
}
}
private fun postQueryValueListener() {
listener = recentPostRef.addValueEventListener(object : ValueEventListener {
override fun onDataChange(dataSnapshot: DataSnapshot) {
for (postSnapshot in dataSnapshot.children) {
var list = ArrayList<ProjectData>()
for (dataSnapshot1 in dataSnapshot.children) {
val p: ProjectData? = dataSnapshot1.getValue(ProjectData::class.java)
if (p != null) {
list.add(p)
}
}
var adapter = HomeProjectListAdapter(list, HomeFragment())
recycler_view.adapter = adapter
recycler_view.layoutManager = LinearLayoutManager(context)
recycler_view.hasFixedSize()
}
}
override fun onCancelled(databaseError: DatabaseError) {
Log.w(TAG, "loadPost:onCancelled", databaseError.toException())
}
})
}
override fun onStop() {
super.onStop()
recentPostRef.removeEventListener(listener)
}
}
Here is my adapter:
class HomeProjectListAdapter(
private val projectList: List<ProjectData>,
private val listener: HomeFragment
) : RecyclerView.Adapter<HomeProjectListAdapter.ProjectListViewHolder>() {
val TAG = "loadUserData Result"
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ProjectListViewHolder {
val itemView = LayoutInflater.from(parent.context).inflate(R.layout.layout_project_card,
parent, false)
return ProjectListViewHolder(itemView)
}
override fun onBindViewHolder(holder: ProjectListViewHolder, position: Int) {
val currentItem = projectList[position]
Glide.with(holder.projectImageView.context)
.load(currentItem.mainImageURL)
.into(holder.projectImageView)
}
override fun getItemCount() = projectList.size
inner class ProjectListViewHolder(itemView: View): RecyclerView.ViewHolder(itemView),
View.OnClickListener{
var projectImageView: ImageView = itemView.project_card_image
init {
projectImageView.setOnClickListener(this)
}
override fun onClick(v: View?) {
val position = adapterPosition
val projectData: ProjectData = projectList[position]
if (position != RecyclerView.NO_POSITION){
if (v != null) {
if(v.id == R.id.project_card_image) {
listener.onProjectImageViewClick(position, projectData)
}
}
}
}
}
interface OnItemClickListener {
fun onProjectImageViewClick(position: Int, projectData: ProjectData)
}
}

When you run HomeProjectListAdapter(list, HomeFragment()), you're passing in a brand new instance of HomeFragment. That brand new instance is never attached to a FragmentManager.
Instead, you need to pass in your current fragment instance:
var adapter = HomeProjectListAdapter(list, this#HomeFragment)

Related

How to display items loading while data is being fetched from Firebase android kotlin

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

How do I get an object when I click on the holder? kotlin

I have a RecycleView in which I display orders (This is my class).
To get an order basket, I need to know the order ID.
I tried to take the ID from RecycleView, but the ID and position may not match.
I would be grateful for a piece of code that solves my problem correctly.
My code:
class HomeItemAdapter(private val viewModel: MainViewModel)
: ListAdapter<Order, HomeItemAdapter.ViewHolder>(HomeItemDiffCallback()) {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
val inflater = LayoutInflater.from(parent.context)
return ViewHolder(inflater.inflate(R.layout.item_main, parent, false))
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
holder.bind(position)
}
inner class ViewHolder(val view: View) : RecyclerView.ViewHolder(view) {
private val title: TextView = view.findViewById(R.id.item_title)
fun bind(pos: Int) {
title.text = viewModel.orderList.value?.get(pos)?.publicOrderId ?: "Error: No Public ID"
view.setOnClickListener {
/*view.findNavController().navigate(R.id.action_detail,
setupInputData(item, adapterPosition).arguments)*/
//view.getTag(pos).
view.findNavController().navigate(R.id.action_homeFragment_to_orderFragment,setupInputData2(adapterPosition).arguments)
}
}
}
private fun setupInputData2(adapterPosition: Int): NavDirections{
return HomeFragmentDirections.actionHomeFragmentToOrderFragment(adapterPosition)
}
class HomeItemDiffCallback : DiffUtil.ItemCallback<Order>() {
override fun areItemsTheSame(oldItem: Order, newItem: Order): Boolean = oldItem == newItem
override fun areContentsTheSame(oldItem: Order, newItem: Order): Boolean = oldItem == newItem
}
}
HomeFragment
class HomeFragment : Fragment() {
private lateinit var viewModel: MainViewModel
private lateinit var recycler: RecyclerView
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?): View {
return inflater.inflate(R.layout.frg_main, container, false)
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
activity?.let {
// viewModel = ViewModelProviders.of(it).get(MainViewModel::class.java)
viewModel = ViewModelProvider(this).get(MainViewModel::class.java)
setRecyclerItemsObserver()
}
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
// set fab listener to start an action
view.addNewButton.setOnClickListener(Navigation.createNavigateOnClickListener(R.id.action_create, null))
recycler = view.recycler
setupRecycler()
}
private fun setRecyclerItemsObserver() {
viewModel.getItems().observe(this, Observer { list ->
list?.let {
(recycler.adapter as? HomeItemAdapter)?.submitList(it)
}
})
}
private fun setupRecycler() {
recycler.layoutManager = LinearLayoutManager(context)
val itemDecor = DividerItemDecoration(context, RecyclerView.VERTICAL)
recycler.addItemDecoration(itemDecor)
recycler.adapter = HomeItemAdapter(viewModel)
viewModel.prepareItems()
}
}
MainViewmodel Class
class MainViewModel : ViewModel() {
val orderList: MutableLiveData<List<Order>> = MutableLiveData()
fun getItems(): LiveData<List<Order>> = orderList
fun prepareItems() {
apiClient.getApiService().getOrders().enqueue(object : Callback<ResultOrders> {
override fun onResponse(call: Call<ResultOrders>?, response: Response<ResultOrders>?) {
if (response?.body() != null) {
orderList.postValue(response.body()!!.results.toMutableList())
} else
Log.d("[INFO]", "Body is null")
}
override fun onFailure(call: Call<ResultOrders>?, t: Throwable?) {
if (t != null && call != null) {
Log.i(
"[ERROR]",
t.message + "\t" + t.localizedMessage + "\t" + t.printStackTrace() + "\t" + t.cause + "\n" + call.request()
)
}
}
})
}
How can I get an order object ( in my case order.id) when clicking on the holder?
You can pass this information from onBindViewHolder because that has the current index of the row. Expose an interface from your fragment into your recycler view which can call some method passing the order id as a parameter.
Something like this
interface Callback {
void passOrderId(String orderId);
}
Fragment implement Callback {
#Override
void passOrderId(String orderId) {
// use the order id here
}
// pass this interface to RecyclerView while creating its object
RecyclerViewAdapter adapter = new Adapter(this);
}
Adapter.java
public void onBindViewHodler(.... int index) {
callback.passOrderId(orderList.get(index).getid());
}
I'm not sure if this is the right solution, but it will work!
You can simply add an internal variable to the
inner class ViewHolder
inner class ViewHolder(val view: View) : RecyclerView.ViewHolder(view) {
var id = -1
private val title: TextView = view.findViewById(R.id.item_title)
fun bind(pos: Int) {
id = viewModel.orderList.value?.get(pos)?.id!!;
title.text = viewModel.orderList.value?.get(pos)?.publicOrderId ?: "Error: No Public ID"
view.setOnClickListener {
/*view.findNavController().navigate(R.id.action_detail,
setupInputData(item, adapterPosition).arguments)*/
//view.getTag(pos).
view.findNavController().navigate(R.id.action_homeFragment_to_orderFragment,setupInputData2(id).arguments)
}
}
}
private fun setupInputData2(idOrder: Int): NavDirections{
return HomeFragmentDirections.actionHomeFragmentToOrderFragment(idOrder)
}

FireBase and LiveData the text on in the cardviews don't appear and the content doesn't update (I can see the cards but there is no content)

I use Firebase Realtime Database. I have RecycleViewAdapter class, a Fragment which shows the recycleview, a ViewHolder class which gives data to the fragment (I use LiveData) and there is a Repository class which communicates with the firebase database.
class Post(
var title: String = "",
var dateStr: String = "",
var desc: String = "",
var id: String = "")
class PostsFireBaseRepository(val bucketListViewModel: BucketListViewModel) {
private val path: String = "posts"
init {
initPostsListener()
}
fun getAllPosts() {
var data = mutableListOf<Post>()
FirebaseDatabase.getInstance().reference
.child(FirebaseAuth.getInstance().currentUser?.uid.toString())
.child(path)
.addListenerForSingleValueEvent(object : ValueEventListener {
override fun onDataChange(dataSnapshot: DataSnapshot) {
for (ds in dataSnapshot.children) {
val newPost = dataSnapshot.getValue<Post>(Post::class.java)
if (newPost != null) {
data.add(newPost)
}
}
bucketListViewModel.allPosts.value = data
bucketListViewModel.dataChanged()
}
override fun onCancelled(error: DatabaseError) {
}
})
}
class BucketListViewModel() : ViewModel() {
private val repository: PostsFireBaseRepository
var recyclerViewAdapter: BucketListRecyclerViewAdapter? = null
var allPosts: MutableLiveData<MutableList<Post>> = MutableLiveData<MutableList<Post>>()
init {
repository = PostsFireBaseRepository(this)
repository.getAllPosts()
}
fun dataChanged() {
recyclerViewAdapter?.notifyDataSetChanged()
}
}
class BucketListFragment : Fragment(), DatePickerDialogFragment.OnDateSelectedListener,
BucketListRecyclerViewAdapter.PostItemClickListener {
private lateinit var bucketListViewModel: BucketListViewModel
private lateinit var recyclerViewAdapter: BucketListRecyclerViewAdapter
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
return inflater.inflate(R.layout.fragment_bucketlist, container, false)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
setupRecyclerView()
bucketListViewModel = ViewModelProvider(requireActivity()).get(BucketListViewModel::class.java)
bucketListViewModel.allPosts.observe(viewLifecycleOwner) { posts ->
recyclerViewAdapter.addAll(posts)
}
}
BucketListRecyclerViewAdapter
class BucketListRecyclerViewAdapter() : RecyclerView.Adapter<BucketListRecyclerViewAdapter.ViewHolder>() {
interface PostItemClickListener {
fun onItemLongClick(position: Int, view: View, post: Post?): Boolean
fun onItemClick(position: Int, view: View, post: Post?): Boolean
}
private var postList = mutableListOf<Post>()
private var lastDeleted: Post? = null
private var lastDeletedPos: Int? = null
var postClickListener: PostItemClickListener? = null
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
val view = LayoutInflater.from(parent.context).inflate(R.layout.bucketlist_post_row, parent, false)
return ViewHolder(view)
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
val post = postList[holder.adapterPosition]
holder.post = post
holder.tvTitle.text = post.title
holder.tvDesc.text = post.desc
holder.tvRowDate.text = post.dateStr
}
override fun getItemCount() = postList.size
fun addItem(post: Post) {
postList.add(post)
notifyItemInserted(postList.size-1)
}
fun addAll(posts: List<Post>) {
postList.clear()
postList.addAll(posts)
notifyDataSetChanged()
}
In your onDataChange method you will need to inform that adapter that its data is changed, otherwise it won't repaint the UI element. To do this, you call notifyDataSetChanged() on the adapter after you've updated the data source for that adapter.
So something like:
override fun onDataChange(dataSnapshot: DataSnapshot) {
for (ds in dataSnapshot.children) {
val newPost = dataSnapshot.getValue<Post>(Post::class.java)
if (newPost != null) {
data.add(newPost)
}
}
bucketListViewModel.allPosts.value = data
adapter.notifyDataSetChanged();
}
Exactly how to reach the adapter depends on your code. For example, in your case you may have to add a method to the BucketListViewModel that you call from onDataChange and that then calls the adapter.

ViewModel still displaying old data after dialog dismissed and parent fragment back to view

I'm still trying to wrap my head around working with viewmodels and a bit confused now that I also have a dialog and recyclerview items but will try to be as clear as I can here if I can get any help please.
I have a dialog with items that when one of them is selected and closed should return data to my calling fragment so the selected item is displayed under that view.
However, once the item is selected and the dialog dismissed, I don't see the new selected item as the one showing on the UI but still the old item instead. (When the fragment is first started it displays the item that is set as selected on my list. The selected value is hardcoded at first but updated when the item is clicked and I can see the update has taken place when I debug the viewmodel observer inside the onDismiss method for the dialog).
I'm a couple of hours on this and have tried a few different things such as calling the viewmodel inside onResume or onDismiss and changing the viewmodel to be initiated by by activityViewModels() as per this post but none of these have worked so far and I think I'm stuck at the moment. Below my most recent version of the code.
class CovidCheckInFragment : Fragment(R.layout.fragment_covid_check_in) {
var navController: NavController? = null
private val model: MainViewModel by activityViewModels()
#RequiresApi(Build.VERSION_CODES.M)
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
navController = Navigation.findNavController(view)
model.userMutableLiveData.observe(viewLifecycleOwner, Observer<Any?> { list ->
if (list != null)
(list as Iterable<*>).map {
if ((it as ModelDialogOption).selected == true) {
tvHeader.text = it.title
}
}
})
}
}
..
class MyDialogFragment : DialogFragment(), RecyclerDialogOptionsItem.AdapterListener {
private val viewModel: MainViewModel by activityViewModels()
private lateinit var adapter: GroupAdapter<GroupieViewHolder>
var selectedPosition = -1
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setStyle(STYLE_NO_TITLE, R.style.AppTheme_Dialog_Custom)
}
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?
): View? {
return inflater.inflate(R.layout.fragment_dialog, container, false)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
rvOptions.layoutManager = LinearLayoutManager(activity)
adapter = GroupAdapter()
rvOptions.adapter = adapter
ivClose.setOnClickListener {
this.dismiss()
}
initViewModel()
}
private fun initViewModel() {
viewModel.userMutableLiveData.observe(this, Observer { list ->
for (i in list!!) {
adapter.add(
RecyclerDialogOptionsItem(
this#MyDialogFragment,
i,
this#MyDialogFragment
)
)
}
})
}
override fun onClickItem(position: Int) {
selectedPosition = position
adapter.notifyDataSetChanged()
Log.i("clicked", "position: $position")
}
}
..
class MainViewModel : ViewModel() {
private var list: ArrayList<ModelDialogOption>? = null
val userMutableLiveData: MutableLiveData<ArrayList<ModelDialogOption>?> = MutableLiveData()
init {
populateList()
userMutableLiveData.value = list!!
}
private fun populateList() {
list = ArrayList()
list!!.add(ModelDialogOption("Prefer not to say", false))
list!!.add(ModelDialogOption("16-39", false))
list!!.add(ModelDialogOption("40-59", true))
list!!.add(ModelDialogOption("60+", false))
}
}
..
class RecyclerDialogOptionsItem(
private val fragment: MyDialogFragment,
private val modelDialogOption: ModelDialogOption,
private val adapterListener: AdapterListener
) : Item<GroupieViewHolder>() {
companion object {
var clickListener: AdapterListener? = null
}
override fun bind(viewHolder: GroupieViewHolder, position: Int) {
viewHolder.apply {
with(viewHolder.itemView) {
tvTitle.text = modelDialogOption.title
clickListener = adapterListener
if (fragment.selectedPosition == position) {
ivChecked.visible()
modelDialogOption.selected = true
} else {
ivChecked.invisible()
modelDialogOption.selected = false
}
itemView.setOnClickListener {
clickListener?.onClickItem(adapterPosition)
}
}
}
}
override fun getLayout() = R.layout.rv_options_item_row
interface AdapterListener {
fun onClickItem(position: Int)
}
}
Thank you very much.
Your main view model should be like this
class MainViewModel : ViewModel() {
private var list: ArrayList<ModelDialogOption>? = null
val userMutableLiveData = MutableLiveData<ArrayList<ModelDialogOption>>()
init {
populateList()
userMutableLiveData.value = list!!
}
private fun populateList() {
list = ArrayList()
list!!.add(ModelDialogOption("Prefer not to say", false))
list!!.add(ModelDialogOption("16-39", false))
list!!.add(ModelDialogOption("40-59", true))
list!!.add(ModelDialogOption("60+", false))
}
fun updateItem(position:Int){
val itemToUpdate = list!!.get(position)
itemToUpdate.selected = !itemToUpdate.selected!!
list!![position] = itemToUpdate
}
fun flushItems(){
userMutableLiveData.value = list!!
}
}
Then from MyDialogFragment Should be like this.
class MyDialogFragment : DialogFragment(), RecyclerDialogOptionsItem.AdapterListener {
private val viewModel: MainViewModel by activityViewModels()
private lateinit var adapter: GroupAdapter<GroupieViewHolder>
var selectedPosition = -1
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setStyle(STYLE_NO_TITLE, R.style.AppTheme_Dialog_Custom)
}
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?
): View? {
return inflater.inflate(R.layout.fragment_dialog, container, false)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
rvOptions.layoutManager = LinearLayoutManager(activity)
adapter = GroupAdapter()
rvOptions.adapter = adapter
ivClose.setOnClickListener {
this.dismiss()
}
initViewModel()
}
override fun onDismiss(dialog: DialogInterface) {
super.onDismiss(dialog)
viewModel.flushItems()
}
private fun initViewModel() {
viewModel.userMutableLiveData.observe(this, Observer { list ->
for (i in list!!) {
adapter.add(
RecyclerDialogOptionsItem(
this#MyDialogFragment,
i,
this#MyDialogFragment
)
)
}
})
}
override fun onClickItem(position: Int) {
selectedPosition = position
adapter.notifyDataSetChanged()
viewModel.updateItem(position)
Log.i("clicked", "position: $position")
}
}

RecyclerView doesn`t get elements from firestore KOTLIN firebase

i want to get recyclerview in my homefragment end set into elements from firestore firebase
my problem with get elements from firestore! maybe whos know how to search a problem because elements doesnt come frome firestore!
HomeFragment
class HomeFragment : Fragment() {
private val TAG = "HomeFragment"
private val firebaseRepository: FirebaseNoteRepo = FirebaseNoteRepo()
private var noteList: List<NotesModel> = ArrayList()
private val notesListAdapter: NotesListAdapterClass = NotesListAdapterClass(noteList)
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
// Inflate the layout for this fragment
return inflater.inflate(R.layout.fragment_home, container, false)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
init(view)
}
private fun init(view: View) {
if(firebaseRepository.getUser() == null){
Log.d(TAG,"Error log user")
}else{ loadNotesData()}
note_list_recycler_view.layoutManager = LinearLayoutManager(context)
note_list_recycler_view.adapter = notesListAdapter
add_note_fab.setOnClickListener {
noteCreateDialog(view)
}
}
private fun loadNotesData() {
firebaseRepository.getNoteList().addOnCompleteListener {
if (it.isSuccessful){
noteList = it.result!!.toObjects(NotesModel::class.java)
notesListAdapter.notesListItem = noteList
notesListAdapter.notifyDataSetChanged()
}else{
Log.d(TAG,"Error: ${it.exception!!.message}")
}
}
}
my NotesModel
data class NotesModel(
val title: String = "",
val date: com.google.firebase.Timestamp? = null,
val post_type: Long = 0
)
my repository
FirebaseNoreRepo
class FirebaseNoteRepo {
private val firebaseAuth: FirebaseAuth = FirebaseAuth.getInstance()
private val firebaseFirestore: FirebaseFirestore = FirebaseFirestore.getInstance()
fun getUser(): FirebaseUser? {
return firebaseAuth.currentUser
}
fun getNoteList(): Task<QuerySnapshot> {
Log.d("HomeFragment"," ad "+ firebaseFirestore.collection("Notes").get().toString())
return firebaseFirestore
.collection("Notes")
.orderBy("title", Query.Direction.ASCENDING)
.get()
}
and my adapter
class NotesListAdapterClass (var notesListItem: List<NotesModel>):
RecyclerView.Adapter<RecyclerView.ViewHolder>() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
val view = LayoutInflater.from(parent.context).inflate(R.layout.note_layout,parent,false)
return ViewHolderNotes(view)
}
override fun getItemViewType(position: Int): Int {
return if (notesListItem[position].post_type == 1L){
POST_TYPE1
}else{
POST_TYPE1
}
}
override fun getItemCount(): Int {
Log.d("HomeFragment","notesListAdapter size : "+notesListItem.size)
return notesListItem.size
}
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
if (getItemViewType(position) == POST_TYPE1){
(holder as ViewHolderNotes).bind(notesListItem[position])
}
}
class ViewHolderNotes(itemView: View):RecyclerView.ViewHolder(itemView){
fun bind(notesModel: NotesModel){
itemView.note_title.text = notesModel.title
}
}
}
in my adapter i have Log.d he say me size of list of item ( is always equal 0), in firestore i have 2 elements. i dont understand why in log i have 0!
Database sceenshot

Categories

Resources