I want to make an application with Kotlin and Google Maps in which the user can add markers on the Google Map and that these markers are sent to Firebase and other users can see those same markers on the map.
This is what I have done but it is not working well for me.
package com.racrapa.displayinglocationapp
import android.content.ContentValues
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.util.Log
import android.widget.Toast
import androidx.annotation.NonNull
import com.google.android.gms.maps.CameraUpdateFactory
import com.google.android.gms.maps.GoogleMap
import com.google.android.gms.maps.OnMapReadyCallback
import com.google.android.gms.maps.SupportMapFragment
import com.google.android.gms.maps.model.BitmapDescriptorFactory
import com.google.android.gms.maps.model.LatLng
import com.google.android.gms.maps.model.Marker
import com.google.android.gms.maps.model.MarkerOptions
import com.google.firebase.database.*
import com.google.firebase.database.ktx.database
import com.google.firebase.ktx.Firebase
import com.google.firebase.database.ValueEventListener
import com.google.firebase.database.DataSnapshot
import com.google.firebase.database.DatabaseError
import com.google.firebase.database.DatabaseReference
private const val TAG = "MapsActivity"
class MapsActivity : AppCompatActivity(), OnMapReadyCallback {
private lateinit var mMap: GoogleMap
public lateinit var databaseRef: DatabaseReference
private lateinit var database: DatabaseReference
private var markers: MutableList<Marker> = mutableListOf()
private var postListener: ValueEventListener? = null
var mDatabase: DatabaseReference? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_maps)
// Obtain the SupportMapFragment and get notified when the map is ready to be used.
val mapFragment = supportFragmentManager
.findFragmentById(R.id.map) as SupportMapFragment
mapFragment.getMapAsync(this)
databaseRef = Firebase.database.reference
mDatabase = FirebaseDatabase.getInstance().reference.child("userlocation")
database = Firebase.database.reference
}
This is to get the data from Firebase
public override fun onStart() {
super.onStart()
var postListener: ValueEventListener = object : ValueEventListener {
override fun onDataChange(dataSnapshot: DataSnapshot) {
if (dataSnapshot.exists()) {
val locationlogging = dataSnapshot.child("userlocation").getValue(LocationLogging::class.java)
var driverLat=locationlogging?.Latitude
var driverLong=locationlogging?.Longitude
if (driverLat != null && driverLong != null) {
val driverLoc = LatLng(driverLat, driverLong)
val markerOptions = MarkerOptions().position(driverLoc).title("Driver")
mMap.addMarker(markerOptions)
Toast.makeText(applicationContext, "Locations accessed from the database", Toast.LENGTH_LONG).show()
}
}
}
override fun onCancelled(error: DatabaseError) {}
}
databaseRef.addValueEventListener(postListener)
}
override fun onMapReady(googleMap: GoogleMap) {
mMap = googleMap
mMap.setOnMapLongClickListener { latLng ->
Log.i(TAG, "onMapLongClickListener")
setMapLongClick(latLng)
}
}
This is the function of sending coordinates to Firebase
private fun setMapLongClick(latLng: LatLng) {
if (latLng != null) {
Log.e("Latitude: ", latLng.latitude.toString() + "longitude: " + latLng.longitude)
val latlang: MutableMap<String, Double> = HashMap()
latlang["latitude"] = latLng.latitude
latlang["longitude"] = latLng.longitude
mDatabase!!.child("userlocation").push().setValue(latlang)
onStart()
}
}
}
Related
I've been trying to figure out how to connect firebase and mapbox together since I am new and trying to learn more about it. Is there any way I can do so the markers in the mapbox will follow the firebase realtime data?
Below is the code written in kotlin.
I tried searching around but it seems like there are rare guides for mapbox.
package com.example.s_tore
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.graphics.Bitmap
import android.graphics.Canvas
import android.graphics.drawable.BitmapDrawable
import android.graphics.drawable.Drawable
import androidx.appcompat.content.res.AppCompatResources
import com.google.firebase.database.DatabaseReference
import com.google.firebase.database.FirebaseDatabase
import com.mapbox.geojson.Point.fromLngLat
import com.mapbox.maps.CameraOptions
import com.mapbox.maps.MapView
import com.mapbox.maps.Style
import com.mapbox.maps.plugin.annotation.AnnotationConfig
import com.mapbox.maps.plugin.annotation.AnnotationPlugin
import com.mapbox.maps.plugin.annotation.annotations
import com.mapbox.maps.plugin.annotation.generated.PointAnnotationManager
import com.mapbox.maps.plugin.annotation.generated.PointAnnotationOptions
import com.mapbox.maps.plugin.annotation.generated.createPointAnnotationManager
class smap : AppCompatActivity() {
var mapView: MapView? = null
private lateinit var database: DatabaseReference
var annotationApi : AnnotationPlugin? = null
lateinit var annotationConfig: AnnotationConfig
var layer1DD = "map_annotation"
var pointAnnotationManager : PointAnnotationManager? = null
var markerlist : ArrayList<PointAnnotationOptions> = ArrayList()
var latitudeList : ArrayList<Double> = ArrayList()
var longitudeList : ArrayList<Double> = ArrayList()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_smap)
mapView = findViewById(R.id.maps)
createDummyAList()
mapView?.getMapboxMap()!!.loadStyleUri(
Style.MAPBOX_STREETS,
object : Style.OnStyleLoaded{
override fun onStyleLoaded(style: Style) {
ZoomCamera()
annotationApi = mapView?.annotations
annotationConfig = AnnotationConfig(
layerId = layer1DD
)
pointAnnotationManager = annotationApi?.createPointAnnotationManager(annotationConfig)
createMarkerOnMap()
}
}
)
}
private fun ZoomCamera(){
mapView!!.getMapboxMap().setCamera(
CameraOptions.Builder().center(fromLngLat(122.061213, 6.913414))
.zoom(17.0)
.build())
}
private fun createDummyAList(){
latitudeList.add(6.914586)
longitudeList.add(122.061069)
}
private fun createMarkerOnMap(){
val bitmap = convertDrawabletoBitMap(AppCompatResources.getDrawable(this, R.drawable.locationmark))
for (i in 0 until 1000){
val pointAnnotationOptions : PointAnnotationOptions = PointAnnotationOptions()
.withPoint(fromLngLat(longitudeList.get(i),latitudeList.get(i)))
.withIconImage(bitmap!!)
markerlist.add(pointAnnotationOptions)
}
pointAnnotationManager?.create(markerlist)
}
private fun convertDrawabletoBitMap(sourceDrawable: Drawable?):Bitmap{
return if(sourceDrawable is BitmapDrawable){
sourceDrawable.bitmap
}else{
val constatState = sourceDrawable?.constantState
val drawable = constatState?.newDrawable()?.mutate()
val bitmap : Bitmap = Bitmap.createBitmap(
drawable!!.intrinsicWidth,drawable!!.intrinsicHeight,
Bitmap.Config.ARGB_8888
)
val canvas = Canvas(bitmap)
drawable.setBounds(0, 0, canvas.width, canvas.height)
drawable.draw(canvas)
bitmap
}
}
}
I am simply trying to setup the ClickListener for changing the user name. Google's tutorials emphasize fragments, which for the moment feels like overkill.
The app crashes when I navigate to the ManageUsers activity. Based on what I've seen in other examples and the Android documentation, I thought I had the View Binding set up properly.
UserListAdapter.kt
package com.neillbarrett.debitsandcredits
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
class UserListAdapter(private val userSelect: (UsersTable?) -> Unit) :
ListAdapter<UsersTable, UserListAdapter.UserViewHolder>(UsersComparator()) {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): UserListAdapter.UserViewHolder {
val view = LayoutInflater.from(parent.context)
.inflate(R.layout.activity_manage_users, parent, false)
return UserViewHolder.create(parent)
}
override fun onBindViewHolder(holder: UserViewHolder, position: Int) {
val current = getItem(position)
holder.bind(current, userSelect)
}
class UserViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
private val userView: TextView = itemView.findViewById(R.id.tv_UserName)
// var text: String? = null
fun bind(usersTable: UsersTable?, userSelect: (UsersTable?) -> Unit, text: String = usersTable?.userName.toString()) {
userView.text = text
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 {
val view: View = LayoutInflater.from(parent.context)
.inflate(R.layout.activity_manage_users, parent, false)
return UserViewHolder(view)
}
}
}
class UsersComparator : DiffUtil.ItemCallback<UsersTable>() {
override fun areItemsTheSame(oldItem: UsersTable, newItem: UsersTable): Boolean {
return oldItem.userName == newItem.userName
}
override fun areContentsTheSame(oldItem: UsersTable, newItem: UsersTable): Boolean {
return oldItem == newItem
}
}
}
ManageUsers.kt
package com.neillbarrett.debitsandcredits
import android.content.Context
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.text.TextUtils
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
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityManageUsersBinding.inflate(layoutInflater)
val view = binding.root
setContentView(view)
//setContentView(R.layout.activity_manage_users)
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
binding.recViewUserList.layoutManager = LinearLayoutManager(this)
//recyclerView.adapter = adapter
//recyclerView.layoutManager = LinearLayoutManager(this)
userViewModel.allUsers.observe(this, Observer() {user ->
user?.let { adapter.submitList(it) }
})
val btnAddUser = findViewById<Button>(R.id.btn_AddUser)
binding.btnAddUser.setOnClickListener {
// 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.i("Add user button", "Username put into newUser")
userViewModel.insertUser(UsersTable(0, newUser))
// Toast.makeText(this, "Username added to table", Toast.LENGTH_SHORT).show()
// Log.i("Add user button", "Username added to table")
}
}
val btnChangeUser = findViewById<Button>(R.id.btn_ChangeUserName)
binding.btnChangeUserName.setOnClickListener {
// 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 androidx.lifecycle.*
import com.neillbarrett.debitsandcredits.database.UsersTable
import kotlinx.coroutines.launch
import java.lang.IllegalArgumentException
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)
//repository.insertUser(usersTable = List<UsersTable>())
//repository.insertUser(UsersTable(0, userName = user.userName))
}
fun updateUser(user: UsersTable) = viewModelScope.launch {
repository.updateUser(user)
}
fun deleteUser(user: UsersTable) = viewModelScope.launch {
repository.deleteUser(user)
}
}
class UserViewModelFactory(private val repository: UserRepository) : ViewModelProvider.Factory{
override fun <T : ViewModel> create(modelClass: Class<T>): T {
if (modelClass.isAssignableFrom(UserViewModel::class.java)) {
#Suppress("UNCHECKED_CAST")
return UserViewModel(repository) as T
}
throw IllegalArgumentException("Unknown ViewModel class")
}
}
The LogCat shows this:
2022-11-25 11:43:59.427 8217-8217/com.neillbarrett.debitsandcredits E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.neillbarrett.debitsandcredits, PID: 8217
java.lang.NullPointerException: itemView.findViewById(R.id.tv_UserName) must not be null
at com.neillbarrett.debitsandcredits.UserListAdapter$UserViewHolder.<init>(UserListAdapter.kt:30)
at com.neillbarrett.debitsandcredits.UserListAdapter$UserViewHolder$Companion.create(UserListAdapter.kt:51)
at com.neillbarrett.debitsandcredits.UserListAdapter.onCreateViewHolder(UserListAdapter.kt:21)
at com.neillbarrett.debitsandcredits.UserListAdapter.onCreateViewHolder(UserListAdapter.kt:15)
at androidx.recyclerview.widget.RecyclerView$Adapter.createViewHolder(RecyclerView.java:7078)
at androidx.recyclerview.widget.RecyclerView$Recycler.tryGetViewHolderForPositionByDeadline(RecyclerView.java:6235)
at androidx.recyclerview.widget.RecyclerView$Recycler.getViewForPosition(RecyclerView.java:6118)
at androidx.recyclerview.widget.RecyclerView$Recycler.getViewForPosition(RecyclerView.java:6114)
at androidx.recyclerview.widget.LinearLayoutManager$LayoutState.next(LinearLayoutManager.java:2303)
at androidx.recyclerview.widget.LinearLayoutManager.layoutChunk(LinearLayoutManager.java:1627)
at androidx.recyclerview.widget.LinearLayoutManager.fill(LinearLayoutManager.java:1587)
at androidx.recyclerview.widget.LinearLayoutManager.onLayoutChildren(LinearLayoutManager.java:665)
at androidx.recyclerview.widget.RecyclerView.dispatchLayoutStep2(RecyclerView.java:4134)
at androidx.recyclerview.widget.RecyclerView.onMeasure(RecyclerView.java:3540)
at android.view.View.measure(View.java:26411)
at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:7845)
at android.widget.LinearLayout.measureChildBeforeLayout(LinearLayout.java:1552)
at android.widget.LinearLayout.measureVertical(LinearLayout.java:842)
at android.widget.LinearLayout.onMeasure(LinearLayout.java:721)
at android.view.View.measure(View.java:26411)
at androidx.constraintlayout.widget.ConstraintLayout$Measurer.measure(ConstraintLayout.java:811)
at androidx.constraintlayout.core.widgets.analyzer.BasicMeasure.measure(BasicMeasure.java:466)
at androidx.constraintlayout.core.widgets.analyzer.BasicMeasure.measureChildren(BasicMeasure.java:134)
at androidx.constraintlayout.core.widgets.analyzer.BasicMeasure.solverMeasure(BasicMeasure.java:278)
at androidx.constraintlayout.core.widgets.ConstraintWidgetContainer.measure(ConstraintWidgetContainer.java:120)
at androidx.constraintlayout.widget.ConstraintLayout.resolveSystem(ConstraintLayout.java:1594)
at androidx.constraintlayout.widget.ConstraintLayout.onMeasure(ConstraintLayout.java:1708)
at android.view.View.measure(View.java:26411)
at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:7845)
at android.widget.FrameLayout.onMeasure(FrameLayout.java:194)
at androidx.appcompat.widget.ContentFrameLayout.onMeasure(ContentFrameLayout.java:145)
at android.view.View.measure(View.java:26411)
at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:7845)
at androidx.appcompat.widget.ActionBarOverlayLayout.onMeasure(ActionBarOverlayLayout.java:496)
at android.view.View.measure(View.java:26411)
at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:7845)
at android.widget.FrameLayout.onMeasure(FrameLayout.java:194)
at android.view.View.measure(View.java:26411)
at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:7845)
at android.widget.LinearLayout.measureChildBeforeLayout(LinearLayout.java:1552)
at android.widget.LinearLayout.measureVertical(LinearLayout.java:842)
at android.widget.LinearLayout.onMeasure(LinearLayout.java:721)
at android.view.View.measure(View.java:26411)
at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:7845)
at android.widget.FrameLayout.onMeasure(FrameLayout.java:194)
at com.android.internal.policy.DecorView.onMeasure(DecorView.java:1050)
at android.view.View.measure(View.java:26411)
at android.view.ViewRootImpl.performMeasure(ViewRootImpl.java:3635)
It's clear I'm missing something, but I have no idea what.
I'm struggling with both a dilemma and an issue here. The dilemma is that I want to keep the ManagerUsers screen simple, but Google is pushing fragments. I can see a use for fragments for this screen later on, but for now, it seems like overkill. Should I use them anyway?
Second, if I do avoid fragments for now, I am struggling to finish setting up the ClickListener to simply change the name of the user in the ListView. Kotlin seems to have at least 6 different methods for doing most things, making it confusing to figure this out. Google's tutorials force you to use fragments, and others' tutorials don't really cover what I'm trying to do. When I navigate to the Manage Users activity, the app crashes. LogCat shows that userSelect needs to be initialized. I have no idea how to do that.
UserListAdapter.kt
package com.neillbarrett.debitsandcredits
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.TextView
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
class UserListAdapter(private val userSelect: (UsersTable?) -> Unit) :
ListAdapter<UsersTable, UserListAdapter.UserViewHolder>(UsersComparator()) {
override fun onBindViewHolder(holder: UserViewHolder, position: Int) {
val current = getItem(position)
holder.bind(current, userSelect)
}
class UserViewHolder(private val binding: ActivityManageUsersBinding) : RecyclerView.ViewHolder(binding.root) {
fun bind(usersTable: UsersTable?, userSelect: (UsersTable?) -> Unit) {
binding.root.setOnClickListener( View.OnClickListener {
userSelect(usersTable)
binding.etEditName.setText(R.layout.activity_list_of_users.toString())
})
}
companion object {
fun create(parent: ViewGroup) : UserViewHolder {
val view: View = LayoutInflater.from(parent.context)
.inflate(R.layout.activity_manage_users, parent, false)
return UserViewHolder(ActivityManageUsersBinding.bind(view))
}
}
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): UserViewHolder {
val view = LayoutInflater.from(parent.context)
.inflate(R.layout.activity_manage_users, parent, false)
return UserViewHolder.create(parent)
}
class UsersComparator : DiffUtil.ItemCallback<UsersTable>() {
override fun areItemsTheSame(oldItem: UsersTable, newItem: UsersTable): Boolean {
return oldItem == newItem
}
override fun areContentsTheSame(oldItem: UsersTable, newItem: UsersTable): Boolean {
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.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
lateinit var userSelect: ((UsersTable?) -> Unit)
var position: Long = 0
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityManageUsersBinding.inflate(layoutInflater)
val view = binding.root
setContentView(view)
//setContentView(R.layout.activity_manage_users)
val userViewModel: UserViewModel by viewModels {
UserViewModelFactory((application as CreditsAndDebitsApp).repository)
}
recyclerView = findViewById(R.id.rec_view_userList)
editTextAddUser = findViewById(R.id.et_UserName)
editTextChangeUser = findViewById(R.id.et_Edit_Name)
val adapter = UserListAdapter(userSelect)
binding.recViewUserList.adapter = adapter
recyclerView.adapter = adapter
recyclerView.layoutManager = LinearLayoutManager(this)
userViewModel.allUsers.observe(this, Observer() {user ->
user?.let { adapter.submitList(it) }
})
val btnAddUser = findViewById<Button>(R.id.btn_AddUser)
btnAddUser.setOnClickListener {
if (TextUtils.isEmpty(editTextAddUser.text)) {
Toast.makeText(this, "User name cannot be empty", Toast.LENGTH_SHORT).show()
} else {
newUser = editTextAddUser.text.toString()
userViewModel.insertUser(UsersTable(0, newUser))
}
}
val btnChangeUser = findViewById<Button>(R.id.btn_ChangeUserName)
btnChangeUser.setOnClickListener {
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 androidx.lifecycle.*
import com.neillbarrett.debitsandcredits.database.UsersTable
import kotlinx.coroutines.launch
import java.lang.IllegalArgumentException
class UserViewModel(private val repository: UserRepository) : ViewModel() {
val allUsers: LiveData<List<UsersTable>> = repository.allUsers.asLiveData()
fun insertUser(user: UsersTable) = viewModelScope.launch {
repository.insertUser(user)
}
fun updateUser(user: UsersTable) = viewModelScope.launch {
repository.updateUser(user)
}
fun deleteUser(user: UsersTable) = viewModelScope.launch {
repository.deleteUser(user)
}
}
class UserViewModelFactory(private val repository: UserRepository) : ViewModelProvider.Factory{
override fun <T : ViewModel> create(modelClass: Class<T>): T {
if (modelClass.isAssignableFrom(UserViewModel::class.java)) {
#Suppress("UNCHECKED_CAST")
return UserViewModel(repository) as T
}
throw IllegalArgumentException("Unknown ViewModel class")
}
}
If you check your ManageUsers activity implementation
lateinit var userSelect: ((UsersTable?) -> Unit)
userSelect is lateinit and it must be initialised before you pass the reference to UserListAdapter adapter. Something like this
userSelect = {
// do something
}
before passing to adapter, or
val userSelect: ((UsersTable?) -> Unit) = {
}
at the time of declaration.
I have an adapter for my RecyclerView where I program that when I click on the element (of my RecyclerView) it executes an Intent with a putExtra to take me to another activity, the variable that contains my putExtra comes from the element that I clicked, but now I need to add a More variable that comes from the activity. The issue is that I don't know how to send it from the adapter.
this is my adapter.
package com.example.atipicoapp
import android.app.Activity
import android.content.Intent
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.AdapterView
import android.widget.LinearLayout
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>
) : RecyclerView.Adapter<MyAdapter.MyViewHolder>() {
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()
when(holder){
is MyViewHolder -> {
holder.bind(platoList[position])
}
}
}
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 platoTouch: LinearLayout = itemView.findViewById(R.id.platoTouch)
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){
platoTouch.setOnClickListener{
intent.putExtra("id", platoName.text.toString())
mActivity.startActivity(intent)
}
}
}
}
And this is my Activity which contains my RecyclerView and the variable I want to send.
package com.example.atipicoapp
import android.content.Intent
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.util.Log
import android.widget.Toast
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.google.firebase.firestore.*
import kotlinx.android.synthetic.main.activity_main.*
import kotlinx.android.synthetic.main.activity_menu_atipico.*
class MenuAtipicoActivity : AppCompatActivity() {
private lateinit var recyclerView: RecyclerView
private lateinit var platoArrayList: ArrayList<Plato>
private lateinit var myAdapter: MyAdapter
private lateinit var db: FirebaseFirestore
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_menu_atipico)
recyclerView = findViewById(R.id.recyclerView)
recyclerView.layoutManager = LinearLayoutManager(this)
recyclerView.setHasFixedSize(true)
platoArrayList = arrayListOf()
myAdapter = MyAdapter(platoArrayList)
recyclerView.adapter = myAdapter
pedidoId = intent.extras?.getString("pedidoId") //This is the variable I need to send
EventChangeListener()
Setup()
}
private fun EventChangeListener() {
db = FirebaseFirestore.getInstance()
db.collection("Platos").addSnapshotListener(object : EventListener<QuerySnapshot> {
override fun onEvent(
value: QuerySnapshot?,
error: FirebaseFirestoreException?
) {
if (error != null) {
Log.e("Firestore Error", error.message.toString())
return
}
for (dc: DocumentChange in value?.documentChanges!!) {
if (dc.type == DocumentChange.Type.ADDED) {
platoArrayList.add(dc.document.toObject(Plato::class.java))
}
}
myAdapter.notifyDataSetChanged()
}
})
}
private fun Setup() {
botonAceptar.setOnClickListener {
val SlotIntent = Intent(this, SlotActivity::class.java).apply {
}
startActivity(SlotIntent)
}
}
}
How can I send the variable if the Intent is executed from the Adapter?
Or... If it is not recommended to send intent from my Adapter, how can I send them from the activity?
Knowing that I have to carry a variable that is in the item of the RecyclerView.
Thanks for your help <3
Firstly, create a MyAdapter constructor where you pass arrayList as well as pedidoId like, your MyAdapter should be something like below:
MyAdapter.class
class MyAdapter(private val platoList : ArrayList<Plato>, val pedidoId:String
) : RecyclerView.Adapter<MyAdapter.MyViewHolder>() {
........
.....
....
//in your bind(..) method
fun bind(plato: Plato){
platoTouch.setOnClickListener{
intent.putExtra("id", platoName.text.toString())
intent.putExtra("pedidoId", pedidoId)
mActivity.startActivity(intent)
}
}
}
And, in your MenuAtipicoActivity you need to do something like:
MenuAtipicoActivity
class MenuAtipicoActivity : AppCompatActivity() {
...............
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_menu_atipico)
recyclerView = findViewById(R.id.recyclerView)
recyclerView.layoutManager = LinearLayoutManager(this)
recyclerView.setHasFixedSize(true)
platoArrayList = arrayListOf()
pedidoId = intent.extras?.getString("pedidoId") //This is the variable I need to send
myAdapter = MyAdapter(platoArrayList,pedidoId)
recyclerView.adapter = myAdapter
EventChangeListener()
Setup()
}
..........
........
}
i'm a beginner in programming so forgive me if my code is messy. I need some help to complete my program.
I'm using recyclerview to retrieve some data from realtime database and i want to add edit and delete button in the recyclerview to edit and delete some data.
I already create the button in the xml file, i just don't know how to code the button. I already tried some tutorial from youtube and refer to some code in some others post on stack overflow but most of the time the code will either give me an error or the button will not do anything. I've included my code and database structure below:
DoctorViewAdapter.kt
package com.example.patientmonitor
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.webkit.WebView
import android.webkit.WebViewClient
import android.widget.TextView
import androidx.recyclerview.widget.RecyclerView
import com.android.volley.Request
import com.android.volley.Response
import com.android.volley.toolbox.StringRequest
import com.android.volley.toolbox.Volley
import java.util.ArrayList
import kotlin.concurrent.fixedRateTimer
class DoctorViewAdapter(private val patientList : ArrayList<doctorViewPatient>) : RecyclerView.Adapter<DoctorViewAdapter.DoctorViewHolder>() {
//WebView
private lateinit var pulseGraph: WebView
private lateinit var temperatureGraph: WebView
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): DoctorViewHolder {
val itemView = LayoutInflater.from(parent.context).inflate(R.layout.patient_item,parent,false)
return DoctorViewHolder(itemView)
pulseGraph.webViewClient = WebViewClient()
temperatureGraph.webViewClient = WebViewClient()
pulseGraph.settings.javaScriptEnabled = true
temperatureGraph.settings.javaScriptEnabled = true
pulseGraph.settings.builtInZoomControls = true
temperatureGraph.settings.builtInZoomControls = true
}
override fun onBindViewHolder(holder: DoctorViewHolder, position: Int) {
val currentitem = patientList[position]
holder.userID.text = currentitem.userID
holder.fullName.text = currentitem.fullName
holder.userEmail.text = currentitem.email
holder.phoneNumber.text = currentitem.phoneNumber
var pulseValue = "null"
var temperatureValue = "null"
//get pulse value from thingspeak and set to text view
val pulsequeue = Volley.newRequestQueue(holder.pulseValue.context)
val temperaturequeue = Volley.newRequestQueue(holder.temperatureValue.context)
val pulseRequest = StringRequest(Request.Method.GET, currentitem.pulseValue as String?,
Response.Listener<String> { response ->
pulseValue = response
holder.pulseValue.text = pulseValue
}, Response.ErrorListener {
// didn't work
})
//get temperature value from thingspeak and set to text view
val tempRequest = StringRequest(Request.Method.GET, currentitem.temperatureValue as String?,
Response.Listener<String> { response ->
temperatureValue = response
holder.temperatureValue.text = temperatureValue
}, Response.ErrorListener {
// didn't work
})
//get thingspeak data every 1 second
fixedRateTimer("timer", false, 0L, 1000) {
pulsequeue.add(pulseRequest)
temperaturequeue.add(tempRequest)
}
}
override fun getItemCount(): Int {
return patientList.size
}
class DoctorViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView){
val userID : TextView = itemView.findViewById(R.id.tvUserID)
val fullName : TextView = itemView.findViewById(R.id.tvFullName)
val userEmail : TextView = itemView.findViewById(R.id.tvEmail)
val phoneNumber : TextView = itemView.findViewById(R.id.tvPhoneNumber)
val pulseValue : TextView = itemView.findViewById(R.id.tvPulseValue)
val temperatureValue : TextView = itemView.findViewById(R.id.tvTemperatureValue)
}
}
PatientlistActivity.kt
package com.example.patientmonitor
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.webkit.WebView
import androidx.appcompat.app.ActionBar
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.google.android.material.floatingactionbutton.FloatingActionButton
import com.google.firebase.database.*
class PatientListActivity : AppCompatActivity() {
private lateinit var dbref : DatabaseReference
private lateinit var userRecyclerview : RecyclerView
private lateinit var userArrayList : ArrayList<doctorViewPatient>
private lateinit var viewPatientBtn : FloatingActionButton
private lateinit var doctorViewAdapter : DoctorViewAdapter
//ActionBar
private lateinit var actionBar: ActionBar
//WebView
private lateinit var pulseView: WebView
private lateinit var tempView: WebView
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_patient_list)
//configure Actionbar //enable back button
actionBar = supportActionBar!!
actionBar.title = "View Patients"
actionBar.setDisplayHomeAsUpEnabled(true)
actionBar.setDisplayShowHomeEnabled(true)
userRecyclerview = findViewById(R.id.patientList)
userRecyclerview.layoutManager = LinearLayoutManager(this)
userRecyclerview.setHasFixedSize(true)
userArrayList = arrayListOf<doctorViewPatient>()
getUserData()
}
private fun getUserData() {
//get intent
val assignedDoctor = intent.getStringExtra("doctorID")
val currentUserAccess = intent.getStringExtra("currentUserAccess")
dbref = FirebaseDatabase.getInstance().getReference("Users")
dbref.orderByChild("assignedDoctor").equalTo(assignedDoctor).addListenerForSingleValueEvent(object : ValueEventListener{
override fun onDataChange(snapshot: DataSnapshot) {
if (snapshot.exists()){
for (userSnapshot in snapshot.children){
val user = userSnapshot.getValue(doctorViewPatient::class.java)
userArrayList.add(user!!)
}
userRecyclerview.adapter = DoctorViewAdapter(userArrayList)
}
}
override fun onCancelled(error: DatabaseError) {
TODO("Not yet implemented")
}
})
}
}
This is my data class
doctorViewPatient.kt
data class doctorViewPatient(val userID : String? =null,
val fullName : String? =null,
val email : String? =null,
val phoneNumber : String? =null,
val pulseValue : String? = null,
val temperatureValue : String? = null)
This is my Realtime Database Structure