I need to get details from the Firebase Realtime Database to RecyclerView which is in a fragment. I refer to many tutorials and finally code this. I got this error:
Here's my fragment code.
class busFragment : Fragment() {
private lateinit var dbref: DatabaseReference
private lateinit var routeRecyclerView: RecyclerView
private lateinit var routesArrayList: ArrayList<BusRoutes>
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?): View? {
// Inflate the layout for this fragment
return inflater.inflate(R.layout.fragment_bus, container, false)
}
#RequiresApi(Build.VERSION_CODES.JELLY_BEAN_MR1)
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
routeRecyclerView = view.findViewById(R.id.bus_routes)
routeRecyclerView.layoutManager = LinearLayoutManager(activity)
routeRecyclerView.setHasFixedSize(true)
routesArrayList = arrayListOf<BusRoutes>()
getRouteDetails()
}
private fun getRouteDetails() {
dbref = FirebaseDatabase.getInstance().getReference("BusRoutes")
dbref.addValueEventListener(object : ValueEventListener{
override fun onDataChange(snapshot: DataSnapshot) {
if (snapshot.exists()){
for (routeSnapshot in snapshot.children){
val route = routeSnapshot.getValue(BusRoutes::class.java)
routesArrayList.add(route!!)
}
routeRecyclerView.adapter = Adapter(routesArrayList)
}
}
override fun onCancelled(error: DatabaseError) {
TODO("Not yet implemented")
}
})
}
companion object {
}
}
here's my adapter.kt
class Adapter(private val routeslist: ArrayList<BusRoutes>) : RecyclerView.Adapter<Adapter.MyViewHolder>() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyViewHolder {
val itemView = LayoutInflater.from(parent.context).inflate(R.layout.items_busroutes,
parent,false)
return MyViewHolder(itemView as ViewGroup)
}
override fun onBindViewHolder(holder: MyViewHolder, position: Int) {
val currentItem = routeslist[position]
holder.route.text = currentItem.routeNo
holder.start.text = currentItem.start
holder.end.text = currentItem.end
}
override fun getItemCount(): Int {
return routeslist.size
}
class MyViewHolder(itemView:ViewGroup) : RecyclerView.ViewHolder(itemView){
val route : TextView = itemView.findViewById(R.id.routeNo)
val start : TextView = itemView.findViewById(R.id.startPlace)
val end : TextView = itemView.findViewById(R.id.endPlace)
}
}
Here's my data class.
Here's my firebase details.
Here's the line 52 val route = routeSnapshot.getValue(BusRoutes::class.java)
I try to fix this many times but still cannot find it. Help me. I'm still Learning Kotlin.
updated
BusRoutes class:
data class BusRoutes(val routeNo: String? = null, val start: String? = null, val end: String? = null)
You are getting the following error:
failed to convert value of type java.lang.long to string
Because the routeNo field in your database is of type number, while in your class is of type String and this is not correct. Both types must match because there is no way in Kotlin in which you can convert an object type Long to String, hence that error.
The simplest solution would to change the declaration fo your BusRoutes class like so:
data class BusRoutes(val routeNo: Long? = null, val start: String? = null, val end: String? = null)
// ^^
Related
hello I have an activity that has a navigation bar and fragments inside, in one of my fragments I save data in firebase, I want to get those data and display it in other fragment with recycler view. I watched a tutorial on youtube to do it cuz my teacher didn't teach us anything, the issue is the fragment that was supposed the display the data shows nothing, it's blank but I didn't understand why cuz I'm really new to this, if someone can take a look at my code and help me I would really appreciate it
This is my data class
data class Event(var eventName: String, var eventTime:String?=null)
This is ViewModel
class EventViewModel :ViewModel() {
private val repository : EventRepository
private val _allEvents = MutableLiveData<List<Event>>()
val allEvents : LiveData<List<Event>> = _allEvents
init {
repository= EventRepository().getInstance()
repository.loadEvents(_allEvents)
}
}
This is Repository
class EventRepository {
private val databaseReference: DatabaseReference= FirebaseDatabase.getInstance().getReference("PetEvents")
#Volatile private var INSTANCE : EventRepository ?=null
fun getInstance() : EventRepository{
return INSTANCE ?: synchronized(this) {
val instance = EventRepository()
INSTANCE=instance
instance
}
}
fun loadEvents(eventList : MutableLiveData<List<Event>>){
databaseReference.addValueEventListener(object:ValueEventListener{
override fun onDataChange(snapshot: DataSnapshot) {
try {
val _eventList : List<Event> = snapshot.children.map { dataSnapshot ->
dataSnapshot.getValue(Event::class.java)!!
}
eventList.postValue(_eventList)
}
catch (e: Exception){
}
}
override fun onCancelled(error: DatabaseError) {
TODO("Not yet implemented")
}
})
}
}
This is Adapter
class EventAdapter : RecyclerView.Adapter<EventAdapter.MyViewHolder>() {
private var eventList =ArrayList<Event>()
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyViewHolder {
val itemView = LayoutInflater.from(parent.context).inflate(
R.layout.event_item_cell,
parent, false
)
return MyViewHolder(itemView)
}
override fun onBindViewHolder(holder: MyViewHolder, position: Int) {
val currentItem = eventList[position]
holder.eventName.text =currentItem.eventName
holder.eventTime.text =currentItem.eventTime
}
override fun getItemCount(): Int {
return eventList.size
}
fun updateEventList(eventList: List<Event>){
this.eventList.clear()
this.eventList.addAll(eventList)
notifyDataSetChanged()
}
class MyViewHolder(itemView: View): RecyclerView.ViewHolder(itemView){
val eventName :TextView = itemView.findViewById(R.id.txEventName)
val eventTime :TextView = itemView.findViewById(R.id.txEventTime)
}
}
And this is the fragment I wish to display the data
private lateinit var viewModel : EventViewModel
private lateinit var eventRecyclerView: RecyclerView
lateinit var adapter: EventAdapter
class MainFragment : Fragment() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
}
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
return inflater.inflate(R.layout.fragment_main, container, false)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
eventRecyclerView=view.findViewById(R.id.recyclerView)
eventRecyclerView.layoutManager= LinearLayoutManager(context)
eventRecyclerView.setHasFixedSize(true)
adapter = EventAdapter()
eventRecyclerView.adapter= adapter
viewModel = ViewModelProvider(this).get(EventViewModel::class.java)
viewModel.allEvents.observe(viewLifecycleOwner, Observer {
adapter.updateEventList(it)
})
}
}
In my fragment xml I put a recycler view, there's no issue about that
And my item cell is a cardview idk if it has anything to with the problem I'm getting tho
I want to display the sum of a column from my room database in a textview.
After implementing my Query and setting the text of the Textview, it displays the following:
androidx.lifecycle.CoroutineLiveData#76f6edd
Any idea why this happens? Is it because my Query does not work or my way of implementign it is wrong?
It is my first time ever of actually programming. I know probably most of my code is not 100% correct, but I am working on it.
The Query from my Dao:
#Query("SELECT SUM(total)AS sum_total FROM receipt_table")
fun getSum(): Flow<Float>
The Fragment:
#AndroidEntryPoint
class HistoryFragment : Fragment(R.layout.fragment_history) {
private val viewModel: PurchaseViewmodel by viewModels()
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
return inflater.inflate(R.layout.fragment_history, container, false)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
val binding = FragmentHistoryBinding.bind(view)
val exampleAdapter = ExampleAdapter()
binding.apply{
recyclerView.apply{
layoutManager = LinearLayoutManager(requireContext())
adapter = exampleAdapter
}
totalSumTextView.apply {
val totalSum = viewModel.totalSum
text = totalSum.toString()
}
ItemTouchHelper(object : ItemTouchHelper.SimpleCallback(0, ItemTouchHelper.LEFT or ItemTouchHelper.RIGHT) {
override fun onMove(
recyclerView: RecyclerView,
viewHolder: RecyclerView.ViewHolder,
target: RecyclerView.ViewHolder
): Boolean {
return false
}
override fun onSwiped(viewHolder: RecyclerView.ViewHolder, direction: Int) {
val receipt = exampleAdapter.currentList[viewHolder.adapterPosition]
viewModel.onSwipe(receipt)
}
}).attachToRecyclerView(recyclerView)
}
setFragmentResultListener("add_receipt_request"){_,bundle ->
val result = bundle.getInt("add_receipt_request")
viewModel.onAddResult(result)
}
viewModel.receipts.observe(viewLifecycleOwner){
exampleAdapter.submitList(it)
}
viewLifecycleOwner.lifecycleScope.launchWhenStarted {
viewModel.addTaskEvent.collect { event->
when(event){
is PurchaseViewmodel.TasksEvent.ShowUndoDelete -> {
Snackbar.make(requireView(),"Tasks deleted", Snackbar.LENGTH_LONG)
.setAction("UNDO"){
viewModel.unDoDeleteClick(event.receipts)
}.show()
}
}
}
}
}
}
And the Viewmodel:
#HiltViewModel
class PurchaseViewmodel #Inject constructor(
private val receiptDao: ReceiptDao
): ViewModel() {
private val tasksEventChannel = Channel<TasksEvent>()
val addTaskEvent = tasksEventChannel.receiveAsFlow()
val receipts = receiptDao.getAllReceipts().asLiveData()
val totalSum = receiptDao.getSum().asLiveData()
fun onAddResult(result: Int){
when (result){
ADD_RECEIPT_RESULT_OK ->showReceiptSavedConfirmation("Receipt has been saved")
}
}
private fun showReceiptSavedConfirmation (text: String) = viewModelScope.launch {
tasksEventChannel.send(TasksEvent.ShowReceiptSavedConfirmation(text))
}
fun onSwipe (receipts: Receipts) = viewModelScope.launch {
receiptDao.delete(receipts)
tasksEventChannel.send(TasksEvent.ShowUndoDelete(receipts))
}
fun unDoDeleteClick (receipts: Receipts) = viewModelScope.launch {
receiptDao.insert(receipts)
}
sealed class TasksEvent {
data class ShowReceiptSavedConfirmation(val msg: String) : TasksEvent()
data class ShowUndoDelete(val receipts: Receipts) : TasksEvent()
}
}
You are getting a LiveData object here, not just a value:
val totalSum = receiptDao.getSum().asLiveData()
When you call:
totalSum.toString()
It calls toString method on the LiveData object that the reason, why you have "androidx.lifecycle.CoroutineLiveData#76f6edd" inside your TextView.
To fix the issue, just replace:
totalSumTextView.apply {
val totalSum = viewModel.totalSum
text = totalSum.toString()
}
with:
viewModel.totalSum.observe(this) { totalSumTextView.text = it}
Detailed review, how to work with a LiveData you can find here:
https://developer.android.com/topic/libraries/architecture/livedata
i have problem when try to display recyvlerview using kotlin , warn no adapter attach skipping layout and nothing happen in my app, ive tried many way but nothing solve it
how could i do ? please review my code, i will very thankfull to anyone can help
i have problem when try to display recyvlerview using kotlin , warn no adapter attach skipping layout and nothing happen in my app, ive tried many way but nothing solve it
how could i do ? please review my code, i will very thankfull to anyone can help
class ArticleFragment : Fragment() {
private lateinit var mPeopleRVAdapter: FirebaseRecyclerAdapter<News, NewsViewHolder>
//function oncreate
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?): View? {
// Inflate the layout for this fragment
return inflater.inflate(R.layout.activity_news, container, false)
}
//viewholder
class NewsViewHolder internal constructor(var mView: View) : RecyclerView.ViewHolder(mView) {
fun setTitle(title: String?) {
val post_title = mView.findViewById<View>(R.id.post_title) as TextView
post_title.text = title
}
fun setDesc(desc: String?) {
val post_desc = mView.findViewById<View>(R.id.post_desc) as TextView
post_desc.text = desc
}
fun setImage(ctx: Context?, image: String?) {
val post_image = mView.findViewById<View>(R.id.post_image) as ImageView
Picasso.with(ctx).load(image).into(post_image)
}
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
//"News" here will reflect what you have called your database in Firebase.
//"News" here will reflect what you have called your database in Firebase.
val mDatabase = FirebaseDatabase.getInstance().reference.child("news")
mDatabase.keepSynced(true)
val mPeopleRV = view.findViewById<View>(R.id.myRecycleView) as RecyclerView
val personsRef =
FirebaseDatabase.getInstance().reference.child("news")
val personsQuery = personsRef.orderByKey()
val personsOptions: FirebaseRecyclerOptions<News> = FirebaseRecyclerOptions.Builder<News>().setQuery(
personsQuery, News::class.java).build()
mPeopleRVAdapter = object : FirebaseRecyclerAdapter<News, NewsViewHolder>(personsOptions) {
override fun onBindViewHolder(holder: NewsViewHolder, position: Int, model: News) {
holder.setTitle(model.title)
holder.setDesc(model.desc)
holder.setImage(activity ,model.image)
holder.mView.setOnClickListener {
val url: String? = model.url
val intent = Intent(activity, NewsWebView::class.java)
intent.putExtra("id", url)
startActivity(intent)
}
mPeopleRV.hasFixedSize()
mPeopleRV.layoutManager = LinearLayoutManager(context)
mPeopleRV.apply {
mPeopleRV.adapter = mPeopleRVAdapter
}
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): NewsViewHolder {
val view = LayoutInflater.from(parent.context).inflate(R.layout.news_row, parent, false)
return NewsViewHolder(view)
}
}
}
override fun onStart() {
super.onStart()
mPeopleRVAdapter.startListening()
}
override fun onStop() {
super.onStop()
this.mPeopleRVAdapter.stopListening()
}
}
Move this code out of adapter:
mPeopleRV.hasFixedSize()
mPeopleRV.layoutManager = LinearLayoutManager(context)
mPeopleRV.apply {
mPeopleRV.adapter = mPeopleRVAdapter
}
This is my first time building an application. I want to display my data in firebase realtimedatabase in recyclerview. but 'E/RecyclerView: No adapter attached; skipping layout' is on the Run chart.
I'll show you my codes in order.
at first, this is my Data class in kotlin
data class BalInputDTO(
var Id : String? = null,
var Itype: String? = null,
var Icategory: String? = null,
var ldate : String? = null,
var balance: String? = null,
var commnet: String? = null)
and then this is my adapter.kt
class BalAdapter(val context: Context, val BalList: ArrayList<BalInputDTO>) :
RecyclerView.Adapter<BalAdapter.Holder>() {
override fun onBindViewHolder(holder: Holder, position: Int) {
holder?.bind(BalList[position], context)
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): Holder {
val view = LayoutInflater.from(context).inflate(R.layout.household_detail, parent, false)
return Holder(view)
}
override fun getItemCount(): Int {
return BalList.size
}
inner class Holder(view: View?) : RecyclerView.ViewHolder(view!!) {
val recordCategory = view?.findViewById<TextView>(R.id.record_category)
val recordNote = view?.findViewById<TextView>(R.id.record_note)
val recordAmount = view?.findViewById<TextView>(R.id.record_amount)
val recordDate = view?.findViewById<TextView>(R.id.record_date)
val recordDeleteImageView = view?.findViewById<ImageButton>(R.id.record_delete_image_button)
fun bind(bal: BalInputDTO, context: Context) {
recordCategory?.text = bal.Icategory
recordNote?.text = bal.commnet
recordAmount?.text = bal.balance
recordDate?.text = bal.ldate
// recordDeleteImageView.imageb
}
}
}
and the last code. this is my Fragment.kt (only onCreatView part)
var fragmentView : View? = null
var firedatabase : FirebaseDatabase? = null
var BalList : ArrayList<BalInputDTO> ? = null
var ref : DatabaseReference? = null
var mRecyclerView : RecyclerView? =null
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
fragmentView= LayoutInflater.from(activity).inflate(R.layout.fragment_household, container, false)
firedatabase = FirebaseDatabase.getInstance()
mRecyclerView = fragmentView?.findViewById(R.id.household_recyclerview)
mRecyclerView?.setHasFixedSize(true)
mRecyclerView?.layoutManager = LinearLayoutManager(context)
BalList = arrayListOf<BalInputDTO>()
ref = FirebaseDatabase.getInstance().getReference("BalInput")
ref?.addValueEventListener(object : ValueEventListener {
override fun onCancelled(p0: DatabaseError) {
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
}
override fun onDataChange(p0: DataSnapshot) {
if(p0!!.exists()){
for (h in p0.children){
val bal = h.getValue(BalInputDTO::class.java)
BalList?.add(bal!!)
}
val adapter = BalAdapter(context!!,BalList = ArrayList<BalInputDTO>())
mRecyclerView?.setAdapter(adapter)
}
}
})
return fragmentView
}
and this is my database enter image description here
Please let me know if I missed something or did something wrong.
It seems like you passing the wrong list to your adapter. Try this
val adapter = BalAdapter(context!!,BalList)
instead of
val adapter = BalAdapter(context!!,BalList = ArrayList<BalInputDTO>())
I'd say the real issue here is this line
val adapter = BalAdapter(context!!,BalList = ArrayList<BalInputDTO>())
try changing it to:
val adapter = BalAdapter(context!!,BalList)
since this is where you are adding all the elements from FB.
I want to create a small application that once the user clicks on floating button action to add element. It will be added automatically
to the top of the recycle view.
I have created for this A main activity which contains the fragment of the recycler and the add flotten.
Once the user clicks on this button a fragmented dialog is shown to insert the element. Once the user confirms i want to add the new element to the top of the recycle view.
TO did I used a global array which contains the item.
I have not implemented the logic yet for that. But what surprised me once I click on confirm button of the shown dialog the element added correctly to the global array, however, the recycle view on his own without any action on it duplicate the latest element.
I used the model view presenter for that and here are my diff classes
1.MainActivity
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
addFragment(InterFragment(), R.id.container)
fab.setOnClickListener { view ->
val dialog = IntervAddFragment()
val ft = supportFragmentManager.beginTransaction()
dialog.show(ft, ContentValues.TAG)
}
}
}
2.ADDFragmet
class IntervAddFragment : DialogFragment(), AddContract.View
{
val presenter: AddPresenter by lazy { AddPresenter(this) }
var dat=""
var plom=""
var typ=""
override fun onCreate(savedInstanceState: Bundle?) { ....}
override fun onStart() {.... }
override fun onCreateView(inflater: LayoutInflater?, parent: ViewGroup?, state: Bundle?): View? {...}
override fun onViewCreated(view: View?, savedInstanceState: Bundle?) {....}
fun on_confirmClick()
{
presenter.on_confirmClick(dat,plom,typ)
Log.e("errrr",presenter.getList().toString())
dismiss()
}
fun fillSpinerPlom(view : View?) {.... }
fun fillSpinerType(view : View?) {.... }
private fun updateDateInView(datep:String) {....}
}
3.IntervFragement
InterFragment: Fragment(), InterContract.View {
var views: View? = null
var List_inter_adapter: InterListAdapter? = null
private var recyclerView: RecyclerView? = null
val presenter: InterventionPresenter by lazy { InterventionPresenter(this) }
override fun onCreateView(inflater: LayoutInflater?, container: ViewGroup?,
savedInstanceState: Bundle?): View? {.... }
interface ClickListener {
fun onClick(view: View, position: Int)
fun onLongClick(view: View?, position: Int)
}
internal class RecyclerTouchListener(context: Context, recyclerView: RecyclerView, private val clickListener: ClickListener?) : RecyclerView.OnItemTouchListener {..... }
override fun onInterceptTouchEvent(rv: RecyclerView, e: MotionEvent): Boolean {.....}
override fun onViewCreated(view: View?, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
recyclerView = views!!.findViewById<View>(R.id.interv_item_listview) as RecyclerView
List_inter_adapter = InterListAdapter(activity, presenter.getList(true))
recyclerView!!.adapter = List_inter_adapter
recyclerView!!.layoutManager = LinearLayoutManager(activity)
}
}
4.InterPresenter
class InterventionPresenter(val view: InterContract.View)
{
fun getList(firstTime:Boolean): LinkedList<InterItem> {
if(firstTime)
return populateList().list
else
return items
}
fun populateList(): ListInter
{
val item1 = InterItem(1, DateT(2018, Calendar.FEBRUARY, 6).date, plomb.get(0), types.get(0))
val item2 = InterItem(2, DateT(2018, Calendar.MARCH, 8).date, plomb.get(1), types.get(1))
val item3= InterItem(3, DateT(2018, Calendar.MAY, 10).date, plomb.get(2), types.get(2))
val lstint = ListInter()
lstint.list= LinkedList(listOf(item1, item2, item3))
items=lstint.list
return lstint
}
companion object {
val plomb= LinkedList<String>(listOf("Dina", "Lili", "Wiseem"))
val types= LinkedList<String>(listOf("type1","type2","type3"))
lateinit var items: LinkedList<InterItem>
}
}
5.ADD Presenter
class AddPresenter(val view: AddContract.View)
{
fun on_confirmClick(date:String,plom:String,type:String)
{
val item= InterItem(items.size+1,date,plom,type)
items.push(item)
}
fun getList(): LinkedList<InterItem> {
return items
}
fun getplom(): LinkedList<String>
{ return plomb
}
fun getType(): LinkedList<String>
{ return types
}
}
6.InterListAdapter
class InterListAdapter(private val context: Context, linkedList: LinkedList<InterItem>) : RecyclerView.Adapter<InterListAdapter.ViewHolder>()
{
internal var linkedList = LinkedList<InterItem>()
private val inflater: LayoutInflater
//private lateinit var listener: OnTaskSelected
init {... }
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {... }
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
holder.numero?.text=(linkedList[position].numero).toString()
holder.date?.text=linkedList[position].date
holder.plom?.text=linkedList[position].plom
holder.type?.text=linkedList[position].type
}
override fun getItemCount(): Int {
return linkedList.size
}
inner class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
var numero: TextView?=null
var date: TextView?=null
var plom: TextView?=null
var type: TextView?=null
var btn_delete: Button?=null
var btn_update: Button?=null
init {
numero = itemView.findViewById(R.id.numero)
date = itemView.findViewById(R.id.date)
plom = itemView.findViewById(R.id.plom)
type = itemView.findViewById(R.id.type)
btn_delete = itemView.findViewById(R.id.btn_supprimer)
btn_update = itemView.findViewById(R.id.btn_modifier)
}
}
}
and here is the ListInter
class ListInter
{
var list= LinkedList<InterItem>()
get() = field
set(value){field = value}
}
RecyclerView can't handle array change event by itself. So, you should notify it by calling the following after you've added element to array:
recyclerView.getAdapter().notifyItemInserted(items.size() - 1)