Objective
I want to expand new items in a RecyclerView at the same time, and the old selected items will be automatically collapsed.
What I done, it can be like this >>
The items expand and collapse on click but do not automatically collapse if another item is expanded
image1
Actually, what I want to make it like this >>
Any expanded item should be automatically collapse when another item is expanded.
image2
What I had researched
From this stack overflow had done what I want, but I don't how to convert into Kotlin, basically as I know it store the previous position then compare to the current position, through the If-Else Statement to determine and perform the action.
There are one more stack overflow, it also from java, slightly understand, briefly know the whole concept, but cannot go deeply explain for myself line-by-line.
This Github Sample is pretty matched my needs, but code styling quite complicated, and not fexible enough for me to add other feature and design.
Question
Why the previousPosition need to be -1?
How should separate currentPosition and previousPosition, and store the previousPosition to different object and value?
If the previousPosition is workable, how should I implement into my project?
If available, can share any related resources to me?
like: screen shot the part of sample code, or other resources
Code
product_list_item.kt
import android.content.Intent
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.widget.Toast
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.android.volley.Request
import com.android.volley.RequestQueue
import com.android.volley.toolbox.JsonObjectRequest
import com.android.volley.toolbox.Volley
import com.google.android.material.floatingactionbutton.FloatingActionButton
import org.json.JSONArray
import org.json.JSONObject
class ViewPartActivity : AppCompatActivity() {
private lateinit var newRecylerview : RecyclerView
private lateinit var newArrayList : ArrayList<Product>
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_view_part)
val btnInsertView: FloatingActionButton = findViewById(R.id.btnInsertView)
btnInsertView.setOnClickListener {
val insertViewIntent = Intent(this, AddPartActivity::class.java)
startActivity(insertViewIntent)
}
getData()
}
private fun getData() {
val url = "WEBAPI"
val requestQueue: RequestQueue = Volley.newRequestQueue(this)
val jsonObjectRequest: JsonObjectRequest = JsonObjectRequest(Request.Method.GET, url, null,
{ response ->
val jsonArray: JSONArray = response.getJSONArray("tbl_product")
val namelist = ArrayList<String>()
val categorylist = ArrayList<String>()
val quantitylist = ArrayList<String>()
val pricelist = ArrayList<String>()
for (x in 0 until jsonArray.length()) {
val jsonObject: JSONObject = jsonArray.getJSONObject(x)
val name: String = jsonObject.getString("NAME")
val category: String = jsonObject.getString("CATOGORYID")
val quantity: String = jsonObject.getString("QUANTITY")
val price: String = jsonObject.getString("PRICE")
namelist.add(name)
categorylist.add(category)
quantitylist.add(quantity)
pricelist.add(price)
}
newRecylerview =findViewById(R.id.recyclerView)
newRecylerview.layoutManager = LinearLayoutManager(this)
newRecylerview.setHasFixedSize(true)
newArrayList = arrayListOf<Product>()
for(i in namelist.indices){
val product = Product(namelist[i],categorylist[i],quantitylist[i],pricelist[i])
newArrayList.add(product)
}
val adapter = ProductAdapter(newArrayList)
newRecylerview.adapter = adapter
}, { error ->
Toast.makeText(this, error.message, Toast.LENGTH_LONG).show()
})
requestQueue.add(jsonObjectRequest)
}
}
ProductAdapter.kt
class ProductAdapter(private val productList : ArrayList<Product>) : RecyclerView.Adapter<ProductAdapter.MyViewHolder>() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyViewHolder {
val itemView = LayoutInflater.from(parent.context).inflate(R.layout.product_list_item, parent,false)
return MyViewHolder(itemView)
}
#SuppressLint("SetTextI18n")
override fun onBindViewHolder(holder: MyViewHolder, position: Int) {
val currentItem = productList[position]
holder.tvProductName.text = currentItem.name
holder.tvProductCategory.text= currentItem.category
holder.tvProductQuantity.text=currentItem.quantity
holder.tvProductPrice.text= "RM "+currentItem.price
val isVisible : Boolean = currentItem.visibility
holder.constraintLayout.visibility = if (isVisible) View.VISIBLE else View.GONE
holder.tvProductName.setOnClickListener{
currentItem.visibility =!currentItem.visibility
notifyItemChanged(position)
}
}
override fun getItemCount(): Int {
return productList.size
}
class MyViewHolder(itemView : View) : RecyclerView.ViewHolder(itemView) {
val tvProductName: TextView = itemView.findViewById(R.id.productName)
val tvProductCategory : TextView = itemView.findViewById(R.id.productCategory)
val tvProductQuantity : TextView = itemView.findViewById(R.id.productQuantity)
val tvProductPrice : TextView = itemView.findViewById(R.id.productPrice)
val constraintLayout : ConstraintLayout = itemView.findViewById(R.id.expandedLayout)
}
}
Product.kt
data class Product(
var name: String, var category: String, var quantity: String, var price: String, var visibility: Boolean = false
)
Promise
Once I get the solution, I would try my best to explain the code more deeply and update at here.
Related
Based on the logs I added in, the request is receiving all the data correctly, and the issues lies somewhere in displaying the list in the recycler list. It just prints out the last item in the your server pets array and prints its according to the length of the database array. My leaderboard seems to only been showing the last entry into the database and repeating it for the pets array length. I would like to show the entire database items in each slot. Also, I am not sure why petPhoto is not being passed through like everything other thing.
***************************************ListAcitivty.kt:****************************************
package com.example.specialpets
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.util.Log
import android.widget.TextView
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.android.volley.Request
import com.android.volley.toolbox.JsonObjectRequest
import com.android.volley.toolbox.Volley
import org.json.JSONArray
import org.json.JSONObject
class ListActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_list)
var findArray: JSONArray
val url = "https://jwuclasses.com/ugly/leaderboard"
val jsonData=JSONObject();
val queue = Volley.newRequestQueue(this);
val request = JsonObjectRequest(Request.Method.POST, url, jsonData,
{ response ->
if (response.getInt("success") == 1) {
Log.e("app", "We have something good going on here");
findArray = response.getJSONArray("pets")
for (i in 0 until findArray.length()) {
val pet = findArray.getJSONObject(i)
val petListID = pet.getInt("id")
val petName = pet.getString("name")
val petBirthdate = pet.getString("birthdate")
val petRating = pet.getInt("rating")
val petVotes = pet.getDouble("votes")
val petPhoto = pet.getString("url")
jsonData.put("token",TokenBox.token)
//pay no attention to the man behind the curtain
//val petName = response.getString("name")
// val petListID = response.getInt("id")
//val petRating = response.getInt("rating")
// val petBirthdate = response.getString("birthdate")
// val petVotes = response.getInt("votes")
val data = ArrayList<petViewModel>()
//data.sortDescending(); Why won't this work??? Maybe because it has to be in the declaration of the array
for(i in 0 until findArray.length()) {
data.add(
petViewModel(
petListID,
petName,
petBirthdate,
petRating,
petVotes,
petPhoto
)
)
val recyclerView = findViewById<RecyclerView>(R.id.recyclerView)
recyclerView.layoutManager = LinearLayoutManager(this)
val adapter = petAdapter(data)
recyclerView.adapter = adapter
}
}
} else {
Log.e("app", "something isn't right here")
}
},{
error ->
Log.e("app", "Request did not go through and no JSON data was sent")
})
request.setShouldCache(false);
queue.add(request);
//val recyclerView = findViewById<RecyclerView>(R.id.recyclerView)
//val data = ArrayList<petViewModel>()
// for(i in 1..20){}
//****These are all my attempts at passing the data through a jsonObject request....
// data.add(petViewModel("petListID", "petName", "petBirthdate", "petRating", "petVotes",)
//take it from JSON, store it in a local array and store it
//data.add(petViewModel("Item" + i, i))
// data.add(petViewModel((response.getString("id"), response.getString("name"), response.getString("birthdate"), response.getString("rating"), response.getString("votes"))
//data.add(petViewModel(2, 2, 3, 10))
// data.add(petViewModel(response.getInt,"id", response.getString,"birthdate", response.getString""
//data.add(petViewModel(reponse.getString,"name", response.getString,"birthdate", response.getString,"rating")
//data.add(petViewModel("id", "", "", "", ""))
//data.add(petViewModel((response.getString"")))
//data.add(petViewModel("id", "name", "rating", "birthdate", "votes"))
//}
}
}
******************************************PetViewModel:****************************************
package com.example.specialpets
//class designed to hold data
data class petViewModel(val id: Int, val name: String, val birthdate: String, val rating: Int, val votes: Double, val url: String){
//id name birthdate rating votes url(picasso image)
}
******************************************PetAdapterModel:****************************************
package com.example.specialpets
import android.content.Intent
import android.util.Log
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.TextView
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.android.volley.Request
import com.android.volley.toolbox.JsonObjectRequest
import com.android.volley.toolbox.Volley
import com.squareup.picasso.Picasso
import org.json.JSONArray
import org.json.JSONObject
//an array of petviewmodel instances
class petAdapter(val dataset: ArrayList<petViewModel>): RecyclerView.Adapter<petAdapter.ViewHolder>() {
//creates a new item or a new card for a pet
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
val view = LayoutInflater.from(parent.context).inflate(R.layout.petcard, parent, false)
return ViewHolder(view)
}
//puts the position. specific information
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
val petViewModel = dataset[position]
holder.petListID.text = petViewModel.id.toString()
holder.petName.text = petViewModel.name
holder.petBirthdate.text = petViewModel.birthdate
holder.petRating.text = petViewModel.rating.toString()
holder.petVotes.text = petViewModel.votes.toString()
//picassoimg
holder.petListID.setOnClickListener{
//grab id from data object which is inside the petviewmodel
val zoomies = Intent(holder.petListID.context,ZoomerActivity::class.java)
//store with the one click one
zoomies.putExtra("ID", petViewModel.id.toString())
zoomies.putExtra("name", petViewModel.name)
zoomies.putExtra("birthdate", petViewModel.birthdate)
zoomies.putExtra("rating", petViewModel.rating.toString())
zoomies.putExtra("votes", petViewModel.votes.toString())
zoomies.putExtra("url", petViewModel.url)
//start the activity
holder.petListID.context.startActivity(zoomies)
}
}
//tells me how many items are in the list
override fun getItemCount(): Int {
return dataset.size
}
class ViewHolder(ItemView: View) : RecyclerView.ViewHolder(ItemView){
val petListID: TextView = ItemView.findViewById(R.id.petListID)
val petName: TextView = ItemView.findViewById(R.id.petName)
val petBirthdate: TextView = ItemView.findViewById(R.id.petBirthdate)
val petRating: TextView = ItemView.findViewById(R.id.petRating)
val petVotes: TextView = ItemView.findViewById(R.id.petVotes)
}
}
I would like it to have every item instead of just the last item in the JSON array.I have tried changing the for loop to a forEach loop with no changes. I have tried altering the data class: dataset with no changes as well.
this is what the app recyler view looks like
You can try following this:
class YourAdapterName(
private val context: Context,
private val dataset: ArrayList<petViewModel>
) :
RecyclerView.Adapter<HistoryAdapter.ViewHolder>() {
inner class ViewHolder(view: View) : RecyclerView.ViewHolder(view) {
val yourview: TextView = view.findViewById(R.id.historyInput)
// and your other views
init {
item.setOnClickListener {
// here is what you want do when an item is clicked
}
}
}
override fun onCreateViewHolder(viewGroup: ViewGroup, viewType: Int): ViewHolder {
val v = LayoutInflater.from(viewGroup.context)
.inflate(R.layout.yourRecyclerViewLayoutShouldBeHere, viewGroup, false)
return ViewHolder(v)
}
override fun onBindViewHolder(viewHolder: ViewHolder, position: Int) {
val petViewModel = dataset[position]
viewHolder.yourview.text = petViewModel.name
// and your other views here
}
// Return the size of your ids
override fun getItemCount() = dataset.size
}
It should be all you need.
hi i want parse my data in to new activity .
I did this but it doesn't work properly and it doesn't transfer data
You can see my code below
my adapter
ItemViewAdapter.kt
package com.example.app.adapter
import android.content.Context
import android.content.Intent
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.TextView
import androidx.recyclerview.widget.RecyclerView
import com.example.app.R
import com.example.app.models.products.Results
import com.example.app.ui.home.DescriptionActivity
import java.security.AccessController.getContext
class ItemViewAdapter(private val context: Context) :
RecyclerView.Adapter<ItemViewAdapter.MyViewHolder>() {
private var itemList: MutableList<Results> = mutableListOf()
private var page: Int = 0
fun setListItem(item: List<Results>) {
itemList.addAll(item)
notifyItemRangeInserted(page * 10, (page.plus(1) * 10) - 1)
}
class MyViewHolder(view: View) : RecyclerView.ViewHolder(view) {
private val title = view.findViewById<TextView>(R.id.tvTitle)
private val code = view.findViewById<TextView>(R.id.tvNumber)
private val quantity = view.findViewById<TextView>(R.id.tvQuantity)
private val price = view.findViewById<TextView>(R.id.tvPrice)
private val available = view.findViewById<TextView>(R.id.tvAvailable)
private val category = view.findViewById<TextView>(R.id.tvCategory)
fun bind(item: Results) {
title.text = item.name
code.text = item.code.toString()
quantity.text = item.quantities[0].quantity.toString()
price.text = item.prices[0].price.toString()
available.text = item.active.toString()
category.text = item.category?.name
}
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyViewHolder {
val v = LayoutInflater.from(parent.context).inflate(R.layout.list_item, parent, false)
return MyViewHolder(v)
}
override fun getItemCount(): Int {
return itemList.size
}
override fun getItemId(position: Int): Long {
return position.toLong()
}
override fun onBindViewHolder(holder: MyViewHolder, position: Int) {
val data = itemList[position]
holder.bind(data)
holder.itemView.setOnClickListener {
val intent = Intent(context, DescriptionActivity::class.java)
intent.putExtra("title", getItemId(position))
intent.putExtra("code", getItemId(position))
intent.putExtra("quantity", getItemId(position))
intent.putExtra("price", getItemId(position))
intent.putExtra("category", getItemId(position))
intent.putExtra("info", getItemId(position))
context.startActivity(intent)
}
}
}
and my description activity
Description.kt
package com.example.app.ui.home
import android.graphics.Color
import android.os.Build
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.view.View
import android.view.WindowInsetsController
import android.view.WindowManager
import com.example.app.R
import com.example.app.databinding.ActivityDescriptionBinding
#Suppress("DEPRECATION")
class DescriptionActivity : AppCompatActivity() {
private lateinit var binding: ActivityDescriptionBinding
override fun onCreate(savedInstanceState: Bundle?) {
window.apply {
clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS)
addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS)
decorView.systemUiVisibility = View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
statusBarColor = Color.TRANSPARENT
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
this#DescriptionActivity.window.decorView.windowInsetsController
?.setSystemBarsAppearance(
0,
WindowInsetsController.APPEARANCE_LIGHT_STATUS_BARS
)
}
}
}
super.onCreate(savedInstanceState)
binding = ActivityDescriptionBinding.inflate(layoutInflater)
val view = binding.root
setContentView(view)
getItemIntent()
}
private fun getItemIntent(){
val title = intent.getStringExtra("title")
val code = intent.getStringExtra("code")
val category = intent.getStringExtra("category")
val info = intent.getStringExtra("info")
val price = intent.getStringExtra("price")
val quantity = intent.getStringExtra("quantity")
setData(title,code,category,info,price,quantity)
}
private fun setData(title: String?, code: String?, category: String?, info: String?, price: String?, quantity: String?) {
binding.tvTitle.setText(title)
binding.tvCode.setText(code)
binding.tvCategory.setText(category)
binding.tvDescription.setText(info)
binding.tvPrice.setText(price)
binding.tvQuantity.setText(quantity)
}
}
But the data is not transferred.
If anyone can help me improve the code or offer me a sample code done with api so I can see.
When you set the data, you call getItemId(position) for every single line. This returns a long, not a string (and you put the same long for each attribute, which makes no sense)
intent.putExtra("title", getItemId(position))
intent.putExtra("code", getItemId(position))
but when you retrieve it, you look for a string
val title = intent.getStringExtra("title")
val code = intent.getStringExtra("code")
If you want to get a string, you need to put a string, probably like this (get the item for that row, then put its string attributes):
val data = itemList[position]
//...
intent.putExtra("title", data.name)
intent.putExtra("code", data.code.toString())
or if you want to get the text off the displayed views (have to make them non-private in the binding class):
intent.putExtra("title", binding.title.text.toString())
intent.putExtra("code", binding.code.text.toString())
If you need to pass the entire object or most of its properties, you can add the library
implementation 'com.google.code.gson:gson:2.8.5' //Maybe the version is not the most recent, I just copied and pasted the implementation from the internet
And convert your object to a Json and pass it as a string
intent.putExtra("data", Gson().toJson(data))
and to receive it
val data = intent.getStringExtra("data", null)?.let {
Gson().fromJson(it, Results::Java.class)
}
I have a RecyclerView that gets information from FirebaseFirestore, I need that when I click on an item I change the activity and add a putExtra. I try to call an Intent inside the Adapter of my RecyclerView but I get the error 'Function declaration must have a name' and 'Expecting member declaration'.
How can I change the Activity and add a putExtra by clicking on an Item in the RecyclerView?
this is MyAdapter
package com.example.atipicoapp
import android.content.Intent
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.AdapterView
import android.widget.TextView
import android.widget.Toast
import androidx.appcompat.view.menu.ActionMenuItemView
import androidx.core.content.ContextCompat.startActivity
import androidx.recyclerview.widget.RecyclerView
import kotlinx.android.synthetic.main.activity_main.*
import kotlinx.android.synthetic.main.list_item.view.*
class MyAdapter(private val platoList : ArrayList<Plato>,
private val itemClickListener: OnPlatoClickListener
) : RecyclerView.Adapter<MyAdapter.MyViewHolder>() {
interface OnPlatoClickListener{
fun onItemClick(nombre: String)
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyAdapter.MyViewHolder {
val itemView = LayoutInflater.from(parent.context).inflate(R.layout.list_item, parent, false)
itemView.platoTouch.setOnClickListener(View.OnClickListener { v: View ->
})
return MyViewHolder(itemView)
}
override fun onBindViewHolder(holder: MyAdapter.MyViewHolder, position: Int) {
val plato : Plato = platoList[position]
holder.platoName.text = plato.platoName
holder.platoDescription.text = plato.platoDescription
holder.platoPrecio.text = plato.platoPrecio.toString()
holder.platoCantidad.text = plato.platoCantidad.toString()
}
override fun getItemCount(): Int {
return platoList.size
}
public class MyViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView){
val platoName : TextView = itemView.findViewById(R.id.platoNombre)
val platoDescription : TextView = itemView.findViewById(R.id.platoDescripcion)
val platoPrecio : TextView = itemView.findViewById(R.id.platoPrecio)
val platoCantidad : TextView = itemView.findViewById(R.id.platoCant)
platoCantidad.setOnClickListener(View.OnClickListener {
val intent = Intent(itemView.getContext(),SlotActivity::class.java)itemView.getContext().startActivity(intent)})
}
}
You're getting an error here because you have two lines of code jammed together on one line.
val intent = Intent(itemView.getContext(),SlotActivity::class.java)itemView.getContext().startActivity(intent)})
A better way to handle this is to expose your click as a listener that the Activity (or Fragment) can implement. It would be more versatile if you change your listener function to use the item type as the parameter instead of a name String explicitly, so I would change it like this. Also, if you mark it as a fun interface, you can use more concise syntax when you define it in your Activity.
fun interface OnPlatoClickListener{
fun onItemClick(item: Plato)
}
Then in your view holder, add a parameter for the item so the click listener you put on your view can pass the item to the Activity's OnPlatoClickListener. Mark the view holder class as inner so it has access to the itemClickListener property of the adapter.
inner class MyViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView){
var item: Plato? = null
val platoName : TextView = itemView.findViewById(R.id.platoNombre)
val platoDescription : TextView = itemView.findViewById(R.id.platoDescripcion)
val platoPrecio : TextView = itemView.findViewById(R.id.platoPrecio)
val platoCantidad : TextView = itemView.findViewById(R.id.platoCant)
// maybe you should set this on itemView instead so the whole row is clickable
platoCantidad.setOnClickListener {
item?.let(itemClickListener::onItemClick)
}
}
In onBindViewHolder you need to pass the item to the holder:
override fun onBindViewHolder(holder: MyAdapter.MyViewHolder, position: Int) {
val plato : Plato = platoList[position]
with(holder) {
item = plato
platoName.text = plato.platoName
platoDescription.text = plato.platoDescription
platoPrecio.text = plato.platoPrecio.toString()
platoCantidad.text = plato.platoCantidad.toString()
}
}
Then in your Activity (or Fragment), you can set a listener on the adapter when you create it:
val adapter = MyAdapter(theDataList) { clickedItem ->
val name = clickedItem.platoName
val intent = Intent(this#YourActivityName, SlotActivity::class.java)
// do something with name and intent?...
startActivity(intent)
}
// ...
override fun onBindViewHolder(holder: MyAdapter.MyViewHolder, position: Int) {
when (holder) {
is MyViewHolder -> {
holder.bind(platoList[position])
}
}
}
class MyViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
val platoName : TextView = itemView.findViewById(R.id.platoNombre)
val platoDescription : TextView = itemView.findViewById(R.id.platoDescripcion)
val platoPrecio : TextView = itemView.findViewById(R.id.platoPrecio)
val platoCantidad : TextView = itemView.findViewById(R.id.platoCant)
private val mActivity = itemView.context as Activity
private val intent = Intent(mActivity, SlotActivity::class.java)
fun bind(plato: Plato) {
platoName.text = plato.platoName
platoDescription.text = plato.platoDescription
platoPrecio.text = plato.platoPrecio.toString()
platoCantidad.text = plato.platoCantidad.toString()
platoCantidad.setOnClickListener {
intent.putExtra("key", value)
mActivity.startActivity(intent)
}
}
}
Note: it is much better to use English in variable names and comments so that developers who read your code later can easily understand it.
In this app, i am adding addresses to account and saving them in Realtime database (firebase).I want also to display them in recyclerview but they aren't visible.
Here visualization of my problem : https://youtu.be/OdlZNUQnA-k
The Code should work like this
Addressfragment Contains AddressesRecyclerview
AddAddressfragment for adding new Address
And it goes back to AddressFragment when new Address has been added.
Also when i tried to display all items from one array like for example postcode .It display only last added item. Even on for each loop. Like last item is deleted after adding
I understand that it need something like notifyDataSetChanged() but it doesnt work here
Here is The code:
AddressFragment:
package com.querto.fragments.address
import android.os.Bundle
import androidx.fragment.app.Fragment
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.Toast
import androidx.lifecycle.ViewModelProvider
import androidx.recyclerview.widget.LinearLayoutManager
import com.google.firebase.auth.FirebaseAuth
import com.google.firebase.database.DatabaseReference
import com.google.firebase.database.FirebaseDatabase
import com.querto.R
import com.querto.adapters.AddressAdapter
import com.querto.viewmodel.MainActivityViewModel
import kotlinx.android.synthetic.main.activity_main.*
import kotlinx.android.synthetic.main.fragment_address.view.*
class AddressFragment : Fragment() {
private lateinit var mMainActivityViewModel: MainActivityViewModel
private lateinit var database: DatabaseReference
private lateinit var mAuth: FirebaseAuth
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?): View? {
// Inflate the layout for this fragment
var view = inflater.inflate(R.layout.fragment_address, container, false)
database = FirebaseDatabase.getInstance().reference
mAuth = FirebaseAuth.getInstance()
mMainActivityViewModel = ViewModelProvider.AndroidViewModelFactory.getInstance(requireActivity().application).create(
MainActivityViewModel::class.java)
if(mAuth.currentUser==null){
Toast.makeText(requireContext(), "To add address please login",Toast.LENGTH_SHORT).show()
activity?.nav_view?.setCheckedItem(R.id.login)
activity?.supportFragmentManager?.beginTransaction()?.setCustomAnimations(R.anim.fragment_slide_in_anim, R.anim.fragment_fade_out_anim, R.anim.fragment_slide_out_anim, R.anim.fragment_fade_in_anim)?.replace(R.id.fragment_container, mMainActivityViewModel.loginFragment)?.commit()
}
view.recyclerViewAddress.layoutManager = LinearLayoutManager(requireContext(), LinearLayoutManager.VERTICAL, false)
view.recyclerViewAddress.adapter = AddressAdapter(requireContext(), mMainActivityViewModel.list_of_addresses)
view.add_address_btn.setOnClickListener {
activity?.supportFragmentManager?.beginTransaction()?.setCustomAnimations(R.anim.fragment_slide_in_anim, R.anim.fragment_fade_out_anim, R.anim.fragment_slide_out_anim, R.anim.fragment_fade_in_anim)?.replace(R.id.fragment_container, mMainActivityViewModel.addAddressFragment)?.commit()
}
return view
}
}
Adapter:
package com.querto.adapters
import android.app.Application
import android.content.Context
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.lifecycle.ViewModelProvider
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.querto.R
import com.querto.model.Address
import com.querto.viewmodel.MainActivityViewModel
import kotlinx.android.synthetic.main.my_address_row.view.*
class AddressAdapter(contextAdapter: Context, addresses: ArrayList<Address>):
RecyclerView.Adapter<AddressAdapter.MyViewHolder>() {
private var mMainActivityViewModel: MainActivityViewModel
private val context: Context = contextAdapter
private val local_addreses : ArrayList<Address> = addresses
private var database: DatabaseReference
private var mAuth: FirebaseAuth
init {
mMainActivityViewModel = ViewModelProvider.AndroidViewModelFactory.getInstance(context.applicationContext as Application).create(
MainActivityViewModel::class.java)
database = FirebaseDatabase.getInstance().reference
mAuth = FirebaseAuth.getInstance()
}
class MyViewHolder(itemView: View): RecyclerView.ViewHolder(itemView) {
val currentTitle = itemView.address_title
val currentId = itemView.address_id
val currentStreet = itemView.address_street
val currentPostcode = itemView.address_postcode
val currentHouseNumber = itemView.address_number
val currentAddressCityName = itemView.address_city
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyViewHolder {
return MyViewHolder(LayoutInflater.from(context).inflate(R.layout.my_address_row, parent, false))
}
override fun onBindViewHolder(holder: MyViewHolder, position: Int) {
holder.currentTitle.text = local_addreses[position].name
holder.currentId.text = (position + 1).toString()
holder.currentStreet.text =local_addreses[position].street
holder.currentPostcode.text =local_addreses[position].postcode
holder.currentHouseNumber.text = local_addreses[position].house_number
holder.currentAddressCityName.text = local_addreses[position].city_name
}
override fun getItemCount(): Int {
return local_addreses.size
}
fun addAddress(address: Address){
mMainActivityViewModel.list_of_addresses.add(address)
notifyDataSetChanged()
}
}
AddAddress:
package com.querto.fragments.address
import android.os.Bundle
import androidx.fragment.app.Fragment
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.Toast
import androidx.lifecycle.ViewModelProvider
import com.google.firebase.auth.FirebaseAuth
import com.google.firebase.database.DatabaseReference
import com.google.firebase.database.FirebaseDatabase
import com.querto.R
import com.querto.adapters.AddressAdapter
import com.querto.model.Address
import com.querto.viewmodel.MainActivityViewModel
import kotlinx.android.synthetic.main.fragment_add_address.view.*
class AddAddressFragment : Fragment() {
private lateinit var database: DatabaseReference
private lateinit var mAuth: FirebaseAuth
private lateinit var mMainActivityViewModel: MainActivityViewModel
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
// Inflate the layout for this fragment
var view = inflater.inflate(R.layout.fragment_add_address, container, false)
mMainActivityViewModel =
ViewModelProvider.AndroidViewModelFactory.getInstance(activity?.application!!)
.create(MainActivityViewModel::class.java)
view.addAddressButton.setOnClickListener {
val addressName = view.addAddressName.text.toString()
val addressStreet = view.addAddressStreet.text.toString()
val addressNumber = view.addAddressHouseNumber.text.toString()
val addressZipCode = view.addAddressCityZipCode.text.toString()
val addressCityName = view.addAddressCityName.text.toString()
if(inputCheck(addressName,addressStreet,addressNumber,addressZipCode, addressCityName)){
mAuth = FirebaseAuth.getInstance()
database = FirebaseDatabase.getInstance().reference
addAddress(addressName, addressStreet, addressNumber, addressZipCode, addressCityName)
}else{
Toast.makeText(requireContext(), "Please enter all fields", Toast.LENGTH_SHORT).show()
}
}
return view
}
private fun addAddress(addressName: String, addressStreet: String, addressNumber: String,addressZipCode: String, addressCityName: String) {
val address = Address(mAuth.currentUser?.uid, addressName, addressStreet,addressZipCode, addressNumber, addressCityName)
database.child("addresses").child(database.push().key.toString()).setValue(address).addOnCompleteListener {
if(it.isSuccessful){
val addressAdapter= AddressAdapter(requireContext(), mMainActivityViewModel.list_of_addresses)
addressAdapter.addAddress(address)
activity?.supportFragmentManager?.beginTransaction()?.setCustomAnimations(R.anim.fragment_slide_in_anim, R.anim.fragment_fade_out_anim, R.anim.fragment_slide_out_anim, R.anim.fragment_fade_in_anim)?.replace(R.id.fragment_container, mMainActivityViewModel.addressFragment)?.commit()
Toast.makeText(requireContext(), "Added address", Toast.LENGTH_SHORT).show()
}else{
Toast.makeText(requireContext(), "Fail at creating address", Toast.LENGTH_SHORT).show()
}
}
}
private fun inputCheck(addressName: String, addressStreet: String, addressNumber: String,addressZipCode: String, adressCityName: String)=
addressName.isNotEmpty() && addressStreet.isNotEmpty() && addressNumber.isNotEmpty() && addressZipCode.isNotEmpty() && adressCityName.isNotEmpty() && addressZipCode.length==5
}
MainActivityViewModel:
class MainActivityViewModel(application: Application) : AndroidViewModel(application) {
private lateinit var database: DatabaseReference
private lateinit var mAuth: FirebaseAuth
val homeFragment = HomeFragment()
val loginFragment = LoginFragment()
val registerFragment = RegisterFragment()
val detailsFragment = DetailsFragment()
val addressFragment = AddressFragment()
val addAddressFragment = AddAddressFragment()
var pizza_names: Array<String> = application.resources.getStringArray(R.array.pizza_titles)
var pizza_desc: Array<String> = application.resources.getStringArray(R.array.pizza_desc)
val pizza_small_price: IntArray = application.resources.getIntArray(R.array.pizza_small_price)
val pizza_medium_price: IntArray = application.resources.getIntArray(R.array.pizza_medium_price)
val pizza_big_price: IntArray = application.resources.getIntArray(R.array.pizza_big_price)
var pizza_img: Array<Int> = arrayOf(R.drawable.napoletana, R.drawable.margherita, R.drawable.estate, R.drawable.pepperone, R.drawable.pancetta, R.drawable.ortolana, R.drawable.marinara, R.drawable.diavola, R.drawable.messicana, R.drawable.quattro_formaggi, R.drawable.sugoza, R.drawable.semola, R.drawable.capriciossa, R.drawable.vulcano, R.drawable.romana, R.drawable.capodanno, R.drawable.primavera, R.drawable.regina, R.drawable.quattro_stagioni, R.drawable.cilento, R.drawable.tirolese, R.drawable.michele, R.drawable.pollo, R.drawable.havana, R.drawable.siciliana, R.drawable.sandra, R.drawable.bari, R.drawable.gringo, R.drawable.angelo, R.drawable.spinaci)
var focaccia_names: Array<String> = application.resources.getStringArray(R.array.foaccia_titles)
var focaccia_desc: Array<String> = application.resources.getStringArray(R.array.foaccia_desc)
val focaccia_price: IntArray = application.resources.getIntArray(R.array.foaccia_price)
var focaccia_img: Array<Int> = arrayOf(R.drawable.base, R.drawable.nutella)
var calzone_names: Array<String> = application.resources.getStringArray(R.array.calzone_titles)
var calzone_desc: Array<String> = application.resources.getStringArray(R.array.calzone_desc)
val calzone_price_normal: IntArray = application.resources.getIntArray(R.array.calzone_normal_price)
val calzone_price_big: IntArray = application.resources.getIntArray(R.array.calzone_big_price)
var calzone_img: Array<Int> = arrayOf(R.drawable.calzone)
var panuozzo_names: Array<String> = application.resources.getStringArray(R.array.panuozzo_titles)
var panuozzo_desc: Array<String> = application.resources.getStringArray(R.array.panuozzo_desc)
val panuozzo_price_normal: IntArray = application.resources.getIntArray(R.array.panuozzo_normal_price)
val panuozzo_price_big: IntArray = application.resources.getIntArray(R.array.panuozzo_big_price)
var panuozzo_img: Array<Int> = arrayOf(R.drawable.panuozzo)
val sosy_names: Array<String> = application.resources.getStringArray(R.array.sosy_titles)
val sosy_price: IntArray = application.resources.getIntArray(R.array.sosy_price)
val napoje_names: Array<String> = application.resources.getStringArray(R.array.napoje_titles)
val napoje_price: IntArray = application.resources.getIntArray(R.array.napoje_price)
val napoje_first_kind: Array<String> = application.resources.getStringArray(R.array.napoje_kinds_one)
val napoje_second_kind: Array<String> = application.resources.getStringArray(R.array.napoje_kinds_two)
val dodatki_names: Array<String> = application.resources.getStringArray(R.array.dodatki_titles)
val dodatki_small_price: IntArray = application.resources.getIntArray(R.array.dodatki_small_price)
val dodatki_medium_price: IntArray = application.resources.getIntArray(R.array.dodatki_medium_price)
val dodatki_big_price: IntArray = application.resources.getIntArray(R.array.dodatki_big_price)
var list_of_addresses = ArrayList<Address>()
private val mutableLoginStatus = MutableLiveData<Boolean>()
val loginStatus: LiveData<Boolean>
get() = mutableLoginStatus
fun checkLogin(username: String, password: String) {
viewModelScope.launch(Dispatchers.IO) {
database = FirebaseDatabase.getInstance().reference
mAuth = FirebaseAuth.getInstance()
mAuth.signInWithEmailAndPassword(username,password).addOnCompleteListener{
if(it.isSuccessful){
mutableLoginStatus.postValue(true)
}else{
mutableLoginStatus.postValue(false)
}
}
}
}
fun shareApp(context: Context) {
val openURL = Intent(android.content.Intent.ACTION_VIEW)
openURL.data = Uri.parse("https://www.facebook.com/1488596184507308/")
context.startActivity(openURL)
}
fun sendMail(context: Context) {
val sendEmail = Intent(Intent.ACTION_SEND)
val email: Array<String> = arrayOf("kontakt#cilento.pl")
sendEmail.setData(Uri.parse("mailto: kontakt#cilento.pl "))
sendEmail.putExtra(Intent.EXTRA_SUBJECT, "Problem z Usługą")
sendEmail.putExtra(Intent.EXTRA_TEXT, "Pizza którą zamówiłem nie przyszła na czas.\n\n\nMoje Dane Kontaktowe: \n\nImie: \nNazwisko: \nAdres: ")
sendEmail.setType("message/rfc822")
sendEmail.putExtra(Intent.EXTRA_EMAIL, email)
val chooser = Intent.createChooser(sendEmail, "Send mail using")
context.startActivity(chooser)
}
}
Address Class:
package com.querto.model
data class Address(
val userId: String?,
val name: String?,
val street: String?,
val postcode: String?,
val house_number: String?,
val city_name: String?
)
After Checking The MainActivityViewModel ,I See that you are not fetching data from the firebase database.
You should add this to AddressFragment class
also Add Variable called addressAdapter in the top of the class
fun getAddresses(){
val ref = FirebaseDatabase.getInstance().reference.child("addresses")
ref.addValueEventListener(object : ValueEventListener {
override fun onDataChange(dataSnapshot: DataSnapshot) {
if (dataSnapshot.exists()) {
val address = dataSnapshot.getValue<Address>()
mMainActivityViewModel.addresses_list.add(address)
addressAdapter.notifyDataChanged()
}
}
override fun onCancelled(databaseError: DatabaseError) {
}
})
}
Also Implementing Address adapter here does not do a thing . just remove it.
private fun addAddress(addressName: String, addressStreet: String, addressNumber: String,addressZipCode: String, addressCityName: String) {
val address = Address(mAuth.currentUser?.uid, addressName, addressStreet,addressZipCode, addressNumber, addressCityName)
database.child("addresses").child(database.push().key.toString()).setValue(address).addOnCompleteListener {
if(it.isSuccessful){
val addressAdapter= AddressAdapter(requireContext(), mMainActivityViewModel.address_title, mMainActivityViewModel.address_street,mMainActivityViewModel.address_post_code, mMainActivityViewModel.address_house_number, mMainActivityViewModel.address_city_name)
addressAdapter.addAddress(addressName, addressStreet,addressZipCode, addressNumber, addressCityName)
activity?.supportFragmentManager?.beginTransaction()?.setCustomAnimations(R.anim.fragment_slide_in_anim, R.anim.fragment_fade_out_anim, R.anim.fragment_slide_out_anim, R.anim.fragment_fade_in_anim)?.replace(R.id.fragment_container, mMainActivityViewModel.addressFragment)?.commit()
Toast.makeText(requireContext(), "Added address", Toast.LENGTH_SHORT).show()
}else{
Toast.makeText(requireContext(), "Fail at creating address", Toast.LENGTH_SHORT).show()
}
}
I will try to explain it here.
When you create a custom RecyclerView adapter usually you suppose to pass data you want to be displayed in the list. For me it is better to pass array Of Class You Made To Pass Data For Single Recylcer View itemArrayList<ClassOfSingleItem>, but it is not nessesary of course.
So when you call notifyDataSetCahnged you notify your adapter that data in this ArrayList was changed. But this call will not work if you create new Adapter every time you add or remove something from the RecyclerView list.
EDIT:
Let me explain what i see: you have button with click listener, when you click it you replace current fragmen with the one where you add data.
view.add_address_btn.setOnClickListener {
activity?.supportFragmentManager?.beginTransaction()?.setCustomAnimations(R.anim.fragment_slide_in_anim, R.anim.fragment_fade_out_anim, R.anim.fragment_slide_out_anim, R.anim.fragment_fade_in_anim)?.replace(R.id.fragment_container, mMainActivityViewModel.addAddressFragment)?.commit()
}
then, when you complete editing all fields what you do? replace your addAdressFragment with addressFragment:
val addressAdapter= AddressAdapter(requireContext(), mMainActivityViewModel.list_of_addresses)
addressAdapter.addAddress(address)
activity?.supportFragmentManager?.beginTransaction()?.setCustomAnimations(R.anim.fragment_slide_in_anim, R.anim.fragment_fade_out_anim, R.anim.fragment_slide_out_anim, R.anim.fragment_fade_in_anim)?.replace(R.id.fragment_container, mMainActivityViewModel.addressFragment)?.commit()
inside this method you CREATE new AdressAdapter to call method addAdrees which has notifyDataSetChanged call inside. But recyclerView in your addressFragment has no idea about this new AddressAdapter. This is the main problem. As i mentioned in comment below my answer there are only two ways to update RecyclerView so implement one of them.
Below I show some java code (you mentioned you know java) which can give you an idea:
public class PointActivity extends BaseActivity
implements SearchView.OnQueryTextListener{
private RecyclerView mRecyclerView;
#Override
protected void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_point);
mRecyclerView = findViewById(R.id.point_list);
mRecyclerView.setLayoutManager(new LinearLayoutManager(this));
List<PointItem> pointItems = getFilteredAndSortedList();
mRecyclerView.setAdapter(new PointAdapter(this, pointItems));
}
//here we need to update recyclerView
#Override
public boolean onQueryTextSubmit(String query) {
searchResult(query);
return false;
}
public void searchResult(String query) {
if (query.isEmpty()) {
//just full list if query is empty
mRecyclerView.setAdapter(new PointAdapter(this, getFilteredAndSortedList()));
} else {
PointSearchManager pointSearchManager = new PointSearchManager();
List<PointItem> list;
list = Arrays.asList(pointSearchManager.toFilters(getFilteredAndSortedList().toArray(new PointItem[0]), query));
//list filtered with query result
mRecyclerView.setAdapter(new PointAdapter(this, list));
}
}
I want each row of my RecyclerView to display all the details of one document of the collection.
I've used this exact same adapter code, albeit with a different class to serialize into. And it works well. But in this instance, it's simply not working.
But the code just doesn't get into populating the views.
My database is like:
reviews--Orange--vault--|
|-firstReview
|-secondReview
|-sjdeifhaih5aseoi
...
My query and adapter from the fragment:
override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
viewModel = ViewModelProviders.of(this).get(ReviewViewModel::class.java)
val reviewQuery = FirebaseFirestore.getInstance().collection("reviews").document("Orange").collection("vault")
val reviewBurnOptions = FirestoreRecyclerOptions.Builder<Review>()
.setQuery(reviewQuery, object : SnapshotParser<Review> {
override fun parseSnapshot(snapshot: DocumentSnapshot): Review {
return snapshot.toObject(Review::class.java)!!.also {
it.id = snapshot.id
}
}
}).setLifecycleOwner(this)
reviewRecycler.adapter=ReviewBurnAdapter(reviewBurnOptions.build())}
class ReviewBurnAdapter(options: FirestoreRecyclerOptions<Review>) :
FirestoreRecyclerAdapter<Review, ReviewBurnAdapter.ViewHolder>(options) {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {//I never reach this point
val view = LayoutInflater.from(parent.context).inflate(R.layout.row_review, parent, false)
return ViewHolder(view)
}
override fun onBindViewHolder(holder: ViewHolder, position: Int, item: Review) {
holder.apply {
holder.itemView.rowAuthor.text = item.author
}
}
inner class ViewHolder(override val containerView: View) :
RecyclerView.ViewHolder(containerView), LayoutContainer
}
Class to serialize into:
import com.google.firebase.firestore.Exclude
import com.google.firebase.firestore.PropertyName
import java.util.*
class Review(
#get:Exclude var id: String = "DEVIL",
#JvmField #PropertyName(AUTHOR) var author: String = "",
#JvmField #PropertyName(WRITEUP) var writeup: String = "",
//#JvmField #PropertyName(MOMENT) var moment:Date=Date(1997,12,1),
#JvmField #PropertyName(RATING) var rating: Int = 0
) {
companion object {
const val AUTHOR = "author"
const val WRITEUP = "writeup"
const val RATING = "rating"
//const val MOMENT="moment"
}
}
Also, there's no errors, it just never reaches the code that would generate and populate with viewHolders.
Alright, the fix was ultra simple, as #Prashant Jha pointed out, I hadn't specified a layout manager for my RecyclerView -_-
To be crystal clear, I added app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
to my xml, and everything worked.