I have been trying to call a function from fragment to adapter, but I can't approach it right.
I want to invisible the button present in the fragment from adapter.
**My Adapter Code:**
package com.littleboo.brandlogo.Adapters
import android.R
import android.content.Context
import android.content.Intent
import android.graphics.Color
import android.graphics.drawable.ColorDrawable
import android.net.Uri
import android.os.Build
import android.os.VibrationEffect
import android.os.Vibrator
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.view.animation.Animation
import android.view.animation.AnimationUtils
import android.widget.Button
import android.widget.TextView
import android.widget.Toast
import androidx.appcompat.app.AlertDialog
import androidx.core.content.ContextCompat.getSystemService
import androidx.recyclerview.widget.RecyclerView
import com.littleboo.brandlogo.Fragments.QuizFragment
import com.littleboo.brandlogo.MainActivity
import com.littleboo.brandlogo.Models.Question
import com.littleboo.brandlogo.databinding.ActivityMainBinding.inflate
import com.littleboo.brandlogo.databinding.FragmentQuizBinding
import kotlinx.coroutines.NonDisposableHandle.parent
class QuestionAdap(val context: Context, val question: Question) :
RecyclerView.Adapter<QuestionAdap.OptionViewHolder>() {
var index = 1
var score = 0
val animShake: Animation = AnimationUtils.loadAnimation(context, com.littleboo.brandlogo.R.anim.shake)
private var options: List<String> = listOf(question.option1, question.option2, question.option3, question.option4)
inner class OptionViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView){
var optionView = itemView.findViewById<TextView>(com.littleboo.brandlogo.R.id.quiz_option)
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): OptionViewHolder {
val view = LayoutInflater.from(context).inflate(com.littleboo.brandlogo.R.layout.quizoptions, parent, false)
return OptionViewHolder(view)
}
override fun onBindViewHolder(holder: OptionViewHolder, position: Int) {
holder.optionView.text = options[position]
holder.itemView.setOnClickListener {
question.userAnswer = options[position]
notifyDataSetChanged()
}
if(question.userAnswer == options[position] && question.userAnswer == question.answer){
holder.itemView.setBackgroundResource(com.littleboo.brandlogo.R.drawable.option_item_selected_bg)
score += 10
Toast.makeText(context,"Score is $score", Toast.LENGTH_SHORT).show()
}
else if(question.userAnswer == options[position] && question.userAnswer != question.answer){
holder.itemView.setBackgroundResource(com.littleboo.brandlogo.R.drawable.wrong_option_item_selected_bg)
holder.itemView.startAnimation(animShake)
}
else{
holder.itemView.setBackgroundResource(com.littleboo.brandlogo.R.drawable.non_option_item_selected_bg)
}
}
override fun getItemCount(): Int {
return options.size
}
}
My Quiz Fragment Code:
package com.littleboo.brandlogo.Fragments
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.Button
import androidx.fragment.app.Fragment
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import androidx.swiperefreshlayout.widget.CircularProgressDrawable
import com.bumptech.glide.Glide
import com.google.firebase.firestore.FirebaseFirestore
import com.littleboo.brandlogo.Adapters.QuestionAdap
import com.littleboo.brandlogo.Models.Question
import com.littleboo.brandlogo.Models.quizmodel
import com.littleboo.brandlogo.R
import com.littleboo.brandlogo.databinding.FragmentQuizBinding
class QuizFragment : Fragment(){
lateinit var binding: FragmentQuizBinding
var quizzes: MutableList<quizmodel>? = null
private var questions = mutableMapOf<String, Question>()
private lateinit var mArraylist: ArrayList<Question>
var index = 1
private lateinit var mfirestore: FirebaseFirestore
private lateinit var mrecycler: RecyclerView
lateinit var myadapter: QuestionAdap
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
}
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
binding = FragmentQuizBinding.inflate(layoutInflater)
return binding.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
mrecycler = binding.optionList
mrecycler.layoutManager
myadapter = QuestionAdap(requireContext(),Question())
mArraylist = arrayListOf()
questions.map { it.key to it.value }.shuffled().toMap()
setUpFirestore()
setUpEventListener()
}
override fun onDestroy() {
super.onDestroy()
mfirestore.terminate()
// finish()
}
private fun setUpEventListener() {
binding.nextbtn.setOnClickListener {
index++
bindViews()
}
// binding.btnSubmit.setOnClickListener {
// Log.d("FINALQUIZ", questions.toString())
// val intent = Intent(this, ResultActivity::class.java)
// val json = Gson().toJson(quizzes!![0])
// intent.putExtra("QUIZ", json)
// startActivity(intent)
// finish()
// }
}
private fun setUpFirestore() {
mfirestore = FirebaseFirestore.getInstance()
val quizTitle = activity?.intent?.getStringExtra("title")
if (quizTitle != null) {
mfirestore.collection("Quizes").whereEqualTo("title", quizTitle)
.get()
.addOnSuccessListener {
if (it != null && !it.isEmpty) {
quizzes = it.toObjects(quizmodel::class.java)
questions = quizzes!![0].questions
shuffle()
bindViews()
}
}
}
}
private fun bindViews() {
// btnPrevious.visibility = View.GONE
// binding.btnSubmit.visibility = View.GONE
// binding.btnNext.visibility = View.GONE
// if (index == 1) { //first question
// binding.btnNext.visibility = View.VISIBLE
// } else if (index == questions!!.size) { // last question
// binding.btnSubmit.visibility = View.VISIBLE
//// btnPrevious.visibility = View.VISIBLE
// } else { // Middle
//// btnPrevious.visibility = View.VISIBLE
// binding.btnNext.visibility = View.VISIBLE
// }
val circularProgressDrawable = CircularProgressDrawable(requireContext())
circularProgressDrawable.strokeWidth = 8f
// circularProgressDrawable.colorFilter = ("#ac5fe1")
circularProgressDrawable.centerRadius = 30f
circularProgressDrawable.start()
val question = questions!!["question$index"]
question?.let {
Glide.with(this).load(it.imagequiz).placeholder(circularProgressDrawable).into(binding.imagequiz)
val optionAdapter = QuestionAdap(requireContext(), it)
mrecycler.layoutManager = LinearLayoutManager(requireContext())
mrecycler.adapter = optionAdapter
mrecycler.setHasFixedSize(true)
}
}
private fun shuffle() {
val keys = questions.keys.toMutableList().shuffled()
val values = questions.values.toMutableList().shuffled()
keys.forEachIndexed { index, key ->
questions[key] = values[index]
}
}
}
I tried calling fragment in adapter like:
QuizFragment().binding.btnxt.visibility = View.Visible
in BindViewHolder function.
Thank You
You can use kotlin lambda function to achieve it. Like this
class QuestionAdap(val context: Context, val question: Question, var onItemClicked: ((boolean) -> Unit))
call onItemClicked in your adapter where you want it
and in fragment
myadapter = QuestionAdap(requireContext(),Question()) { boolean ->
if (boolean) {
//hide/show
} else {
//hide/show
}
}
You can use the Callback functions of kotlin in your fragment, which will be passed into your adapter. So whenever you invoke that callback from your adapter, it will be triggered in your fragment.
Step 1: Create a method like the one below in your fragment.
private fun showHideButtonFromAdapter (
isButtonVisible: Boolean
) {
// set your button visibility according to isButtonVisible value.
}
Step 2: pass a method from your fragment to adapter as argument
val adapter = YourAdapter(::showHideButtonFromAdapter)
// set above adapter in your recycler view.
Step 3: In your adapter invoke that callback function like the one below.
class ColorPickerAdapter constructor(
private val onItemClicked: (Boolean) -> Unit
) : RecyclerView.Adapter<YourAdapter.ViewHolder>() {
// your other adapter methods here
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
onItemClicked.invoke(pass true or false as per your requirement)
// above invocation will trigger an event in the fragment.
}
}
In your adapter there need to be instance of your fragment, so change it like:
class QuestionAdap(val context: Context, val question: Question, fragment: Fragment) :
RecyclerView.Adapter<QuestionAdap.OptionViewHolder>() {
Then you can call your fragment by simply writing fragment in your adapter like:
override fun onBindViewHolder(holder: OptionViewHolder, position: Int) {
holder.optionView.text = options[position]
holder.itemView.setOnClickListener {
question.userAnswer = options[position]
notifyDataSetChanged()
}
//Example
fragment.shuffle()
And you need to send this fragment instance when you create it's adapter, so in your fragment use this:
myadapter = QuestionAdap(requireContext(),Question(), this)
Related
I am simply trying to setup the ClickListener for changing the user name. Google's tutorials emphasize fragments, which I will do later. Based on what I've seen in other examples and the Android documentation, I thought I had the View Binding set up properly. I don't have enough information to understand why I see a list of copies of the activity screen inside the RecyclerView.
UserListAdapter.kt
package com.neillbarrett.debitsandcredits
import android.util.Log
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.EditText
import android.widget.TextView
import android.widget.Toast
import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.ListAdapter
import androidx.recyclerview.widget.RecyclerView
import com.neillbarrett.debitsandcredits.database.UsersTable
import com.neillbarrett.debitsandcredits.databinding.ActivityManageUsersBinding
val inAdapter: String = "In UserListAdapter "
class UserListAdapter(private val userSelect: (UsersTable?) -> Unit) :
ListAdapter<UsersTable, UserListAdapter.UserViewHolder>(UsersComparator()) {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): UserListAdapter.UserViewHolder {
Log.w(inAdapter,"OnCreateViewHolder started")
val view = LayoutInflater.from(parent.context)
.inflate(R.layout.activity_manage_users, parent, false)
return UserViewHolder.create(parent)
}
class UserViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
fun bind(usersTable: UsersTable?, userSelect: (UsersTable?) -> Unit) {
Log.w(inAdapter,"UserViewHolder started")
itemView.setOnClickListener { View.OnClickListener {
/* if (View.) { }*/
val nameSelected = userSelect(usersTable)
//userSelect(usersTable)
//need to assign the result of the clicklistener to the editText
//binding.etEditName.setText(R.layout.activity_list_of_users.toString())
}}
}
companion object {
fun create(parent: ViewGroup) : UserViewHolder {
Log.w(inAdapter,"Companion object 'Create' function started")
val view: View = LayoutInflater.from(parent.context)
.inflate(R.layout.activity_manage_users, parent, false)
return UserViewHolder(view)
}
}
}
override fun onBindViewHolder(holder: UserViewHolder, position: Int) {
Log.w(inAdapter,"OnBindViewHolder started")
val current = getItem(position)
holder.bind(current, userSelect)
}
class UsersComparator : DiffUtil.ItemCallback<UsersTable>() {
override fun areItemsTheSame(oldItem: UsersTable, newItem: UsersTable): Boolean {
Log.w(inAdapter,"areItemsTheSame function started")
return oldItem == newItem
}
override fun areContentsTheSame(oldItem: UsersTable, newItem: UsersTable): Boolean {
Log.w(inAdapter,"areContentsTheSame function started")
return oldItem.userName == newItem.userName
}
}
}
ManageUsers.kt
package com.neillbarrett.debitsandcredits
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.text.TextUtils
import android.util.Log
import android.widget.Button
import android.widget.EditText
import android.widget.Toast
import androidx.activity.viewModels
import androidx.lifecycle.Observer
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.neillbarrett.debitsandcredits.database.CreditsAndDebitsApp
import com.neillbarrett.debitsandcredits.database.UsersTable
import com.neillbarrett.debitsandcredits.databinding.ActivityManageUsersBinding
class ManageUsers : AppCompatActivity() {
lateinit var binding: ActivityManageUsersBinding
lateinit var recyclerView: RecyclerView
lateinit var editTextAddUser: EditText
lateinit var editTextChangeUser: EditText
lateinit var newUser: String
var userSelect: ((UsersTable?) -> Unit) = {}
var position: Long = 0
val inWhichActivity: String = "In ManageUsers"
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityManageUsersBinding.inflate(layoutInflater)
val view = binding.root
setContentView(view)
//setContentView(R.layout.activity_manage_users)
Log.w(inWhichActivity, "Setting up userViewModel & repository")
val userViewModel: UserViewModel by viewModels {
UserViewModelFactory((application as CreditsAndDebitsApp).repository)
}
recyclerView = findViewById(R.id.rec_view_userList)
editTextAddUser = findViewById(R.id.et_AddUser)
editTextChangeUser = findViewById(R.id.et_Edit_Name)
val adapter = UserListAdapter(userSelect)
binding.recViewUserList.adapter = adapter
recyclerView.adapter = adapter
recyclerView.layoutManager = LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false)
userViewModel.allUsers.observe(this, Observer() {user ->
Log.w(inWhichActivity,"Starting Observer")
user?.let { adapter.submitList(it) }
Log.w(inWhichActivity, "Started Observer")
})
val btnAddUser = findViewById<Button>(R.id.btn_AddUser)
btnAddUser.setOnClickListener {
Log.w(inWhichActivity,"Started btnAddUser.setOnClickListener")
if (TextUtils.isEmpty(editTextAddUser.text)) {
Toast.makeText(this, "User name cannot be empty", Toast.LENGTH_SHORT).show()
} else {
newUser = editTextAddUser.text.toString()
// Log.w("Add user button", "Username put into newUser")
userViewModel.insertUser(UsersTable(0, newUser))
// Toast.makeText(this, "Username added to table", Toast.LENGTH_SHORT).show()
// Log.w("Add user button", "Username added to table")
}
}
val btnChangeUser = findViewById<Button>(R.id.btn_ChangeUserName)
btnChangeUser.setOnClickListener {
Log.w(inWhichActivity,"Started btnChangeUser.setOnClickListener")
Toast.makeText(this, "Selected position is ${recyclerView.getChildAdapterPosition(it)}", Toast.LENGTH_SHORT).show()
/* if (recyclerView.getChildAdapterPosition(it) == -1) {
Toast.makeText(this, "Select a name.", Toast.LENGTH_SHORT).show()
} else {
if (editTextChangeUser.text.toString() == recyclerView.adapter.toString()) {
Toast.makeText(this, "Name has not been changed.", Toast.LENGTH_SHORT).show()
} else {
Toast.makeText(this, "Name would have been changed.", Toast.LENGTH_SHORT).show()
val rvItemRecId: Long
rvItemRecId = adapter.getItemId(position.toInt())
userViewModel.updateUser(UsersTable(rvItemRecId.toInt(), adapter.toString()))
}
}*/
}
}
}
UserViewModel.kt
package com.neillbarrett.debitsandcredits
import android.util.Log
import androidx.lifecycle.*
import com.neillbarrett.debitsandcredits.database.UsersTable
import kotlinx.coroutines.launch
import java.lang.IllegalArgumentException
val inViewModel: String = "In UserViewModel "
class UserViewModel(private val repository: UserRepository) : ViewModel() {
// Using LiveData and caching what allWords returns has several benefits:
// - We can put an observer on the data (instead of polling for changes) and only update the
// the UI when the data actually changes.
// - Repository is completely separated from the UI through the ViewModel.
val allUsers: LiveData<List<UsersTable>> = repository.allUsers.asLiveData()
/**
* Launching a new coroutine to insert the data in a non-blocking way
*/
fun insertUser(user: UsersTable) = viewModelScope.launch {
repository.insertUser(user)
Log.w(inViewModel,"insertUser called")
//repository.insertUser(usersTable = List<UsersTable>())
//repository.insertUser(UsersTable(0, userName = user.userName))
}
fun updateUser(user: UsersTable) = viewModelScope.launch {
repository.updateUser(user)
Log.w(inViewModel, "updateUser called")
}
fun deleteUser(user: UsersTable) = viewModelScope.launch {
repository.deleteUser(user)
Log.w(inViewModel, "deteUser called")
}
}
class UserViewModelFactory(private val repository: UserRepository) : ViewModelProvider.Factory{
override fun <T : ViewModel> create(modelClass: Class<T>): T {
Log.w(inViewModel,"UserViewModelFactory called")
if (modelClass.isAssignableFrom(UserViewModel::class.java)) {
#Suppress("UNCHECKED_CAST")
return UserViewModel(repository) as T
}
throw IllegalArgumentException("Unknown ViewModel class")
}
}
As of now I have fetched some product data from api and showing it on homescreen. Now I have detailed screen for each product and from detailed screen user can add that product with whatever quantity into cart screen. Now I want to add multiple products with any number of quantity into cart screen and each product is shown on different cards into cart screen.
How can I achieve that? Currently I am able to do it for one product but quantity value is same for all types of products on click.
This is my ProductDetail fragment for each product from where I am adding items and it's quantity to the cart.
package com.example.ecomapp.view
import android.app.Application
import android.os.Bundle
import android.util.Log
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.Toast
import androidx.activity.viewModels
import androidx.appcompat.app.AppCompatActivity
import androidx.core.os.bundleOf
import androidx.fragment.app.*
import com.example.ecomapp.CartFragment
import com.example.ecomapp.R
import com.example.ecomapp.databinding.FragmentProductDetailBinding
import com.example.ecomapp.viewmodel.MainViewModel
import com.google.android.material.bottomnavigation.BottomNavigationView
import java.util.*
import kotlin.collections.ArrayList
class ProductDetailFragment : Fragment() {
lateinit var binding: FragmentProductDetailBinding
//var quantityValue = 0
private val viewmodel:MainViewModel by activityViewModels()
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
// Inflate the layout for this fragment
/// return inflater.inflate(R.layout.fragment_product_detail, container, false)
binding = FragmentProductDetailBinding.inflate(inflater,container,false)
// Log.d("PriceAmount", binding.priceAmount.text.toString())
Log.d("Price", binding.price.toString())
Log.d("Title", binding.titleDetail.toString())
return binding.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
val navBar:BottomNavigationView = requireActivity().findViewById(R.id.bottomNavigation)
// navBar.visibility = View.GONE
binding.productDetails1 = arguments?.getParcelable("product")
var productDetail = binding.productDetails1
val currency:Currency = Currency.getInstance(Locale.getDefault())
val symbol = currency.symbol
binding.price.text = "$symbol ${productDetail?.price}"
binding.lifecycleOwner = viewLifecycleOwner
viewmodel.idsList.add(productDetail?.id)
val array = viewmodel.idsList
Log.d("List of ids",array.toString())
// var priceAmount = binding.priceAmount.text.toString().toFloat()
// Log.d("PriceAmount", binding.priceAmount.toString())
// Log.d("ProductTitle", binding.productDetails1.title)
binding.addButton.setOnClickListener {
// Log.d("ProductDetail",initialValue.toString())
if (productDetail == null) return#setOnClickListener
viewmodel.quantityValue = viewmodel.quantityValue + 1
// binding.totalPriceText.text = "Total price"
binding.quantityText.text = viewmodel.quantityValue.toString()
// binding.priceAmount.text =
// (productDetail.price * viewmodel.quantityValue).toString()
}
binding.addCartButton.setOnClickListener{
viewmodel.quantityValue = viewmodel.quantityValue + 1
binding.quantityText.text = viewmodel.quantityValue.toString()
Toast.makeText(activity, "Item added to cart", Toast.LENGTH_SHORT).show()
}
// Log.d("ProductDetail",priceAmount.toString())
/* binding.removeButton.setOnClickListener {
if (quantityValue != 0) {
if (productDetail == null) return#setOnClickListener
quantityValue = quantityValue - 1
binding.quantityText.text = quantityValue.toString()
// binding.priceAmount.text = (priceAmount*quantityValue).toString()
binding.priceAmount.text = (productDetail.price * quantityValue).toString()
}
} */
binding.continueButton.setOnClickListener{
val bundle = Bundle()
bundle.putString("quantity_text", String.format("%d",viewmodel.quantityValue))
val cartFragment = CartFragment()
cartFragment.arguments = bundle
val ft = fragmentManager?.beginTransaction()
ft?.replace(R.id.flFragment,cartFragment)
ft?.addToBackStack("")
ft?.commit()
// Create the transaction
// Create the transaction
}
Toast.makeText(activity,"onCreateViewCalled",Toast.LENGTH_SHORT).show()
// binding.productDetails = viewModel.products.value!!.get(0)
// binding.lifecycleOwner = viewLifecycleOwner
}
override fun onResume() {
super.onResume()
(activity as AppCompatActivity?)!!.supportActionBar!!.hide()
binding.quantityText.text = viewmodel.quantityValue.toString()
// binding.priceAmount.text = (binding.productDetails1!!.price!! * viewmodel.quantityValue).toString()
Toast.makeText(activity,"onResumeCalled",Toast.LENGTH_SHORT).show()
}
override fun onStop() {
super.onStop()
// (activity as AppCompatActivity?)!!.supportActionBar!!.show()
Toast.makeText(activity,"onStopCalled",Toast.LENGTH_SHORT).show()
}
override fun onPause() {
super.onPause()
Toast.makeText(activity,"onPauseCalled",Toast.LENGTH_SHORT).show()
}
override fun onDestroyView() {
super.onDestroyView()
Toast.makeText(activity,"onDestroyViewCalled",Toast.LENGTH_SHORT).show()
}
override fun onDestroy() {
super.onDestroy()
Toast.makeText(activity,"onDestroyCalled",Toast.LENGTH_SHORT).show()
}
}
And this is my MainViewModel from where I am getting data of quantity value throughout the fragments.
package com.example.ecomapp.viewmodel
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.example.ecomapp.data.Repository
import com.example.ecomapp.model.ProductDataModel
import com.example.ecomapp.model.ProductsData
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.launch
class MainViewModel: ViewModel(){
private val repository = Repository()
private val _products = MutableLiveData<List<ProductDataModel>>()
val products:LiveData<List<ProductDataModel>> = _products
private val _isLoading = MutableStateFlow(true)
val isLoading = _isLoading.asStateFlow()
private var quantity_value = 0
var quantityValue = quantity_value
init {
viewModelScope.launch {
delay(3000)
_isLoading.value = false
}
getProducts()
}
fun addQuantityValue(): Int? {
quantity_value = quantityValue + 1
return quantity_value
}
/* fun productPrice(quantity:Int):Float {
_price.value =
} */
private fun getProducts() {
viewModelScope.launch {
val data = repository.getProducts()
_products.postValue(data)
}
}
}
I am making a quiz app. My main activity has blocks. Each block has a date written over it. the date act as title while fetching information from firebase. These blocks are working perfectly fine, (fetching questions).I also have a floating action button, on clicking displays a calender and we can select date from there and access quiz for that particular day, but it is not working .
Home screen : home screen
Interface while accessing question from block : image
Interface while accessing through floating action bar :image
Main Activity:
package com.example.quiz.activities
import android.annotation.SuppressLint
import android.content.Intent
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.util.Log
import android.view.MenuItem
import android.widget.Toast
import androidx.appcompat.app.ActionBarDrawerToggle
import androidx.recyclerview.widget.GridLayoutManager
import com.example.quiz.R
import com.example.quiz.adapters.QuizAdapter
import com.example.quiz.databinding.ActivityMainBinding
import com.example.quiz.models.Quiz
import com.google.android.material.datepicker.MaterialDatePicker
import com.google.firebase.firestore.DocumentSnapshot
import com.google.firebase.firestore.FirebaseFirestore
import java.text.SimpleDateFormat
import java.util.*
class MainActivity() : AppCompatActivity() {
private lateinit var binding: ActivityMainBinding
lateinit var actionBarDrawerToggle: ActionBarDrawerToggle
lateinit var adapter: QuizAdapter
private var quizList = mutableListOf<Quiz>()
lateinit var firestore : FirebaseFirestore
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)
setUpViews()
}
fun setUpViews(){
setUpFirestore()
setUpDrawerLayout()
setUpRecyclerView()
setUpDatePicker()
}
#SuppressLint("SimpleDateFormat")
private fun setUpDatePicker() {
// val btnDatePicker = binding.btnDatePicker
binding.btnDatePicker.setOnClickListener{
val datePicker = MaterialDatePicker.Builder.datePicker().build()
datePicker.show(supportFragmentManager,"DatePicker")
datePicker.addOnPositiveButtonClickListener {
Log.d("DATEPICKER",datePicker.headerText)
val dateFormatter = SimpleDateFormat("dd-mm-yyyy")
val date = dateFormatter.format(Date(it))
val intent = Intent(this,QuestionActivity::class.java)
intent.putExtra("DATE",date)
startActivity(intent)
}
datePicker.addOnNegativeButtonClickListener {
Log.d("DATEPICKER",datePicker.headerText)
}
datePicker.addOnCancelListener {
Log.d("DATEPICKER","Date Picker was cancelled")
}
}
}
private fun setUpFirestore() {
firestore = FirebaseFirestore.getInstance()
val collectionReference = firestore.collection("quizzes")
collectionReference.addSnapshotListener{ value, error ->
if(value == null || error != null){
Toast.makeText(this,"Error fetching data",Toast.LENGTH_SHORT).show()
return#addSnapshotListener
}
Log.d("DATA", value.toObjects(Quiz::class.java).toString())
quizList.clear()
quizList.addAll(value.toObjects(Quiz::class.java))
adapter.notifyDataSetChanged()
}
}
private fun setUpRecyclerView(){
val quizRecyclerView = binding.quizRecyclerView
adapter = QuizAdapter(this,quizList)
quizRecyclerView.layoutManager = GridLayoutManager(this,2)
quizRecyclerView.adapter = adapter
}
private fun setUpDrawerLayout(){
val appBar = binding.appBar
val mainDrawer = binding.mainDrawer
val navigationView = binding.navigationView
setSupportActionBar(appBar)
actionBarDrawerToggle = ActionBarDrawerToggle(this,mainDrawer,
R.string.app_name,
R.string.app_name
)
actionBarDrawerToggle.syncState()
navigationView.setNavigationItemSelectedListener {
val intent = Intent(this,ProfileActivity::class.java)
startActivity(intent)
mainDrawer.closeDrawers()
true
}
}
override fun onOptionsItemSelected(item: MenuItem): Boolean {
if (actionBarDrawerToggle.onOptionsItemSelected(item)){
return true
}
return super.onOptionsItemSelected(item)
}
}
Quiz adapter
package com.example.quiz.adapters
import android.content.Context
import android.content.Intent
import android.graphics.Color
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.ImageView
import android.widget.TextView
import android.widget.Toast
import androidx.cardview.widget.CardView
import androidx.recyclerview.widget.RecyclerView
import com.example.quiz.R
import com.example.quiz.activities.QuestionActivity
import com.example.quiz.models.Quiz
import com.example.quiz.utils.ColorPicker
import com.example.quiz.utils.IconPicker
class QuizAdapter(val context: Context, val quizzes: List<Quiz>) :
RecyclerView.Adapter<QuizAdapter.QuizViewHolder>() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): QuizViewHolder {
val view = LayoutInflater.from(context).inflate(R.layout.quiz_item,parent,false)
return QuizViewHolder(view)
}
override fun onBindViewHolder(holder: QuizViewHolder, position: Int) {
holder.textViewTitle.text = quizzes[position].title
holder.cardContainer.setCardBackgroundColor(Color.parseColor(ColorPicker.getColor()))
holder.iconView.setImageResource(IconPicker.getIcon())
holder.itemView.setOnClickListener{
Toast.makeText(context,quizzes[position].title,Toast.LENGTH_SHORT).show()
val intent = Intent(context , QuestionActivity::class.java)
intent.putExtra("DATE",quizzes[position].title)
context.startActivity(intent)
}
}
override fun getItemCount(): Int {
return quizzes.size
}
inner class QuizViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
var textViewTitle: TextView = itemView.findViewById(R.id.quizTitle)
var iconView: ImageView = itemView.findViewById(R.id.quizIcon)
var cardContainer: CardView = itemView.findViewById(R.id.cardContainer)
}
}
Question Activity
package com.example.quiz.activities
import android.content.Intent
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.util.Log
import android.view.LayoutInflater
import android.view.View
import android.widget.Toast
import androidx.core.view.allViews
import androidx.recyclerview.widget.LinearLayoutManager
import com.example.quiz.R
import com.example.quiz.adapters.OptionAdapter
import com.example.quiz.databinding.ActivityQuestionBinding
import com.example.quiz.models.Question
import com.google.firebase.firestore.FirebaseFirestore
import com.example.quiz.models.Quiz
import com.google.gson.Gson
class QuestionActivity : AppCompatActivity() {
var quizzes : MutableList<Quiz>? = null
var questions : MutableMap<String,Question>? = null
var index = 1
lateinit var firestore : FirebaseFirestore
private lateinit var binding: ActivityQuestionBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityQuestionBinding.inflate(layoutInflater)
setContentView(binding.root)
setUpFireStore()
setUpEventListener()
}
private fun setUpEventListener() {
val btnPrevious = binding.btnPrevious
val btnSubmit = binding.btnSubmit
val btnNext = binding.btnNext
btnPrevious.setOnClickListener{
index--
bindViews()
}
btnNext.setOnClickListener{
index++
bindViews()
}
btnSubmit.setOnClickListener{
Log.d("FINAL QUIZ",questions.toString())
val intent = Intent(this,ResultActivity::class.java)
val json = Gson().toJson(quizzes!![0])
intent.putExtra("QUIZ",json)
startActivity(intent)
}
}
private fun setUpFireStore() {
val firestore = FirebaseFirestore.getInstance()
var date = intent.getStringExtra("DATE")
if (date!= null){
firestore.collection("quizzes").whereEqualTo("title",date)
.get()
.addOnSuccessListener {
if (it != null && !it.isEmpty){
Log.d("DATA",it.toObjects(Quiz::class.java).toString())
quizzes = it.toObjects(Quiz::class.java)
questions = quizzes!![0].questions
bindViews()
}
else{
Toast.makeText(this,"Error Fetching data",Toast.LENGTH_SHORT).show()
}
}
}
}
private fun bindViews() {
val btnPrevious = binding.btnPrevious
val btnSubmit = binding.btnSubmit
val btnNext = binding.btnNext
val description = binding.description
val optionList = binding.optionList
btnPrevious.visibility = View.GONE
btnSubmit.visibility = View.GONE
btnNext.visibility = View.GONE
if (index == 1) {
btnNext.visibility = View.VISIBLE
}
else if(index == questions!!.size){
btnSubmit.visibility = View.VISIBLE
btnPrevious.visibility = View.VISIBLE
}
else{
btnNext.visibility = View.VISIBLE
btnPrevious.visibility = View.VISIBLE
}
val question = questions!!["question$index"]
question?.let {
description.text = it.description
val optionAdapter = OptionAdapter(this,it)
optionList.layoutManager = LinearLayoutManager(this)
optionList.adapter = optionAdapter
optionList.setHasFixedSize(true)
}
}
}
No logcat error is shown but this error is shown in run section : E/RecyclerView: No adapter attached; skipping layout
I'm currently writing an app that displays a list of movies. I have many fragments that display a cardview containing movies, and each cardview has a checkbox. The user can press on the cardview to go to the details page of the movie where another checkbox is present.
The goal of both checkboxes is to add the movie to the favorites tab.
My question is, how can I make the checkbox that is inside the details page checked when the user checks the one in the cardview?
Below is the relevant code.
Appreciate all the help I can get.
MoviesListFragment.kt
package com.example.moviesapp.ui.Fragments
import android.os.Bundle
import android.view.*
import androidx.core.view.isGone
import androidx.core.view.isVisible
import androidx.fragment.app.Fragment
import androidx.fragment.app.viewModels
import androidx.navigation.fragment.findNavController
import androidx.recyclerview.widget.LinearLayoutManager
import com.example.moviesapp.R
import com.example.moviesapp.databinding.FragmentMoviesListBinding
import com.example.moviesapp.network.MoviesFavorites
import com.example.moviesapp.network.MoviesResults
import com.example.moviesapp.ui.DaoViewModel
import com.example.moviesapp.ui.MovieApiStatus
import com.example.moviesapp.ui.MoviesListAdapter
import com.example.moviesapp.ui.MoviesListViewModel
import dagger.hilt.android.AndroidEntryPoint
#AndroidEntryPoint
class MoviesListFragment : Fragment(R.layout.fragment_movies_list), MoviesListAdapter.OnItemClickListener {
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
return inflater.inflate(R.layout.fragment_movies_list, container, false)
}
private val daoViewModel by viewModels<DaoViewModel>()
private val viewModel by viewModels<MoviesListViewModel>()
private var _binding: FragmentMoviesListBinding? = null
private val binding get() = _binding!!
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
//View is inflated layout
_binding = FragmentMoviesListBinding.bind(view)
val adapter = MoviesListAdapter(this)
binding.apply {
recyclerView.layoutManager = LinearLayoutManager(requireContext())
//Disable animations
recyclerView.setHasFixedSize(true)
recyclerView.adapter = adapter
}
//Observe the movies livedata
//Use viewLifecycleOwner instead of this because the UI should stop being updated when the fragment view is destroyed
viewModel.getTrending()
viewModel.moviesTrending.observe(viewLifecycleOwner) {
adapter.submitList(it)
}
viewModel.networkState.observe(viewLifecycleOwner, {
binding.progressBar.isVisible = if (it==MovieApiStatus.LOADING) true else view.isGone
binding.buttonRetry.isVisible = if(it==MovieApiStatus.ERROR) true else view.isGone
binding.errorTextView.isVisible = if(it==MovieApiStatus.ERROR) true else view.isGone
binding.recyclerView.isVisible = if(it==MovieApiStatus.DONE) true else view.isGone
binding.noResultsText.isVisible = false
})
//Display trending movies
//loadstate is of type combined loadstates, which combines the loadstate of different scenarios(when we refresh dataset or when we append new data to it) into this one object
//We can use it to check for these scenarios and make our views visible or unvisible according to it
setHasOptionsMenu(true)
}
override fun onItemClick(movie: MoviesResults.Movies) {
val action = MoviesListFragmentDirections.actionMoviesListFragmentToMoviesDetailsFragment(movie)
findNavController().navigate(action)
}
override fun onFavoriteClick(favorites: MoviesFavorites) {
daoViewModel.addMovieToFavs(favorites)
}
override fun onDeleteClick(favorites: MoviesFavorites) {
daoViewModel.deleteMovieFromFavs(favorites)
}
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
super.onCreateOptionsMenu(menu, inflater)
// Inflate the gallery menu
inflater.inflate(R.menu.menu_gallery, menu)
}
override fun onDestroyView() {
super.onDestroyView()
_binding = null
}
}
MoviesListAdapter.kt
package com.example.moviesapp.ui
import android.view.LayoutInflater
import android.view.ViewGroup
import android.widget.Toast
import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.ListAdapter
import androidx.recyclerview.widget.RecyclerView
import com.bumptech.glide.Glide
import com.example.moviesapp.R
import com.example.moviesapp.databinding.MovieLayoutBinding
import com.example.moviesapp.network.MoviesFavorites
import com.example.moviesapp.network.MoviesResults
val IMAGE_BASE_URL = "https://image.tmdb.org/t/p/w500"
class MoviesListAdapter constructor(private val listener: OnItemClickListener) :
ListAdapter<MoviesResults.Movies, MoviesListAdapter.MoviesListViewHolder>(DiffCallback) {
private lateinit var fav: MoviesFavorites
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MoviesListViewHolder {
val binding = MovieLayoutBinding.inflate(LayoutInflater.from(parent.context), parent, false)
return MoviesListViewHolder(binding)
}
override fun onBindViewHolder(holder: MoviesListViewHolder, position: Int) {
val currentItem = getItem(position)
holder.binding.favoritesCheckbox.isChecked = currentItem.isFavorite
holder.binding.favoritesCheckbox.setOnCheckedChangeListener { _, isChecked ->
currentItem.isFavorite
}
if(holder.binding.favoritesCheckbox.isChecked ) {
currentItem.isFavorite = true
}
if (currentItem != null) {
holder.bind(currentItem)
}
}
inner class MoviesListViewHolder(val binding: MovieLayoutBinding) :
RecyclerView.ViewHolder(binding.root) {
init {
binding.root.setOnClickListener {
val position = absoluteAdapterPosition
if (position != RecyclerView.NO_POSITION) {
val item = getItem(position)
listener.onItemClick(item)
}
}
}
init {
binding.favoritesCheckbox.setOnClickListener{
if(binding.favoritesCheckbox.isChecked) {
val position = absoluteAdapterPosition
if (position != RecyclerView.NO_POSITION) {
val item = getItem(position)
item.isFavorite = true
fav = MoviesFavorites(item.title, item.id, item.release_date, item.overview, item.vote_average, item.poster_path, item.original_language, item.isFavorite)
listener.onFavoriteClick(fav)
listener.onCheckboxClick(binding.favoritesCheckbox.isChecked)
}
showToast("${fav.title} is added to your favorites")
}
else {
val position = absoluteAdapterPosition
if (position != RecyclerView.NO_POSITION) {
val item = getItem(position)
item.isFavorite = false
fav = MoviesFavorites(item.title, item.id, item.release_date, item.overview, item.vote_average, item.poster_path, item.original_language, item.isFavorite)
listener.onDeleteClick(fav)
listener.onCheckboxClick(binding.favoritesCheckbox.isChecked)
}
showToast("${fav.title} is removed from your favorites")
}
}
}
fun bind(movie: MoviesResults.Movies) {
binding.apply {
movieTitle.text = movie.title
movieRating.text = movie.vote_average
movieYear.text = movie.release_date
Glide.with(itemView)
.load(IMAGE_BASE_URL + movie.poster_path)
.centerCrop()
.error(R.drawable.ic_baseline_error_outline_24)
.into(movieImage)
val item = getItem(absoluteAdapterPosition)
favoritesCheckbox.isChecked = item.isFavorite
}
}
private fun showToast(string: String) {
Toast.makeText(itemView.context, string, Toast.LENGTH_SHORT).show()
}
}
interface OnItemClickListener {
fun onItemClick(movie: MoviesResults.Movies)
fun onFavoriteClick(favorites: MoviesFavorites)
fun onDeleteClick(favorites: MoviesFavorites)
fun onCheckboxClick(fav: Boolean)
}
companion object DiffCallback : DiffUtil.ItemCallback<MoviesResults.Movies>() {
override fun areItemsTheSame(
oldItem: MoviesResults.Movies,
newItem: MoviesResults.Movies
): Boolean {
return oldItem.id == newItem.id
}
override fun areContentsTheSame(
oldItem: MoviesResults.Movies,
newItem: MoviesResults.Movies
): Boolean {
return oldItem == newItem
}
}
}
MoviesDetailsFragment.kt
package com.example.moviesapp.ui.Fragments
import android.os.Bundle
import android.view.View
import android.widget.Toast
import androidx.fragment.app.Fragment
import androidx.fragment.app.viewModels
import androidx.navigation.fragment.navArgs
import com.bumptech.glide.Glide
import com.example.moviesapp.R
import com.example.moviesapp.databinding.FragmentMoviesDetailsBinding
import com.example.moviesapp.network.MoviesFavorites
import com.example.moviesapp.network.MoviesResults
import com.example.moviesapp.ui.DaoViewModel
import com.example.moviesapp.ui.IMAGE_BASE_URL
import com.example.moviesapp.ui.SharedViewModel
import dagger.hilt.android.AndroidEntryPoint
#AndroidEntryPoint
class MoviesDetailsFragment() : Fragment(R.layout.fragment_movies_details) {
//We can get the movies from the args property
private val args by navArgs<MoviesDetailsFragmentArgs>()
private val daoViewModel by viewModels<DaoViewModel>()
private val sharedViewModel by viewModels<SharedViewModel>()
private fun showToast(string: String) {
Toast.makeText(view?.context, string, Toast.LENGTH_SHORT).show()
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
val binding = FragmentMoviesDetailsBinding.bind(view)
sharedViewModel.checkBox.observe(viewLifecycleOwner) {
binding.favCheckbox.isChecked = it
}
binding.apply {
val movie: MoviesResults.Movies = args.movie
val fav = MoviesFavorites(
movie.title,
movie.id,
movie.release_date,
movie.overview,
movie.vote_average,
movie.poster_path,
movie.original_language,
movie.isFavorite,
)
//When you are in fragment/activity, pass it to a glide.with because view is less efficient
Glide.with(this#MoviesDetailsFragment)
.load(IMAGE_BASE_URL + movie.poster_path)
//Have the textview visible only when image is visible
.error(R.drawable.ic_baseline_error_outline_24)
.fitCenter()
.into(coverPhoto)
title.text = movie.title
releaseDate.text = movie.release_date
language.text = movie.original_language
rating.text = movie.vote_average
plot.text = movie.overview
favCheckbox.setOnClickListener {
if (favCheckbox.isChecked) {
fav.isFavorite = true
daoViewModel.addMovieToFavs(fav)
showToast("${fav.title} is added to your favorites")
} else {
fav.isFavorite = false
daoViewModel.deleteMovieFromFavs(fav)
showToast("${fav.title} is removed from your favorites")
}
}
}
}
}
SharedViewModel.kt
package com.example.moviesapp.ui
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
class SharedViewModel: ViewModel() {
val checkBox = MutableLiveData<Boolean>()
fun sendValue(favorite: Boolean) {
checkBox.value = favorite
}
class SharedViewModelFactor(
) : ViewModelProvider.Factory {
override fun <T : ViewModel?> create(modelClass: Class<T>): T {
if (modelClass.isAssignableFrom(SharedViewModel::class.java)) {
#Suppress("UNCHECKED_CAST")
return SharedViewModel() as T
}
throw IllegalArgumentException("Unknown ViewModel class")
}
}
}
You can user 2 methods to do so:
1 ) You can use LocalBroadcast to notify one fragment/activity of change in another.
Note: LocalBroadcast in now deprecated. Alternatively you can use eventbus to communication between fragments
Create a local Broadcast
private BroadcastReceiver onNotice= new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
// intent can contain anydata
Log.d(TAG,"onReceive called");
}
};
Register your receiver in onResume of fragment like:
public void onResume() {
super.onResume();
IntentFilter iff= new IntentFilter(MyIntentService.ACTION);
LocalBroadcastManager.getInstance(this).registerReceiver(onNotice, iff);
}
unRegister receiver in onPause:
public void onPause() {
super.onPause();
LocalBroadcastManager.getInstance(this).unregisterReceiver(onNotice);
}
For more information can you can refer to:
https://blog.mindorks.com/using-localbroadcastmanager-in-android
2 ) You can use LiveData to observe data changes of one fragment in another
Create shared ViewModel
public class SharedViewModel extends ViewModel {
private MutableLiveData<String> name;
public void setNameData(String nameData) {
name.setValue(nameData);
}
public MutableLiveData<String> getNameData() {
if (name == null) {
name = new MutableLiveData<>();
}
return name;
}
}
Fragment One
private SharedViewModel sharedViewModel;
public FragmentOne() {
}
#Override
public void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
sharedViewModel = new ViewModelProvider(requireActivity()).get(SharedViewModel.class);
submitButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
sharedViewModel.setNameData(submitText.getText().toString());
}
});
}
Fragment Two
private SharedViewModel sharedViewModel;
public FragmentTwo() {
}
#Override
public void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
sharedViewModel = new ViewModelProvider(requireActivity()).get(SharedViewModel.class);
sharedViewModel.getNameData().observe(this, nameObserver);
}
Observer<String> nameObserver = new Observer<String>() {
#Override
public void onChanged(String name) {
receivedText.setText(name);
}
};
For more details on viewmodel you can refer to :
https://nabeelj.medium.com/android-how-to-share-data-between-fragments-using-viewmodel-and-livedata-android-mvvm-9fc463af5152
https://developer.android.com/guide/fragments/communicate#fragments
I want to write a hashmap into my firebase using the click listener which I have implemented in the fragment which acts only if one of the items is selected. but I get an error that I haven't got anytime before.
The logcat shows:
java.lang.IndexOutOfBoundsException: Index: 2, Size: 2
at java.util.ArrayList.get(ArrayList.java:437)
at com.reazon.foodrunner.adapter.MenuRecyclerAdapter$MenuViewHolder$bind$1$1.onDataChange(MenuRecyclerAdapter.kt:104)
at com.google.firebase.database.core.ValueEventRegistration.fireEvent(ValueEventRegistration.java:75)
at com.google.firebase.database.core.view.DataEvent.fire(DataEvent.java:63)
at com.google.firebase.database.core.view.EventRaiser$1.run(EventRaiser.java:55)
at android.os.Handler.handleCallback(Handler.java:873)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loop(Looper.java:193)
at android.app.ActivityThread.main(ActivityThread.java:6669)
this is my adapter :
package com.reazon.foodrunner.adapter
import android.content.Context
import android.content.Intent
import android.os.Bundle
import android.util.Log
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.Button
import android.widget.RelativeLayout
import android.widget.TextView
import android.widget.Toast
import androidx.recyclerview.widget.RecyclerView
import com.google.firebase.auth.FirebaseAuth
import com.google.firebase.database.*
import com.reazon.foodrunner.R
import com.reazon.foodrunner.fragment.CartFragment
import com.reazon.foodrunner.model.MenuItem
class MenuRecyclerAdapter(
val context: Context,
private val btnProceedToCart: Button,
private val proceedToCartPassed: RelativeLayout,
private val menuList: List<MenuItem>,
private val listener: ContentListener
) : RecyclerView.Adapter<MenuRecyclerAdapter.MenuViewHolder>() {
private var goToCart: Int = 0
private var itemSelected: Int = 0
private var itemNumber: Int = 0
private var itemSelectedId = arrayListOf<String>()
private var itemSelectedName = arrayListOf<String>()
lateinit var proceedToCartLayout: RelativeLayout
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MenuViewHolder {
val view = LayoutInflater.from(parent.context)
.inflate(R.layout.recycler_menu_single_row, parent, false)
return MenuViewHolder(view)
}
override fun getItemCount(): Int {
return menuList.size
}
override fun onBindViewHolder(holder: MenuViewHolder, position: Int) {
val menuItemItem = menuList[position]
holder.menuItemName.text = menuItemItem.item.toString()
proceedToCartLayout = proceedToCartPassed
btnProceedToCart.setOnClickListener {
Toast.makeText(context, "Proceeding To Cart", Toast.LENGTH_SHORT).show()
}
holder.btnMenuItem.setOnClickListener {
if (holder.btnMenuItem.text.toString() == "Remove") {
itemSelected--
holder.btnMenuItem.text = "ADD"
itemSelectedId.remove(holder.btnMenuItem.toString())
itemSelectedName.remove(holder.menuItemName.toString())
holder.btnMenuItem.setBackgroundResource(R.drawable.btn_menu_add_background)
} else {
itemSelected++
holder.btnMenuItem.text = "Remove"
itemSelectedId.add(holder.btnMenuItem.toString())
itemSelectedName.add(holder.menuItemName.toString())
holder.btnMenuItem.setBackgroundResource(R.drawable.btn_menu_remove_background)
}
if (itemSelected > 0) {
proceedToCartLayout.visibility = View.VISIBLE
} else {
proceedToCartLayout.visibility = View.INVISIBLE
}
print("$goToCart")
holder.btnMenuItem.tag = menuItemItem.id
itemNumber = position + 1
Log.d("Restaurant", "" + holder.menuItemName.text)
}
holder.btnMenuItem.tag = menuItemItem.id.toString() + ""
holder.bind(itemSelectedId, itemSelectedName, btnProceedToCart, listener)
}
class MenuViewHolder(view: View) : RecyclerView.ViewHolder(view) {
fun bind(menuSelectedId: List<String>, menuList: List<String>, btnProceedToCart: Button, listener: ContentListener) {
btnProceedToCart.setOnClickListener {
val mReference = FirebaseDatabase.getInstance().reference.child("Orders")
.child(FirebaseAuth.getInstance().currentUser!!.uid)
mReference.addValueEventListener(object : ValueEventListener {
override fun onDataChange(snapshot: DataSnapshot) {
val orderHashMap = HashMap<String, Any>()
for (item in 1..menuList.size) {
orderHashMap["id"] = menuSelectedId[item]
orderHashMap["item"] = menuList[item]
orderHashMap["cost"] = 200
orderHashMap["placed"] = true
}
mReference.updateChildren(orderHashMap)
}
override fun onCancelled(error: DatabaseError) {
Log.d("MenuRecyclerAdapter", error.message)
}
})
listener.openCart()
}
}
val menuItemName: TextView = view.findViewById(R.id.menuItemName)
val btnMenuItem: Button = view.findViewById(R.id.btnMenuAdd)
}
interface ContentListener {
fun openCart()
}
}
this is my menu fragment:
package com.reazon.foodrunner.fragment
import android.os.Bundle
import android.util.Log
import androidx.fragment.app.Fragment
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.Button
import android.widget.RelativeLayout
import androidx.core.os.bundleOf
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.google.firebase.database.*
import com.reazon.foodrunner.R
import com.reazon.foodrunner.adapter.MenuRecyclerAdapter
import com.reazon.foodrunner.model.MenuItem
import com.reazon.foodrunner.model.Restaurant
import kotlinx.android.synthetic.main.fragment_menu.*
import kotlinx.android.synthetic.main.recycler_home_single_row.*
import kotlinx.android.synthetic.main.recycler_menu_single_row.*
class MenuFragment : Fragment(),MenuRecyclerAdapter.ContentListener{
private var menuRecyclerAdapter: MenuRecyclerAdapter? = null
private lateinit var menuReference: DatabaseReference
private var recyclerMenu: RecyclerView? = null
internal var menuItemList: MutableList<MenuItem> = ArrayList()
private var layoutManager: RecyclerView.LayoutManager? = null
private lateinit var proceedToCartLayout:RelativeLayout
private lateinit var btnProceedToCart: Button
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
// Inflate the layout for this fragment
val view = inflater.inflate(R.layout.fragment_menu, container, false)
menuReference = FirebaseDatabase.getInstance().reference.child("Menu")
recyclerMenu = view.findViewById(R.id.recyclerMenu)
btnProceedToCart = view.findViewById(R.id.btnGoToCart)
proceedToCartLayout = view.findViewById(R.id.rlProceedToCart)
// val restaurant = (Restaurant::class.java)
layoutManager = LinearLayoutManager(activity)
recyclerMenu!!.layoutManager = layoutManager
retrieveMenu()
return view
}
private fun retrieveMenu() {
menuItemList.clear()
menuReference.addValueEventListener(object : ValueEventListener {
override fun onCancelled(error: DatabaseError) {
Log.d("ERROR", "" + error.message)
}
override fun onDataChange(snapshot: DataSnapshot) {
for (p0 in snapshot.children) {
val menu = p0.getValue(MenuItem::class.java)
menuRecyclerAdapter = MenuRecyclerAdapter(context!!, btnProceedToCart, proceedToCartLayout,menuItemList,this#MenuFragment)//,this#MenuFragment)
recyclerMenu!!.adapter = menuRecyclerAdapter
if (menu != null) {
menuItemList.add(menu)
}
}
}
})
}
fun onBackPressed() {
when (this.fragmentManager?.findFragmentById(R.id.frame)) {
!is HomeFragment -> openHome()
}
}
private fun openHome() {
val transaction = fragmentManager?.beginTransaction()
transaction?.replace(R.id.frame, HomeFragment())
transaction?.commit()
}
override fun openCart() {
val transaction = fragmentManager?.beginTransaction()
transaction?.replace(R.id.frame, CartFragment())
transaction?.commit()
}
}
although I haven't made the cart fragment and recycler properly, this is my cart fragment:
package com.reazon.foodrunner.fragment
import android.content.Intent
import android.os.Bundle
import androidx.fragment.app.Fragment
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.Button
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.google.firebase.auth.FirebaseAuth
import com.google.firebase.database.DatabaseReference
import com.google.firebase.database.FirebaseDatabase
import com.reazon.foodrunner.R
import com.reazon.foodrunner.activity.FinishActivity
import com.reazon.foodrunner.adapter.CartRecyclerAdapter
import com.reazon.foodrunner.model.Order
import kotlinx.android.synthetic.main.fragment_cart.*
class CartFragment : Fragment() {
lateinit var btnCheckout:Button
private var recyclerCart:RecyclerView? = null
private var recyclerAdapter:CartRecyclerAdapter? = null
private var layoutManager:RecyclerView.LayoutManager? = null
private var orderList:MutableList<Order> = ArrayList()
private var cartDatabaseReference:DatabaseReference? = null
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
// Inflate the layout for this fragment
val view = inflater.inflate(R.layout.fragment_cart, container, false)
btnCheckout = view.findViewById(R.id.btnCheckout)
val firebaseUid = FirebaseAuth.getInstance().currentUser!!.uid
cartDatabaseReference = FirebaseDatabase.getInstance().reference.child("Orders").child(firebaseUid)
layoutManager =LinearLayoutManager(activity)
recyclerCart = view.findViewById(R.id.recyclerCart)
recyclerCart!!.layoutManager = layoutManager
btnCheckout.setOnClickListener {
val intent = Intent(activity, FinishActivity::class.java )
startActivity(intent)
}
return view
}
}
Cart adapter:
package com.reazon.foodrunner.adapter
import android.content.Context
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.TextView
import androidx.recyclerview.widget.RecyclerView
import com.reazon.foodrunner.R
import com.reazon.foodrunner.model.Order
import kotlinx.android.synthetic.*
import kotlinx.android.synthetic.main.recycler_cart_single_row.view.*
import java.util.function.ToDoubleBiFunction
class CartRecyclerAdapter(
val context: Context,
private val orderList:List<Order>
):RecyclerView.Adapter<CartRecyclerAdapter.CartViewHolder>() {
class CartViewHolder(view: View):RecyclerView.ViewHolder(view){
val txtItemName:TextView = view.findViewById(R.id.txtItemNameCart)
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): CartViewHolder {
val view = LayoutInflater.from(parent.context).inflate(R.layout.recycler_cart_single_row,parent,false)
return CartViewHolder(view)
}
override fun onBindViewHolder(holder: CartViewHolder, position: Int) {
val order = orderList[position]
holder.txtItemName.text = order.item
}
override fun getItemCount(): Int {
return orderList.size
}
}
I am unable to understand what to do here to simply upload the hash map of orders and retrieve them.
it was a simple error as I was using the index (indices) in the wrong manner as it should have been
for (item in menuList.indices) {
orderHashMap["id"] = menuSelectedId[item]
orderHashMap["item"] = menuList[item]
orderHashMap["cost"] = 200
orderHashMap["placed"] = true
}
and the list got the correct index of each element thereafter, and hashmap was written to firebase.