The app must show all product but show only last (number 6). when i launch app, work, but return this error : E/RecyclerView: No adapter attached; skipping layout. I'm beginner in android language and searching online i didn't find nothing.
the app was composed from mainActivity and CartActivity
output
this is realtime DB
this is main code
package com.example.tpsi_maccagnola
import android.content.Intent
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import androidx.recyclerview.widget.GridLayoutManager
import com.example.tpsi_maccagnola.events.UpdateCarrello
import com.example.tpsi_maccagnola.listener.cartlistener
import com.example.tpsi_maccagnola.listener.foodListener
import com.example.tpsi_maccagnola.modelli.Carrello
import com.example.tpsi_maccagnola.modelli.Food
import com.google.android.material.snackbar.Snackbar
import com.google.firebase.database.DataSnapshot
import com.google.firebase.database.DatabaseError
import com.google.firebase.database.FirebaseDatabase
import com.google.firebase.database.ValueEventListener
import kotlinx.android.synthetic.main.activity_main.*
import org.greenrobot.eventbus.EventBus
import org.greenrobot.eventbus.Subscribe
import org.greenrobot.eventbus.ThreadMode
class MainActivity : AppCompatActivity(), foodListener, cartlistener {
lateinit var foodListener: foodListener
lateinit var cartlistener1: cartlistener
override fun onStart() {
super.onStart()
EventBus.getDefault().register(this)
}
override fun onStop() {
super.onStop()
if (EventBus.getDefault().hasSubscriberForEvent(UpdateCarrello::class.java))
EventBus.getDefault().removeStickyEvent(UpdateCarrello::class.java)
EventBus.getDefault().unregister(this)
}
#Subscribe(threadMode = ThreadMode.MAIN, sticky = true)
fun onUpdateCart(event: UpdateCarrello) {
counCartDB()
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
init()
caricaDB()
counCartDB()
}
private fun counCartDB() {
val cartmodels: MutableList<Carrello> = ArrayList()
FirebaseDatabase.getInstance()
.getReference("carrello")
.child("UNIQUE_USER_ID")
.addListenerForSingleValueEvent(object : ValueEventListener {
override fun onDataChange(snapshot: DataSnapshot) {
for (cartsnapshot in snapshot.children) {
val cartmodel = cartsnapshot.getValue(Carrello::class.java)
cartmodel!!.key = cartsnapshot.key
cartmodels.add(cartmodel)
}
cartlistener1.onCartSuccess(cartmodels)
}
override fun onCancelled(error: DatabaseError) {
cartlistener1.onCartFailed(error.message)
}
})
}
private fun caricaDB() {
val foods: MutableList<Food> = ArrayList()
FirebaseDatabase.getInstance()
.getReference("Food").addListenerForSingleValueEvent(object : ValueEventListener {
override fun onDataChange(snapshot: DataSnapshot) {
if (snapshot.exists()) {
for (FoodSnapshot in snapshot.children) {
val foodModel = FoodSnapshot.getValue(Food::class.java)
foodModel!!.key = FoodSnapshot.key
foods.add(foodModel)
}
foodListener.onloadsuccess(foods)
} else {
foodListener.onloadfailed("non esiste")
}
}
override fun onCancelled(error: DatabaseError) {
foodListener.onloadfailed(error.message)
}
})
}
private fun init() {
foodListener = this
cartlistener1 = this
val gridLayoutManager = GridLayoutManager(this, 2)
food_recycler.layoutManager = gridLayoutManager
food_recycler.addItemDecoration(decoration())
btnCart.setOnClickListener{ startActivity(Intent(this,CartActivity::class.java))}
}
override fun onloadsuccess(foodModelList: List<Food>?) {
val adapter = FoodAdapter(this, foodModelList!!, cartlistener1)
food_recycler.adapter = adapter
}
override fun onloadfailed(message: String?) {
Snackbar.make(layoutprincipale, message!!, Snackbar.LENGTH_LONG).show()
}
override fun onCartSuccess(carrelloList: List<Carrello>) {
var cartTot = 0
for (cartlist in carrelloList!!) {
cartTot += cartlist!!.quantita
/*badge!!.setNumber(cartTot)*/
}
}
override fun onCartFailed(message: String?) {
Snackbar.make(layoutprincipale, message!!, Snackbar.LENGTH_LONG).show()
}
}
this is adapter
package com.example.tpsi_maccagnola
import android.content.Context
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.ImageView
import android.widget.TextView
import androidx.recyclerview.widget.RecyclerView
import com.bumptech.glide.Glide
import com.example.tpsi_maccagnola.events.UpdateCarrello
import com.example.tpsi_maccagnola.listener.RecyclerListener
import com.example.tpsi_maccagnola.listener.cartlistener
import com.example.tpsi_maccagnola.modelli.Carrello
import com.example.tpsi_maccagnola.modelli.Food
import com.google.firebase.database.DataSnapshot
import com.google.firebase.database.DatabaseError
import com.google.firebase.database.FirebaseDatabase
import com.google.firebase.database.ValueEventListener
import org.greenrobot.eventbus.EventBus
class FoodAdapter(
private val context: Context,
private val list: List<Food>,
private val cartlistener1: cartlistener
) : RecyclerView.Adapter<FoodAdapter.FoodHolder>() {
class FoodHolder(itemView: View) : RecyclerView.ViewHolder(itemView), View.OnClickListener {
var imageView: ImageView?= null
var txtName: TextView?= null
var txtprice: TextView?= null
private var clickListener: RecyclerListener? = null
fun setClickListener (clickListener: RecyclerListener){
this.clickListener = clickListener;
}
init {
imageView = itemView.findViewById(R.id.imageView) as ImageView;
txtName = itemView.findViewById(R.id.txtName) as TextView;
txtprice = itemView.findViewById(R.id.txtprice) as TextView;
itemView.setOnClickListener(this)
}
override fun onClick(p0: View?) {
clickListener!!.onclcickProdotto(p0,adapterPosition )
}
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): FoodHolder {
return FoodHolder(LayoutInflater.from(context).inflate(R.layout.layout_food,parent,false))
}
override fun onBindViewHolder(holder: FoodHolder, position: Int) {
Glide.with(context).load(list[position].image).into(holder.imageView!!)
holder.txtName!!.text =StringBuilder().append(list[position].name)
holder.txtprice!!.text =StringBuilder("€").append(list[position].price)
holder.setClickListener(object: RecyclerListener{
override fun onclcickProdotto(view: View?, position: Int) {
aggiungiCarrello(list[position])
}
} )
}
private fun aggiungiCarrello(food: Food) {
val utenteCart = FirebaseDatabase.getInstance().getReference("Cart").child("UNIQUE_USER_ID")
utenteCart.child(Food.key!!).addListenerForSingleValueEvent(object :ValueEventListener{
override fun onDataChange(snapshot: DataSnapshot) {
if(snapshot.exists()){ //se è gia presente aggiorno
val cartmodel = snapshot.getValue(Carrello::class.java)
val updatecart: MutableMap<String,Any> = HashMap()
cartmodel!!.quantita = cartmodel!!.quantita+1
updatecart["quantita"] = cartmodel!!.quantita
updatecart["totale"] =cartmodel!!.quantita * cartmodel.price!!.toFloat()
utenteCart.child(food.key!!)
.updateChildren(updatecart)
.addOnSuccessListener {
EventBus.getDefault().postSticky(UpdateCarrello())
cartlistener1.onCartFailed("aggiunto correttamente")
}.addOnFailureListener{e -> cartlistener1.onCartFailed(e.message)
}
}else{
val carrello1 = Carrello
carrello1.key = food.key
carrello1.name = food.name
carrello1.image = food.image
carrello1.price = food.price
carrello1.quantita = 1
carrello1.totale = food.price!!.toFloat()
utenteCart.child(food.key!!)
.setValue(carrello1)
.addOnSuccessListener {
EventBus.getDefault().postSticky(UpdateCarrello())
cartlistener1.onCartFailed("aggiunto correttamente")
}.addOnFailureListener{e -> cartlistener1.onCartFailed(e.message)
}
}
}
override fun onCancelled(error: DatabaseError) {
cartlistener1.onCartFailed(error.message)
}
})
}
override fun getItemCount(): Int {
return list.size
}
}
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")
}
}
I'm hoping to update a Boolean value of data that I have in the list. If checkbox is checked I would like the value to be true, but if the checkbox is unchecked, I would like it to change the value to false, and vice versa.
On clicking the checkbox, I would like it to change the value of 'favourite' in my db to either true or false, depending on whether the checkbox is checked or not. I am unsure how to update this value with a checkbox. Please help! My code is as follows:
Adapter:
package org.wit.hikingtrails.adapters
import android.view.LayoutInflater
import android.view.ViewGroup
import androidx.recyclerview.widget.RecyclerView
import com.squareup.picasso.Picasso
import org.wit.hikingtrails.databinding.CardHikeBinding
import org.wit.hikingtrails.main.MainApp
import org.wit.hikingtrails.models.HikeModel
import timber.log.Timber.i
interface HikeListener {
fun onHikeClick(hike: HikeModel)
}
class HikeAdapter constructor(private var hikes: List<HikeModel>,
private val listener: HikeListener) :
RecyclerView.Adapter<HikeAdapter.MainHolder>() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MainHolder {
val binding = CardHikeBinding
.inflate(LayoutInflater.from(parent.context), parent, false)
return MainHolder(binding)
}
override fun onBindViewHolder(holder: MainHolder, position: Int) {
val hike = hikes[holder.adapterPosition]
holder.bind(hike, listener)
}
override fun getItemCount(): Int = hikes.size
class MainHolder(private val binding : CardHikeBinding) :
RecyclerView.ViewHolder(binding.root) {
fun bind(hike: HikeModel, listener: HikeListener) {
binding.hikeName.text = hike.name
binding.description.text = hike.description
binding.favourite.isChecked = hike.favourite
binding.favourite.setOnClickListener {
hike.favourite = binding.favourite.isChecked
listener.onHikeClick(hike)
}
if (hike.image != ""){
Picasso.get()
.load(hike.image)
.resize(200, 200)
.into(binding.imageIcon)
}
binding.root.setOnClickListener { listener.onHikeClick(hike) }
}
}
}
Presenter:
package org.wit.hikingtrails.views.hikeList
import android.content.Intent
import androidx.activity.result.ActivityResultLauncher
import androidx.activity.result.contract.ActivityResultContracts
import com.google.firebase.auth.FirebaseAuth
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
import org.wit.hikingtrails.main.MainApp
import org.wit.hikingtrails.models.HikeModel
import org.wit.hikingtrails.views.BasePresenter
import org.wit.hikingtrails.views.BaseView
import org.wit.hikingtrails.views.VIEW
import org.wit.hikingtrails.views.hike.HikeView
import org.wit.hikingtrails.views.map.HikeMapView
import org.wit.hikingtrails.views.signIn.SignInView
class HikeListPresenter(view: BaseView) : BasePresenter(view) {
var hike = HikeModel()
private lateinit var refreshIntentLauncher : ActivityResultLauncher<Intent>
private lateinit var editIntentLauncher : ActivityResultLauncher<Intent>
init {
registerEditCallback()
registerRefreshCallback()
}
suspend fun getHikes() = app.hikes.findAll()
fun doAddHike() {
val launcherIntent = Intent(view, HikeView::class.java)
editIntentLauncher.launch(launcherIntent)
}
fun doEditHike(hike: HikeModel) {
val launcherIntent = Intent(view, HikeView::class.java)
launcherIntent.putExtra("hike_edit", hike)
editIntentLauncher.launch(launcherIntent)
}
fun doShowHikesMap() {
val launcherIntent = Intent(view, HikeMapView::class.java)
editIntentLauncher.launch(launcherIntent)
}
fun doLogout() {
FirebaseAuth.getInstance().signOut()
val launcherIntent = Intent(view, SignInView::class.java)
editIntentLauncher.launch(launcherIntent)
}
private fun registerRefreshCallback() {
refreshIntentLauncher =
view!!.registerForActivityResult(ActivityResultContracts.StartActivityForResult())
{
GlobalScope.launch(Dispatchers.Main){
getHikes()
}
}
}
private fun registerEditCallback() {
editIntentLauncher =
view!!.registerForActivityResult(ActivityResultContracts.StartActivityForResult())
{ }
}
}
View:
package org.wit.hikingtrails.views.hikeList
import android.os.Bundle
import android.view.Menu
import android.view.MenuItem
import androidx.appcompat.app.AppCompatActivity
import androidx.recyclerview.widget.LinearLayoutManager
import org.wit.hikingtrails.R
import org.wit.hikingtrails.adapters.HikeAdapter
import org.wit.hikingtrails.adapters.HikeListener
//import org.wit.hikingtrails.databinding.ActivityHikeListBinding
import org.wit.hikingtrails.main.MainApp
import org.wit.hikingtrails.models.HikeModel
import android.content.Intent
import android.view.*
import androidx.recyclerview.widget.RecyclerView
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
import org.wit.hikingtrails.databinding.ActivityHikeListBinding
import org.wit.hikingtrails.views.BaseView
import timber.log.Timber
class HikeListView : BaseView(), HikeListener {
lateinit var app: MainApp
lateinit var binding: ActivityHikeListBinding
lateinit var presenter: HikeListPresenter
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityHikeListBinding.inflate(layoutInflater)
setContentView(binding.root)
binding.toolbar.title = title
setSupportActionBar(binding.toolbar)
presenter = HikeListPresenter(this)
val layoutManager = LinearLayoutManager(this)
binding.recyclerView.layoutManager = layoutManager
updateRecyclerView()
}
// override fun showHikes(hikes: List<HikeModel>) {
// recyclerView.adapter = HikeAdapter(hikes, this)
// recyclerView.adapter?.notifyDataSetChanged()
// }
override fun onCreateOptionsMenu(menu: Menu): Boolean {
menuInflater.inflate(R.menu.menu_main, menu)
return super.onCreateOptionsMenu(menu)
}
override fun onResume() {
//update the view
super.onResume()
updateRecyclerView()
binding.recyclerView.adapter?.notifyDataSetChanged()
Timber.i("recyclerView onResume")
}
override fun onOptionsItemSelected(item: MenuItem): Boolean {
when (item?.itemId) {
R.id.item_add -> presenter.doAddHike()
R.id.item_map -> presenter.doShowHikesMap()
R.id.logout -> presenter.doLogout()
}
return super.onOptionsItemSelected(item)
}
override fun onHikeClick(hike: HikeModel) {
presenter.doEditHike(hike)
}
private fun updateRecyclerView(){
GlobalScope.launch(Dispatchers.Main){
binding.recyclerView.adapter =
HikeAdapter(presenter.getHikes(), this#HikeListView)
}
}
}
I want to filter from the recyclerview, recyclerview is being populated from API call. I want to filter by country. How can I achieve this?
This is the adapter
import android.content.Intent
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.Button
import android.widget.ImageView
import android.widget.TextView
import androidx.recyclerview.widget.RecyclerView
import com.bumptech.glide.Glide
class MyAdapter(private var data: List<DemoDataBaseItem>, private val onClickListener: OnClickListener) : RecyclerView.Adapter<MyAdapter.MyViewHolder>() {
val listener = onClickListener
class MyViewHolder(val view: View) : RecyclerView.ViewHolder(view) {
lateinit var btn: Button
val description = view.findViewById<TextView>(R.id.tvDescription)
val title = view.findViewById<TextView>(R.id.tvTitle)
val imageView = view.findViewById<ImageView>(R.id.imageView)
fun bind(property: DemoDataBaseItem) {
btn = view.findViewById(R.id.url_btn)
title.text = property.property_name
description.text = property.location_country
Glide.with(view.context).load(property.imageurl).centerCrop().into(imageView)
}
}
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 data.size
}
override fun onBindViewHolder(holder: MyViewHolder, position: Int) {
holder.bind(data[position])
holder.btn.setOnClickListener {
listener.onClick(data[position].beurl)
}
}
}
and this is the Main.kt file
import android.annotation.SuppressLint
import android.app.DatePickerDialog
import android.content.Intent
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.text.Editable
import android.text.TextWatcher
import android.util.Log
import android.widget.EditText
import android.widget.Filter
import android.widget.TextView
import androidx.core.view.contains
import androidx.core.view.iterator
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import retrofit2.Call
import retrofit2.Callback
import retrofit2.Response
import java.text.SimpleDateFormat
import java.util.*
import kotlin.collections.ArrayList
class MainActivity : AppCompatActivity(), OnClickListener{
private lateinit var recyclerView: RecyclerView
private lateinit var manager: RecyclerView.LayoutManager
private lateinit var myAdapter: RecyclerView.Adapter<*>
private lateinit var checkin: EditText
private lateinit var checkout: EditText
private lateinit var editTextSearch: EditText
private lateinit var itemlist: List<DemoDataBaseItem>
#SuppressLint("SimpleDateFormat")
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
manager = LinearLayoutManager(this)
getAllData()
checkin = findViewById(R.id.editTextDate)
checkout = findViewById(R.id.editTextDate2)
editTextSearch = findViewById(R.id.et_search)
val myCalender = Calendar.getInstance()
val dateFormat = SimpleDateFormat("yyyy-MM-dd")
checkin.setText(dateFormat.format(myCalender.time))
checkout.setText(dateFormat.format(myCalender.time))
checkin.setOnClickListener {
DatePickerDialog(this#MainActivity,checkIndatePickerDialogListener,myCalender.get(Calendar.YEAR),myCalender.get(Calendar.MONTH),myCalender.get(Calendar.DAY_OF_MONTH)).show()
}
checkout.setOnClickListener {
DatePickerDialog(this#MainActivity,checkOutdatePickerDialogListener,myCalender.get(Calendar.YEAR),myCalender.get(Calendar.MONTH),myCalender.get(Calendar.DAY_OF_MONTH)).show()
}
//search text
editTextSearch.addTextChangedListener(object :TextWatcher{
override fun beforeTextChanged(p0: CharSequence?, p1: Int, p2: Int, p3: Int) {
}
override fun onTextChanged(p0: CharSequence?, p1: Int, p2: Int, p3: Int) {
TODO("Not yet implemented")
}
override fun afterTextChanged(p0: Editable?) {
fliterList(p0.toString())
}
})
}
private fun fliterList(filterItem: String) {
var templist:MutableList<DemoDataBaseItem> = ArrayList()
for (d in itemlist){
if (filterItem in d.localtion_city){
templist.add(d)
}
}
}
#SuppressLint("SimpleDateFormat")
val checkIndatePickerDialogListener = DatePickerDialog.OnDateSetListener{ _, year, month, day->
val myCalender = Calendar.getInstance()
myCalender.set(Calendar.YEAR,year)
myCalender.set(Calendar.MONTH,month)
myCalender.set(Calendar.DAY_OF_MONTH,day)
val dateFormat = SimpleDateFormat("yyyy-MM-dd")
checkin.setText(dateFormat.format(myCalender.time))
}
#SuppressLint("SimpleDateFormat")
val checkOutdatePickerDialogListener = DatePickerDialog.OnDateSetListener{ _, year, month, day->
val myCalender = Calendar.getInstance()
myCalender.set(Calendar.YEAR,year)
myCalender.set(Calendar.MONTH,month)
myCalender.set(Calendar.DAY_OF_MONTH,day)
val dateFormat = SimpleDateFormat("yyyy-MM-dd")
checkout.setText(dateFormat.format(myCalender.time))
}
fun getAllData(){
Api.retrofitService.getAllData().enqueue(object: Callback<List<DemoDataBaseItem>>{
override fun onResponse(
call: Call<List<DemoDataBaseItem>>,
response: Response<List<DemoDataBaseItem>>
) {
if(response.isSuccessful){
recyclerView = findViewById<RecyclerView>(R.id.recycler_view).apply{
myAdapter = MyAdapter(response.body()!!, this#MainActivity)
layoutManager = manager
adapter = myAdapter
}
}
}
override fun onFailure(call: Call<List<DemoDataBaseItem>>, t: Throwable) {
Log.d("fds","failed")
}
})
}
//additional url adding
override fun onClick(url: String) {
val intent = Intent(this, Webview::class.java)
val finalURL = "$url#checkin=${checkin.text.toString()}&checkout=${checkout.text.toString()}"
Log.i("URL-DaTE",finalURL)
intent.putExtra("url", finalURL)
startActivity(intent)
}
}
interface OnClickListener {
fun onClick(url: String)
}
I'm using an EditText view on the layout, so people can just type a country in and it'll be able to filter it and show it on the recyclerview.
I'm retrieving a list of lists from an API in JSON. This is how the response looks like.
{
"result":"success",
"documentation":"https://www.exchangerate-api.com/docs",
"terms_of_use":"https://www.exchangerate-api.com/terms"
"supported_codes":[
["AED","UAE Dirham"],
["AFN","Afghan Afghani"],
["ALL","Albanian Lek"],
["AMD","Armenian Dram"],
["ANG","Netherlands Antillian Guilder"],
["AOA","Angolan Kwanza"],
["ARS","Argentine Peso"],
["AUD","Australian Dollar"],
["AWG","Aruban Florin"],
["AZN","Azerbaijani Manat"],
["BAM","Bosnia and Herzegovina Convertible Mark"],
["BBD","Barbados Dollar"] etc. etc.
]
}
And this is the data class
data class CurrencyResponse(
#Json(name="documentation") val documentation: String,
#Json(name="result") val result: String,
#Json(name="supported_codes") val supported_codes: List<List<String>>,
#Json(name="terms_of_use") val terms_of_use: String
)
I managed to get the list of lists(supported_codes) but what I want now is to get the first index of each list(AED,AFN,ALL,etc.) and store them in a list, convert it to an array, then use that array to populate my spinners. Can anyone help me out with that?
Below is the relevant code.
Thank you.
CurrencyRepository.kt
package com.example.currencyconverter.data
import com.example.currencyconverter.BuildConfig
import javax.inject.Inject
import javax.inject.Singleton
#Singleton
class CurrencyRepository #Inject constructor(private val currencyApi: CurrencyApi) {
suspend fun getConversionRate(baseCurrency: String, toCurrency: String, amount: Double?): ConversionResponse {
return currencyApi.convert(BuildConfig.API_KEY, baseCurrency, toCurrency, amount)
}
suspend fun getCurrencies(): List<String> {
return currencyApi.getCurrencies(BuildConfig.API_KEY).supported_codes
}
}
CurrencyViewModel.kt
package com.example.currencyconverter.ui
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.example.currencyconverter.data.CurrencyRepository
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.launch
import javax.inject.Inject
#HiltViewModel
class CurrencyViewModel #Inject constructor(private val repository: CurrencyRepository): ViewModel() {
private val _conversionResult = MutableLiveData<String>()
val conversionResult: LiveData<String> = _conversionResult
private val _currencies = MutableLiveData<List<String>>()
val currencies: LiveData<List<String>> = _currencies
init {
getCurrencies()
}
fun getConversionRate(baseCurrency: String, toCurrency: String, amount: Double?) {
viewModelScope.launch {
_conversionResult.value = repository.getConversionRate(baseCurrency, toCurrency, amount).conversion_result
}
}
fun getCurrencies() {
viewModelScope.launch {
_currencies.value = repository.getCurrencies()
}
}
}
HomeFragment.kt
package com.example.currencyconverter.ui
import android.content.Context
import android.os.Bundle
import android.view.LayoutInflater
import android.view.MotionEvent
import android.view.View
import android.view.ViewGroup
import android.view.inputmethod.InputMethodManager
import android.widget.AdapterView
import android.widget.ArrayAdapter
import androidx.fragment.app.Fragment
import androidx.fragment.app.viewModels
import com.example.currencyconverter.R
import com.example.currencyconverter.databinding.FragmentHomeBinding
import dagger.hilt.android.AndroidEntryPoint
#AndroidEntryPoint
class HomeFragment : Fragment(R.layout.fragment_home) {
private val viewModel by viewModels<CurrencyViewModel>()
private var _binding: FragmentHomeBinding? = null
private val binding get() = _binding!!
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
// Inflate the layout for this fragment
return inflater.inflate(R.layout.fragment_home, container, false)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
_binding = FragmentHomeBinding.bind(view)
lateinit var firstCurrency: String
lateinit var secondCurrency: String
viewModel.currencies.observe(viewLifecycleOwner) {
binding.list.text = " ${it[0]} , ${it[1]} "
}
var currenciesFormat = arrayOf(viewModel.currencies.value?.get(0),
viewModel.currencies.value?.get(1)
)
binding.apply {
spinnerFirst.adapter = ArrayAdapter(requireContext(), android.R.layout.simple_spinner_item, currenciesFormat)
spinnerFirst.onItemSelectedListener = object : AdapterView.OnItemSelectedListener {
override fun onItemSelected(
adapterView: AdapterView<*>?,
view: View?,
position: Int,
id: Long
) {
firstCurrency = adapterView?.getItemAtPosition(position).toString()
}
override fun onNothingSelected(p0: AdapterView<*>?) {
}
}
spinnerSecond.onItemSelectedListener = object : AdapterView.OnItemSelectedListener {
override fun onItemSelected(
adapterView: AdapterView<*>?,
view: View?,
position: Int,
id: Long
) {
secondCurrency = adapterView?.getItemAtPosition(position).toString()
}
override fun onNothingSelected(p0: AdapterView<*>?) {
}
}
}
binding.constraintLayout.setOnTouchListener(object : View.OnTouchListener {
override fun onTouch(p0: View?, p1: MotionEvent?): Boolean {
closeKeyboard(p0)
return true
}
})
binding.button.setOnClickListener {
val stringInTextField = binding.amountText.text.toString()
val amount = stringInTextField.toDoubleOrNull()
if (amount == null) {
binding.resultText.text = " "
}
closeKeyboard(binding.amountText)
viewModel.getConversionRate(firstCurrency, secondCurrency, amount)
viewModel.conversionResult.observe(viewLifecycleOwner) {
binding.resultText.text = it
}
}
}
private fun closeKeyboard(view: View?) {
val imm =
view?.context?.getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
if (view != null) {
imm.hideSoftInputFromWindow(view.windowToken, 0)
}
}
}
You can use kotlin.collections's map function. https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.collections/map.html
fun main(){
val supportedCodes = listOf(
listOf("AED","UAE Dirham"),
listOf("AFN","Afghan Afghani"),
listOf("ALL","Albanian Lek")
)
val codes = supportedCodes.map { it[0] }
println(codes)
}
The output is a list containing just the first element from each nested list.
[AED, AFN, ALL]
Just after calling loadInitial method it automatically starts calling loadAfter method until and unless all pages are loaded and it gets an empty array from response. I am attaching my DataSource and DataSourceFactory code along with ViewModel function and PagedListAdapter.
PassbookDataSource.kt
package co.indiagold.gold.buy.loan.home
import androidx.paging.PageKeyedDataSource
import co.indiagold.gold.buy.loan.helper.GenericMethods.handleServerError
import co.indiagold.gold.buy.loan.helper.GenericResponse
import co.indiagold.gold.buy.loan.network.Repository
import co.indiagold.gold.buy.loan.network.ResultWrapper
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.launch
class PassbookDataSource(private val accessToken: String, private val assetName:String,private val viewModelScope: CoroutineScope) : PageKeyedDataSource<Int, PassbookResponseModel>() {
private val PAGE_SIZE = 20
override fun loadInitial(params: LoadInitialParams<Int>, callback: LoadInitialCallback<Int, PassbookResponseModel>) {
var result: ResultWrapper<GenericResponse<ArrayList<PassbookResponseModel>?>?>? = null
viewModelScope.launch {
result = Repository.getPassbookDetail(accessToken, assetName,1, PAGE_SIZE)
}.invokeOnCompletion {
if (result is ResultWrapper.Success) {
val successResult = result as ResultWrapper.Success
callback.onResult(successResult.value!!.result as MutableList<PassbookResponseModel>, null, 2)
} else {
it.handleServerError("vault/passbook")
}
}
}
override fun loadBefore(params: LoadParams<Int>, callback: LoadCallback<Int, PassbookResponseModel>) {
}
override fun loadAfter(params: LoadParams<Int>, callback: LoadCallback<Int, PassbookResponseModel>) {
var result: ResultWrapper<GenericResponse<ArrayList<PassbookResponseModel>?>?>? = null
viewModelScope.launch {
result = Repository.getPassbookDetail(accessToken,assetName, params.key, PAGE_SIZE)
}.invokeOnCompletion {
if (result is ResultWrapper.Success) {
val successResult = result as ResultWrapper.Success
callback.onResult(successResult.value!!.result as MutableList<PassbookResponseModel>, params.key+1)
} else {
it.handleServerError("vault/passbook")
}
}
}
}
PassbookDataSourceFactory.kt
class PassbookDataSourceFactory(private val accessToken:String,private val assetName:String,private val viewModelScope:CoroutineScope) : DataSource.Factory<Int,PassbookResponseModel>(){
val orderListMLD = MutableLiveData<PassbookDataSource>(null)
var orderDataSource : PassbookDataSource? = null
override fun create(): DataSource<Int, PassbookResponseModel> {
orderDataSource = PassbookDataSource(accessToken,assetName,viewModelScope)
orderListMLD.postValue(orderDataSource)
return orderDataSource!!
}
}
Viewmodel Function
fun getPassbookDetail(assetName: String) {
val accessToken = GenericMethods.getDataFromPreferences(Constants.SHARED_PREFS.LOGIN_PREFERENCES, Constants.SHARED_PREFS.ACCESS_TOKEN)
if (accessToken.isNullOrEmpty()) {
handleNullAccessTokenCase()
} else {
val factory = PassbookDataSourceFactory(accessToken, assetName, viewModelScope)
val config = PagedList.Config.Builder()
.setEnablePlaceholders(true)
.setInitialLoadSizeHint(10)
.setPrefetchDistance(4)
.build()
val executor = Executors.newFixedThreadPool(5)
passbookResponseMLD = LivePagedListBuilder(factory, config)
.setFetchExecutor(executor)
.build()
}
}
PassbookAdapter.kt
package co.indiagold.gold.buy.loan.home
import android.app.Activity
import android.content.Intent
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.paging.PagedList
import androidx.paging.PagedListAdapter
import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.RecyclerView
import co.indiagold.gold.buy.loan.databinding.CardPassbookBinding
import co.indiagold.gold.buy.loan.helper.Constants
import co.indiagold.gold.buy.loan.orders.OrdersActivity
import kotlinx.android.synthetic.main.card_passbook.view.*
class PassbookAdapter(private val activity: Activity,private val emptyListListener: EmptyListListener) : PagedListAdapter<PassbookResponseModel,PassbookAdapter.MyHolder>(PASSBOOK_DIFF_CALLBACK) {
private lateinit var binding: CardPassbookBinding
class MyHolder(itemView : View) : RecyclerView.ViewHolder(itemView)
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyHolder {
binding = CardPassbookBinding.inflate(LayoutInflater.from(parent.context))
emptyListListener.onListLoad(currentList?.size)
return MyHolder(binding.root)
}
override fun onCurrentListChanged(previousList: PagedList<PassbookResponseModel>?, currentList: PagedList<PassbookResponseModel>?) {
super.onCurrentListChanged(previousList, currentList)
emptyListListener.onListLoad(currentList?.size)
}
override fun onBindViewHolder(holder: MyHolder, position: Int) {
binding.passbookEntry = getItem(position)
holder.itemView.passbookRootLayout.setOnClickListener {
val intent = Intent(activity,OrdersActivity::class.java)
intent.putExtra(Constants.BUNDLE_STRINGS.ORDER_ID,getItem(position)?.referenceId)
activity.startActivity(intent)
}
}
override fun getItemId(position: Int): Long {
return getItem(position)!!.createdAt
}
/*override fun getItemViewType(position: Int): Int {
return position
}*/
}
private val PASSBOOK_DIFF_CALLBACK = object : DiffUtil.ItemCallback<PassbookResponseModel>(){
override fun areItemsTheSame(oldItem: PassbookResponseModel, newItem: PassbookResponseModel): Boolean {
return oldItem.createdAt == newItem.createdAt
}
override fun areContentsTheSame(oldItem: PassbookResponseModel, newItem: PassbookResponseModel): Boolean {
return oldItem == newItem
}
}
The issue is, that you may put recycler view inside NextedScrollview or ScrollView, by removing scrollview it will work fine