When I just added it, everything is fine, but when I restart the application, only textViews is displayed. There is an empty space instead of an imageView.
My recycler adapter:
class PersonAdapter(options: FirebaseRecyclerOptions<Outfit?>) :
FirebaseRecyclerAdapter<Outfit, personsViewHolder>(options) {
override fun onBindViewHolder(holder: personsViewHolder, position: Int, model: Outfit) {
holder.name.text = model.name
holder.brand.text = model.brand
holder.size.text = model.size
holder.comment.text = model.comment
holder.price.text = model.price
Picasso.get().load(model.imageUrl).into(holder.imageView) //Glide analog
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): personsViewHolder {
val view = LayoutInflater.from(parent.context)
.inflate(R.layout.recycler_item, parent, false)
return personsViewHolder(view)
}
class personsViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
var name: TextView = itemView.findViewById(R.id.tv_name_value)
var brand: TextView = itemView.findViewById(R.id.tv_brand_value)
var size: TextView = itemView.findViewById(R.id.tv_size_value)
var comment: TextView = itemView.findViewById(R.id.tv_comment_value)
var price: TextView = itemView.findViewById(R.id.tv_price_value)
var imageView: ImageView = itemView.findViewById(R.id.imageView)
}
}
Class what add to database:
private var dataBase: DatabaseReference? = null
private lateinit var outfitImage: ImageView
private var imageUri: Uri? = null
private var id: Int = 0
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_add_outfit)
nameView = findViewById(R.id.et_outfitName)
brandView = findViewById(R.id.et_outfitBrand)
sizeView = findViewById(R.id.et_outfitSize)
commentView = findViewById(R.id.et_outfitComment)
priceView = findViewById(R.id.et_outfitPrice)
btnAdd = findViewById(R.id.btn_addOutfit)
btnLoad = findViewById(R.id.btn_load)
btnAdd?.setOnClickListener {
onClickSave()
Toast.makeText(this, "Added to database", Toast.LENGTH_SHORT).show()
startActivity(Intent(this, MainActivity::class.java))
}
btnLoad?.setOnClickListener {
onClickRead()
}
outfitImage = findViewById(R.id.iv_upload_image)
outfitImage.setOnClickListener {
chooseImage()
}
}
//SAVE BUTTON
private fun onClickSave() {
dataBase = FirebaseDatabase.getInstance().getReference(UUID.randomUUID().toString())
dataBase?.setValue(
Outfit(
nameView?.text.toString(),
brandView?.text.toString(),
sizeView?.text.toString(),
commentView?.text.toString(),
priceView?.text.toString(),
imageUri.toString()
)
)
uploadImageToFirebase(imageUri!!)
}
//----------------UPLOAD IMAGE-----------------------
private fun chooseImage() {
val intent = Intent()
intent.type = "image/*"
intent.action = Intent.ACTION_GET_CONTENT
startActivityForResult(
Intent.createChooser(intent, "Please select..."), 1
)
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
if (requestCode == 1 && resultCode == Activity.RESULT_OK && data != null && data.data != null) {
// Get the Uri of data
imageUri = data.data
outfitImage.setImageURI(imageUri)
}
}
private fun uploadImageToFirebase(imageUri: Uri) {
val pd: ProgressDialog = ProgressDialog(this)
pd.setTitle("Uploading image...")
pd.show()
val fileName = UUID.randomUUID().toString()
val refStorage = FirebaseStorage.getInstance().reference.child("images/$fileName")
refStorage.putFile(imageUri)
.addOnSuccessListener { taskSnapshot ->
taskSnapshot.storage.downloadUrl.addOnSuccessListener {
pd.dismiss()
Snackbar.make(
findViewById(android.R.id.content),
"Image uploaded.",
Snackbar.LENGTH_LONG
)
val imageUrl = it.toString()
}
}
.addOnFailureListener { e ->
print(e.message)
pd.dismiss()
}
//progressbar
.addOnProgressListener {
var progressPercents: Double = (100.00 * it.bytesTransferred / it.totalByteCount)
pd.setMessage("Percentage: $progressPercents%")
}
}
}
Logic at MainActivity:
mbase = FirebaseDatabase.getInstance().reference
recyclerView = findViewById(R.id.recycler1)
recyclerView.layoutManager = LinearLayoutManager(this)
val options = FirebaseRecyclerOptions.Builder<Outfit>()
.setQuery(mbase!!, Outfit::class.java)
.build()
// Connecting object of required Adapter class to the Adapter class itself
adapter = PersonAdapter(options)
// Connecting Adapter class with the Recycler view*/
recyclerView.adapter = adapter
swipeRefreshLayout.setOnRefreshListener(this)
Here are screenshots of how everything looks in the backend:
my backend
images storage
private fun uploadToFirebase() {
val pd: ProgressDialog = ProgressDialog(this)
pd.setTitle("Uploading image...")
pd.show()
val fileName = UUID.randomUUID().toString()
val refStorage = FirebaseStorage.getInstance().getReference("/images/$fileName")
refStorage.putFile(selectedPhotoUri!!)
.addOnSuccessListener {
pd.dismiss() //close loading
refStorage.downloadUrl.addOnSuccessListener {
saveDataToDatabase(it.toString()) //get downloading url
}
}
.addOnFailureListener { e ->
print(e.message)
pd.dismiss()
}
//progressbar
.addOnProgressListener {
var progressPercents: Double = (100.00 * it.bytesTransferred / it.totalByteCount)
pd.setMessage("Percentage: $progressPercents%")
}
}
private fun saveDataToDatabase(imageUrl:String) {
dataBase = FirebaseDatabase.getInstance().getReference(UUID.randomUUID().toString())
dataBase?.setValue(
Outfit(
nameView?.text.toString(),
brandView?.text.toString(),
sizeView?.text.toString(),
commentView?.text.toString(),
priceView?.text.toString(),
imageUrl
)
)
}
Here is my solution. Bad url was a ploblem.
Related
In my app I have implemented image upload to firebase storage, after upload url of the image is stored in realtime database for future use. Everything is working perfectly except whenever the url of the image is stored in realtime database the app move towards the BottomNavigation. below is my code
Adapter Class from which the app is redirected towards the pictureTakingActivity
class PrintAdapter(val data:ArrayList<PrintModel>) : RecyclerView.Adapter<PrintAdapter.MyViewHolder>() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyViewHolder {
var view = LayoutInflater.from(parent.context).inflate(R.layout.single_row_home, parent, false)
return MyViewHolder(view)
}
override fun onBindViewHolder(holder: MyViewHolder, position: Int) {
holder.name.setText(data.get(position).name)
Picasso.get().load(data[position].image).into(holder.image)
Log.d("TAG", "The image is: " + data[position].catName)
var cat = data.get(position).catName
holder.itemView.setOnClickListener{
if (cat.equals("photo")){
val intent = Intent(it.context, PictureTakingActivity::class.java)
it.context.startActivity(intent)
}
}
}
override fun getItemCount(): Int {
return data.size
}
class MyViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
// Both of these can be used to find id
var name: TextView = itemView.findViewById(R.id.printName)
var image = itemView.findViewById<ImageView>(R.id.circleImageView1)
}
}
PictureTakingActivity
class PictureTakingActivity : AppCompatActivity() {
lateinit var uri: Uri
lateinit var binding: ActivityPictureTakingBinding
lateinit var databaseReference: DatabaseReference
lateinit var firebaseUser: FirebaseUser
lateinit var id: String
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityPictureTakingBinding.inflate(layoutInflater)
setContentView(binding.root)
databaseReference = FirebaseDatabase.getInstance().reference
firebaseUser = FirebaseAuth.getInstance().currentUser!!
id = firebaseUser.uid
binding.takePhoto.setOnClickListener {
ImagePicker.with(this)
.crop()
.galleryMimeTypes(
mimeTypes = arrayOf(
"image/png",
"image/jpg",
"image/jpeg"
)
)
.cameraOnly()
.compress(1024)
.maxResultSize(1080, 1080)
.start()
}
binding.uploadGallery.setOnClickListener {
ImagePicker.with(this)
.crop()
.galleryMimeTypes(
mimeTypes = arrayOf(
"image/png",
"image/jpg",
"image/jpeg"
)
)
.galleryOnly()
.compress(1024)
.maxResultSize(1080, 1080)
.start()
}
binding.ok.setOnClickListener {
uploadImage()
}
binding.notOk.setOnClickListener {
ImagePicker.with(this)
.crop()
.galleryMimeTypes(
mimeTypes = arrayOf(
"image/png",
"image/jpg",
"image/jpeg"
)
)
.compress(1024)
.maxResultSize(1080, 1080)
.start()
}
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
if (resultCode == Activity.RESULT_OK) {
//Image Uri will not be null for RESULT_OK
uri = data?.data!!
binding.userImage.setImageURI(uri)
binding.takePhoto.visibility = View.INVISIBLE
binding.uploadGallery.visibility = View.INVISIBLE
binding.ok.visibility = View.VISIBLE
binding.notOk.visibility = View.VISIBLE
Log.d("TAG", "The uri inside is: $" )
} else if (resultCode == ImagePicker.RESULT_ERROR) {
Toast.makeText(this, ImagePicker.getError(data), Toast.LENGTH_SHORT).show()
} else {
Toast.makeText(this, "Task Cancelled", Toast.LENGTH_SHORT).show()
}
}
private fun uploadImage(){
val progressBar = ProgressDialog(this)
progressBar.setMessage("Uploading file...")
progressBar.setCancelable(false)
progressBar.show()
val formatter = SimpleDateFormat("yyyy_MM_dd_HH_mm_ss", Locale.getDefault())
val now = Date()
val fileName = formatter.format(now)
val storageRefrence = FirebaseStorage.getInstance().reference.child(id).child("images/$fileName")
storageRefrence.putFile(uri).addOnSuccessListener {
progressBar.dismiss()
storageRefrence.downloadUrl.addOnSuccessListener {
databaseReference.child("Users").child(id).child("image").setValue(it.toString()).addOnSuccessListener { // After this line is executed app redirect towards MainActivity
Toast.makeText(this, "File uploaded successfully", Toast.LENGTH_SHORT).show()
}
}
}.addOnFailureListener{
progressBar.dismiss()
}
}
}
BottomNavigationActivity
class BottomNavigationActivity : AppCompatActivity() {
lateinit var binding:ActivityBottomNavigationBinding
private val fragment1: Fragment = HomeFragment()
private val fragment2: Fragment = DocumentsFragment()
private val fragment3: Fragment = OrdersFragment()
private val fragment4: Fragment = ProfileFragment()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityBottomNavigationBinding.inflate(layoutInflater)
setContentView(binding.root)
replaceFrameLayout(fragment1)
binding.bottomNavigation.setOnItemSelectedListener {
when(it.itemId){
R.id.home -> replaceFrameLayout(fragment1)
R.id.documents -> replaceFrameLayout(fragment2)
R.id.orders -> replaceFrameLayout(fragment3)
R.id.profile -> replaceFrameLayout(fragment4)
else ->{
}
}
true
}
}
private fun replaceFrameLayout(fragment: Fragment){
val fragmentManager = supportFragmentManager
val fragmentTransaction = fragmentManager.beginTransaction()
fragmentTransaction.replace(R.id.flFragment, fragment)
fragmentTransaction.commit()
}
}
I am new in Kotlin so please pardon if this is a simple issue. Thanks in advance
I've recently started programming in Kotlin and cannot seem to add a profile picture to a user when registering it.
According to the code here, I can access to the gallery and retrieve the image information. The picture will appear on screen, but after registering the user the image url will not appear anywhere.
class RegisterUser : AppCompatActivity() {
private val database = FirebaseDatabase.getInstance()
private val auth: FirebaseAuth = FirebaseAuth.getInstance()
private val UserCreation = database.getReference("Usuarios")
private val pickImage = 100
private var imageUri: Uri? = null
lateinit var imageView: ImageView
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_register_user)
Goback.setOnClickListener {
val Gobackou = Intent(this, MainActivity::class.java)
startActivity(Gobackou)
}
RegisterConfirm.setOnClickListener {
val SetUser = SetUser.text.toString()
val SetPass = setPass.text.toString()
val SetEmail = SetEmail.text.toString()
if (SetUser.isEmpty() && SetPass.isEmpty() && SetEmail.isEmpty()) {
Toast.makeText(this, "Faltan Campos", Toast.LENGTH_SHORT).show()
} else {
RegisterUserv2(SetEmail, SetPass, SetUser)
}
}
selectPP.setOnClickListener {
val intent = Intent(Intent.ACTION_PICK)
intent.type = "image/*"
startActivityForResult(intent, pickImage)
}
}
var selectedPhotoUri: Uri? = null
//guardar la foto de perfil
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
if (resultCode == RESULT_OK && requestCode == pickImage) {
val selectedPhotoUri = data?.data
val bitmap = MediaStore.Images.Media.getBitmap(contentResolver, selectedPhotoUri)
val bitmapDrawable = BitmapDrawable(bitmap)
userimg.setBackgroundDrawable(bitmapDrawable)
}
}
private fun RegisterUserv2(email: String, password: String, user: String) {
auth.createUserWithEmailAndPassword(email, password).addOnCompleteListener(this) { task ->
if (task.isSuccessful) {
uploadimage()
UltrasaveUsuario(Usuarios(auth.currentUser!!.uid, user, password, email))
val Gobackou = Intent(this, MainActivity::class.java)
startActivity(Gobackou)
} else {
Toast.makeText(this, "Registro ERROR", Toast.LENGTH_LONG).show()
}
}
}
private fun UltrasaveUsuario(usuario: Usuarios) {
val mensajeFirebase = UserCreation.push()
usuario.id = mensajeFirebase.key ?: ""
mensajeFirebase.setValue(usuario)
}
private fun uploadimage(imageurl: String){
if (selectedPhotoUri == null) return
val filename = UUID.randomUUID().toString()
val ref = FirebaseStorage.getInstance().getReference("/images/$filename/")
ref.putFile(selectedPhotoUri!!)
.addOnSuccessListener {
ref.downloadUrl.addOnSuccessListener{
}
}
}
}
Since you are using firebase I will show you how to save firebase storage.
private fun uploadImage() {
if (mSelectedImageFileUri != null) {
val sRef: StorageReference = FirebaseStorage.getInstance().reference.child(
"users/ $uid/profile.jpg"
)
sRef.putFile(mSelectedImageFileUri!!)
.addOnSuccessListener { taskSnapshot ->
taskSnapshot.metadata!!.reference!!.downloadUrl
.addOnSuccessListener { url ->
}
}.addOnFailureListener {
//error
}
} else {
}
}
then you will take this picture using the user id to take .
firebase>storage to view the image
getImage
val sRef: StorageReference = FirebaseStorage.getInstance().reference.child(
"users/ $uid/profile.jpg"
)
sRef.downloadUrl.addOnSuccessListener {
Picasso.get().load(it).into(globalprofileImage)
}
Build.gradle
implementation 'com.squareup.picasso:picasso:2.71828'
https://www.youtube.com/watch?v=nNYLQcmB7AU&t=449s&ab_channel=SmallAcademy
I make a mobile application using the "io fotoapparat" library and I want to create a document with a random id and in the document in the id field, add the document id, the photo we will take was updated in the firebase store and in the previously made about the same id in the receipt collection when taking a photo
link from which I took "io fotoapparat"
EDIT
add to firestore document
binding.button.setOnClickListener {
// Inflate the layout for this fragment
val biedronka = "biedronka"
val price = "20"
val img = " "
val identificator = " "
val from = biedronka
val value = price
val image = img
val idd = identificator
val db = Firebase.firestore
val data = hashMapOf(
"from" to from,
"value" to value,
"image" to image,
"id" to idd
)
db.collection("receipts")
.add(data)
.addOnSuccessListener { documentReference ->
Log.d(SCAN_DEBUG, "DocumentSnapshot written with ID: ${documentReference.id}")
db.collection("receipts")
.document(documentReference.id)
.update("id", documentReference.id)
.addOnSuccessListener {
}
}
.addOnFailureListener { e ->
Log.w(SCAN_DEBUG, "Error adding document", e)
}
}
2 add to storage and to document (doesnt work)
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
if (requestCode == REQUEST_CAPTURE_IMAGE && resultCode == RESULT_OK) {
val uid = profileVm.user.value?.uid!!
val imageBitmap = data?.extras?.get("data") as Bitmap
val userImage = binding.userImg
val stream = ByteArrayOutputStream()
val result = imageBitmap.compress(Bitmap.CompressFormat.JPEG, 100, stream)
val byteArray = stream.toByteArray()
if (result) profileVm.uploadUserPhoto(byteArray, "$uid.jpg")
}
}
repository to 2
fun uploadReceiptPhoto(bytes: ByteArray) {
storage.getReference("receipts")
.child("${docId}.jpg")
.putBytes(bytes)
.addOnCompleteListener{
Log.d(REPO_DEBUG, "COMPLETE UPLOAD PHOTO")
}
.addOnSuccessListener {
getReceiptPhotoDownloadUrl(it.storage)
}
.addOnFailureListener {
Log.d(REPO_DEBUG, it.message.toString())
}
}
private fun getReceiptPhotoDownloadUrl(storage: StorageReference) {
storage.downloadUrl
.addOnSuccessListener {
updateReceiptPhoto(it.toString())
}
.addOnFailureListener {
Log.d(REPO_DEBUG, it.message.toString())
}
}
private fun updateReceiptPhoto(url: String) {
cloud.collection("receipts")
.document(docId)
.update("image", url)
.addOnSuccessListener {
Log.d(REPO_DEBUG, "UPDATE USER PHOTO")
}
.addOnFailureListener {
Log.d(REPO_DEBUG, it.message.toString())
}
}
THE REST OF THE CODE (camera code "io fotoapparat" and take pictures)
class ScanFragment : Fragment(), OnReceiptsItemAdd {
private var _binding: FragmentScanBinding? = null
private val binding get() = _binding!!
private val scanVm by viewModels<ScanViewModel>()
private val SCAN_DEBUG = "SCAN_DEBUG"
private var fotoapparat: Fotoapparat? = null
private var fotoapparatState: FotoapparatState? = null
private var cameraStatus: CameraState? = null
private var flashState: FlashState? = null
private val permissions = arrayOf(Manifest.permission.CAMERA,
Manifest.permission.WRITE_EXTERNAL_STORAGE,
Manifest.permission.READ_EXTERNAL_STORAGE)
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?,
): View {
_binding = FragmentScanBinding.inflate(inflater, container, false)
createFotoapparat()
cameraStatus = CameraState.BACK
flashState = FlashState.OFF
fotoapparatState = FotoapparatState.OFF
binding.fabSwitchCamera.setOnClickListener {
switchCamera()
}
binding.fabFlash.setOnClickListener {
changeFlashState()
}
return binding.root
}
private fun createFotoapparat(){
val cameraView = binding.cameraView
fotoapparat = Fotoapparat(
context = requireContext(),
view = cameraView,
scaleType = ScaleType.CenterCrop,
lensPosition = back(),
logger = loggers(
logcat()
),
cameraErrorCallback = { error ->
println("Recorder errors: $error")
}
)
}
private fun changeFlashState() {
fotoapparat?.updateConfiguration(
CameraConfiguration(
flashMode = if(flashState == FlashState.TORCH) off() else torch()
)
)
flashState = if(flashState == FlashState.TORCH) FlashState.OFF
else FlashState.TORCH
}
private fun switchCamera() {
fotoapparat?.switchTo(
lensPosition = if (cameraStatus == CameraState.BACK) front() else back(),
cameraConfiguration = CameraConfiguration()
)
cameraStatus = if(cameraStatus == CameraState.BACK) CameraState.FRONT
else CameraState.BACK
}
private fun takePhoto() {
if (hasNoPermissions()) {
requestPermission()
}else{
fotoapparat
?.takePicture()
?.toBitmap()
}
}
override fun onStart() {
super.onStart()
if (hasNoPermissions()) {
requestPermission()
}else{
fotoapparat?.start()
fotoapparatState = FotoapparatState.ON
}
}
private fun hasNoPermissions(): Boolean{
return ContextCompat.checkSelfPermission(requireContext(),
Manifest.permission.READ_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED || ContextCompat.checkSelfPermission(requireContext(),
Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED || ContextCompat.checkSelfPermission(requireContext(),
Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED
}
private fun requestPermission(){
ActivityCompat.requestPermissions(requireActivity(), permissions,0)
}
override fun onStop() {
super.onStop()
fotoapparat?.stop()
FotoapparatState.OFF
}
}
enum class CameraState{
FRONT, BACK
}
enum class FlashState{
TORCH, OFF
}
enum class FotoapparatState{
ON, OFF
}
I am trying to rewrite an existing app in Kotlin for the purpose of learning and getting used to the language. The app allows the user to enter and modify entries and each entry is hosted in a RecyclerView list with a custom list item. Although entries are successfully added to the database (confirming that with Toast messages), there isn't a list item present for the said entry.
This is my adapter:
class EntryAdapter : androidx.recyclerview.widget.ListAdapter<Entry, EntryAdapter.ViewHolder>(DIFF_CALLBACK){
private var listener: OnItemLongClickListener? = null
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
val view = LayoutInflater.from(parent.context).inflate(R.layout.recyclerview_item, parent, false)
return ViewHolder(view)
}
override fun onBindViewHolder(holder: ViewHolder, position: Int){
val currentEntry = getItem(position)
holder.hint.text = currentEntry.hint
holder.username.text = currentEntry.username
holder.password.text = currentEntry.password
}
fun getEntryAt(position: Int): Entry{return getItem(position)}
inner class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView){
val username: TextView = itemView.findViewById(R.id.username_display)
val password: TextView = itemView.findViewById(R.id.password_display)
val hint: TextView = itemView.findViewById(R.id.hint_display)
init {
itemView.setOnLongClickListener{
val position = adapterPosition
if (listener != null && position != RecyclerView.NO_POSITION){listener!!.onItemLongClick(getItem(position))}
true
}
}
}
interface OnItemLongClickListener{fun onItemLongClick(entry: Entry)}
fun setOnItemLongClickListener(listener: OnItemLongClickListener){this.listener = listener}
companion object{
private val DIFF_CALLBACK = object : DiffUtil.ItemCallback<Entry>(){
override fun areItemsTheSame(oldItem: Entry, newItem: Entry): Boolean{return oldItem.id == newItem.id}
override fun areContentsTheSame(oldItem: Entry, newItem: Entry): Boolean{return oldItem.username == newItem.username && oldItem.hint == newItem.hint && oldItem.password == newItem.password}
}
}
}
This is my AddEditEntry.kt activity. The way it works is that when the user wishes to make an entry, he/she clicks on the FAB button which invokes this activity. This activity is where the user enters the entry and adds it to the database (and by extension, the RecyclerView) by clicking the saveEntry button:
class AddEditEntryActivity : AppCompatActivity() {
private var usernameEditText: EditText? = null
private var passwordEditText: EditText? = null
private var hintEditText: EditText? = null
private var passwordABCD: CheckBox? = null
private var passwordabcd: CheckBox? = null
private var password0123: CheckBox? = null
private var passwordSymbols: CheckBox? = null
private var radio4: RadioButton? = null
private var radio8: RadioButton? = null
private var radio12: RadioButton? = null
private var radio16: RadioButton? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_addedit_entry)
usernameEditText = findViewById(R.id.username_field)
passwordEditText = findViewById(R.id.password_field)
hintEditText = findViewById(R.id.hint_field)
passwordABCD = findViewById(R.id.upp_checkbox)
passwordabcd = findViewById(R.id.low_checkbox)
password0123 = findViewById(R.id.num_checkbox)
passwordSymbols = findViewById(R.id.sym_checkbox)
radio4 = findViewById(R.id.four)
radio8 = findViewById(R.id.eight)
radio12 = findViewById(R.id.twelve)
radio16 = findViewById(R.id.sixteen)
val generatePassword = findViewById<Button>(R.id.btn_password_generate)
val saveEntry = findViewById<Button>(R.id.btn_save)
val intent = intent
if (intent.hasExtra(EXTRA_ID)) {
title = getString(R.string.edit_entry)
saveEntry.setText(R.string.update_entry)
usernameEditText!!.setText(getIntent().getStringExtra(EXTRA_USERNAME))
passwordEditText!!.setText(getIntent().getStringExtra(EXTRA_PASSWORD))
hintEditText!!.setText(getIntent().getStringExtra(EXTRA_HINT))
}
else {title = "Add Entry"}
Objects.requireNonNull<ActionBar>(supportActionBar).setHomeAsUpIndicator(R.drawable.ic_close_white_24dp)
generatePassword.setOnClickListener { passwordEditText!!.setText(generatedPassword()) }
saveEntry.setOnClickListener {
val data = Intent()
data.putExtra(EXTRA_USERNAME, usernameEditText!!.text.toString())
data.putExtra(EXTRA_HINT, hintEditText!!.text.toString())
data.putExtra(EXTRA_PASSWORD, passwordEditText!!.text.toString())
val id = getIntent().getIntExtra(EXTRA_ID, -1)
if (id != -1) {data.putExtra(EXTRA_ID, id)}
setResult(Activity.RESULT_OK, data)
finish()
Toast.makeText(this, "data.putExtra() from AddEditEntryActivity", Toast.LENGTH_SHORT).show()
Toast.makeText(this, usernameEditText!!.text.toString(), Toast.LENGTH_SHORT).show()
Toast.makeText(this, hintEditText!!.text.toString(), Toast.LENGTH_SHORT).show()
Toast.makeText(this, passwordEditText!!.text.toString(), Toast.LENGTH_SHORT).show()
}
}
private fun generatedPassword(): String? {
var length = 0
val generatedString = StringBuilder()
val rand = Random()
val capitalLetters = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
val lowercaseLetters = "abcdefghijklmnopqrstuvwxyz"
val numbers = "0123456789"
val characters = "!##$%^&*()"
if (radio4!!.isChecked) {length = 4}
else if (radio8!!.isChecked) {length = 8}
else if (radio12!!.isChecked) {length = 12}
else if (radio16!!.isChecked) {length = 16}
var totalCharacters = ""
if (passwordABCD!!.isChecked) {totalCharacters += capitalLetters}
if (passwordabcd!!.isChecked) {totalCharacters += lowercaseLetters}
if (password0123!!.isChecked) {totalCharacters += numbers}
if (passwordSymbols!!.isChecked) {totalCharacters += characters}
if (!totalCharacters.trim { it <= ' ' }.isEmpty() && length > 0) {
for (i in 0 until length) {generatedString.append(totalCharacters[rand.nextInt(totalCharacters.length)])}
return generatedString.toString()
}
else {Toast.makeText(this, "Not a valid password!", Toast.LENGTH_SHORT).show()}
return null
}
companion object {
val EXTRA_USERNAME = "com.ozbek.cryptpass.EXTRA_USERNAME"
val EXTRA_HINT = "com.ozbek.cryptpass.EXTRA_HINT"
val EXTRA_PASSWORD = "com.ozbek.cryptpass.EXTRA_PASSWORD"
val EXTRA_ID = "com.ozbek.cryptpass.EXTRA_ID"
}
}
And this is the MainActivity.kt file where the user enters new entries. The first if block in onActivityResult() is the code that retrieves the entries from AddEditEntry.kt file and adds it to the entity class:
class MainActivity : AppCompatActivity(), LifecycleOwner {
private lateinit var recyclerView: RecyclerView
internal lateinit var adapter: EntryAdapter
private lateinit var floatingActionButton: FloatingActionButton
private lateinit var layoutManager: RecyclerView.LayoutManager
private lateinit var addEditEntryActivity: AddEditEntryActivity
internal lateinit var entry: Entry
private lateinit var viewModel: EntryViewModel
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val ctx = this.applicationContext
val sentryDsn = "https://93f06d5090d646ac9443a3fc531fc5de#sentry.io/1454826:port/1?options"
Sentry.init(sentryDsn, AndroidSentryClientFactory(ctx))
Sentry.init(AndroidSentryClientFactory(ctx))
viewModel = ViewModelProviders.of(this).get(EntryViewModel::class.java)
viewModel.allEntries.observe(this, Observer { entries -> adapter.submitList(entries) })
adapter = EntryAdapter()
layoutManager = LinearLayoutManager(this)
recyclerView = findViewById<RecyclerView>(R.id.userpass_recyclerview).apply{
layoutManager = layoutManager
adapter = adapter
}
addEditEntryActivity = AddEditEntryActivity()
entry = Entry()
floatingActionButton = findViewById<FloatingActionButton>(R.id.generate_fab)
floatingActionButton.setOnClickListener {
val intent = Intent(this#MainActivity, AddEditEntryActivity::class.java)
Toast.makeText(this, "AddEditActivity started", Toast.LENGTH_SHORT).show()
startActivityForResult(intent, ADD_ENTRY_REQUEST)
}
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) {viewModel.delete(adapter.getEntryAt(viewHolder.adapterPosition))}
}).attachToRecyclerView(recyclerView)
adapter.setOnItemLongClickListener (object: EntryAdapter.OnItemLongClickListener {
override fun onItemLongClick(entry: Entry){
val intent = Intent(this#MainActivity, AddEditEntryActivity::class.java)
intent.putExtra(AddEditEntryActivity.EXTRA_ID, entry.id)
intent.putExtra(AddEditEntryActivity.EXTRA_USERNAME, entry.username)
intent.putExtra(AddEditEntryActivity.EXTRA_HINT, entry.hint)
intent.putExtra(AddEditEntryActivity.EXTRA_PASSWORD, entry.password)
startActivityForResult(intent, EDIT_ENTRY_REQUEST)
}
})
}
override fun onCreateOptionsMenu(menu: Menu): Boolean {
menuInflater.inflate(R.menu.menu, menu)
return true
}
override fun onOptionsItemSelected(item: MenuItem): Boolean {
val itemId = item.itemId
if (itemId == R.id.delete_all) {viewModel.deleteAll()}
return super.onOptionsItemSelected(item)
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
if (requestCode == ADD_ENTRY_REQUEST && resultCode == Activity.RESULT_OK) {
Toast.makeText(this, data?.getStringExtra(AddEditEntryActivity.EXTRA_USERNAME), Toast.LENGTH_SHORT).show()
Toast.makeText(this, data?.getStringExtra(AddEditEntryActivity.EXTRA_PASSWORD), Toast.LENGTH_SHORT).show()
Toast.makeText(this, data?.getStringExtra(AddEditEntryActivity.EXTRA_HINT), Toast.LENGTH_SHORT).show()
val username = Objects.requireNonNull<Intent>(data).getStringExtra(AddEditEntryActivity.EXTRA_USERNAME)
val password = Objects.requireNonNull<Intent>(data).getStringExtra(AddEditEntryActivity.EXTRA_PASSWORD)
val hint = Objects.requireNonNull<Intent>(data).getStringExtra(AddEditEntryActivity.EXTRA_HINT)
val entry = Entry(username, hint, password)
viewModel.insert(entry)
Toast.makeText(this, "Entry added!", Toast.LENGTH_SHORT).show()
} else if (requestCode == EDIT_ENTRY_REQUEST && resultCode == Activity.RESULT_OK) {
Toast.makeText(this, data!!.getIntExtra(AddEditEntryActivity.EXTRA_ID, -1), Toast.LENGTH_SHORT).show()
Toast.makeText(this, data.getStringExtra(AddEditEntryActivity.EXTRA_USERNAME), Toast.LENGTH_SHORT).show()
Toast.makeText(this, data.getStringExtra(AddEditEntryActivity.EXTRA_PASSWORD), Toast.LENGTH_SHORT).show()
Toast.makeText(this, data.getStringExtra(AddEditEntryActivity.EXTRA_HINT), Toast.LENGTH_SHORT).show()
val id = Objects.requireNonNull<Intent>(data).getIntExtra(AddEditEntryActivity.EXTRA_ID, -1)
if (id == -1) {
Toast.makeText(this, "Something went wrong", Toast.LENGTH_SHORT).show()
return
}
val username = Objects.requireNonNull<Intent>(data).getStringExtra(AddEditEntryActivity.EXTRA_USERNAME)
val password = Objects.requireNonNull<Intent>(data).getStringExtra(AddEditEntryActivity.EXTRA_PASSWORD)
val hint = Objects.requireNonNull<Intent>(data).getStringExtra(AddEditEntryActivity.EXTRA_HINT)
val entry = Entry(username, hint, password, id)
entry.id = id
viewModel.update(entry)
Toast.makeText(this, "Entry updated", Toast.LENGTH_SHORT).show()
} else {Toast.makeText(this, "Entry not added!", Toast.LENGTH_SHORT).show()}
}
companion object {
const val ADD_ENTRY_REQUEST = 1
const val EDIT_ENTRY_REQUEST = 2
}
}
I can add more code per request. This is the full Github repo.
Here is the problem:
recyclerView = findViewById<RecyclerView>(R.id.userpass_recyclerview).apply{
layoutManager = layoutManager
adapter = adapter
}
Idk what's going on but it seems like you're initializing them to themselves. Don't do that. Its good practice to keep your variable names distinct. Refactor the objects like this:
internal lateinit var entryAdapter: EntryAdapter
private lateinit var linearLayoutManager: RecyclerView.LayoutManager
And make the following changes in your onCreate():
recyclerView = findViewById<RecyclerView>(R.id.userpass_recyclerview).apply{
adapter = entryAdapter
layoutManager = linearLayoutManager
}
I have two recyclerViews, which represent my grocery list.
When I click on an item it gets "transferred" to the other list.
The problem is that the images are loaded with Glide and Firebase Storage and they don't show the correct images even if the listitem does not have a path.
I have tried every notifyDataSetChanged(), recyclerView.notifyItemChanged() and recyclerView.invalidate() and others. Maybe you could help me.
This is my Adapter:
class RecyclerViewAdapterEinkaufslisteDetail(
val context: Context,
val lebensmittelList: ArrayList<Inventarstueck>
) :
RecyclerView.Adapter<RecyclerViewAdapterEinkaufslisteDetail.ViewHolder>() {
override fun onBindViewHolder(holder: RecyclerViewAdapterEinkaufslisteDetail.ViewHolder, position: Int) {
// TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
//Es wird der Name, die Anzahl, die Einheit (gramm, kg, etc) das Bild und ein Warnzeichen angezeigt, falls ein Produkt abläufty
holder.mName.text = lebensmittelList[position].name_de.toString()
val array = holder.itemView.getContext().getResources().getStringArray(R.array.einheit_kurz)
val placeholder =
lebensmittelList[position].restlichemenge.toString() + " " + array[lebensmittelList[position].einheit!!]
holder.mAnzahl.text = placeholder
// holder.txtTitle.text = userList[position]
holder.mZeichen.visibility = View.INVISIBLE
//KLEINEs Anzeigebild der Produkte runterladen
if (!(lebensmittelList[position].fotoURL.isNullOrEmpty() || lebensmittelList[position].fotoURL.isNullOrBlank())) {
//Überprüfen, ob im Feld die Endung gespeichert ist, wenn nicht, immer png!!
val url:String
if(lebensmittelList[position].fotoURL!!.endsWith(".png")){
url = lebensmittelList[position].fotoURL!!
}else{
url = lebensmittelList[position].fotoURL + ".png"
}
val storage = FirebaseStorage.getInstance()
val storageRef = storage.reference
// Points to "images"
val imagesRef = storageRef.child(Constants.FIREBASE_STORAGE_PRODUKTFOTO_KLEIN_PFAD)
val fotoRef = imagesRef.child(url)
GlideApp.with(this.context).load(fotoRef).into(holder.mProduktfoto)
}
}
override fun getItemCount(): Int {
return lebensmittelList.size
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerViewAdapterEinkaufslisteDetail.ViewHolder {
val v = LayoutInflater.from(parent.context).inflate(R.layout.recyclerview_item_inventar, parent, false)
return ViewHolder(v)
}
class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView), View.OnClickListener, View.OnLongClickListener{
init {
itemView.setOnClickListener(this)
itemView.setOnLongClickListener(this)
}
override fun onClick(v: View) {
clickListener!!.onItemClick(adapterPosition, v);
}
override fun onLongClick(v: View): Boolean {
clickListener!!.onItemLongClick(adapterPosition, v)
return true
}
val mName = itemView.findViewById<TextView>(R.id.rvInventar_tv_Produktname)
val mAnzahl = itemView.findViewById<TextView>(R.id.rvInventar_tv_Anzahl)
val mProduktfoto = itemView.findViewById<ImageView>(R.id.rvInventarProduktFoto)
val mZeichen = itemView.findViewById<ImageView>(R.id.rvInventar_Icon)
// val txtTitle = itemView.findViewById<TextView>(R.id.txtTitle)
val mCard = itemView.findViewById<MaterialCardView>(R.id.rvCardInventar)
}
fun setOnItemClickListener(clickListener: ClickListener) {
RecyclerViewAdapterEinkaufslisteDetail.clickListener = clickListener
}
interface ClickListener {
fun onItemClick(position: Int, v: View)
fun onItemLongClick(position: Int, v: View)
}
companion object {
private var clickListener: RecyclerViewAdapterEinkaufslisteDetail.ClickListener? = null
}
}
This is where I change the data:
rvKaufen = findViewById(R.id.rvEinkaufslisteDetailKaufen)
val layoutmanager = GridLayoutManager(this, 3)
rvKaufen.layoutManager = layoutmanager
rvGekauft = findViewById(R.id.rvEinkaufslisteDetailGekauft)
val layoutmanager2 = GridLayoutManager(this, 3)
rvGekauft.layoutManager = layoutmanager2
rvadapter = RecyclerViewAdapterEinkaufslisteDetail(this, itemsInEinkaufsliste)
// val rvadapter = RecyclerViewAdapterInventar(this.context!!, lebensmittelliste)
rvadapterGekauft = RecyclerViewAdapterEinkaufslisteDetail(this, itemsGekauft)
//nur einmal itemclicklistener zuweisen, sonst megafehler! intern abrufen aus welchem Recyclerview der Click gekommen ist
/**/ rvadapter.setOnItemClickListener(object : RecyclerViewAdapterEinkaufslisteDetail.ClickListener {
override fun onItemClick(position: Int, v: View) {
Log.d("Click", "onItemClick position: $position")
val parentId = (v.parent as View).id
if (parentId == rvKaufen.id) {
Toast.makeText(this#EinkaufslisteDetail, "GeKAUFt", Toast.LENGTH_SHORT).show()
/* val inventarstueck = rvadapter.lebensmittelList[position].copy()
//Hier auch aus den Listen löschen??
//TODO
rvadapterGekauft.lebensmittelList.add(inventarstueck)
rvadapterGekauft.notifyDataSetChanged()
rvadapter.lebensmittelList.removeAt(position)
// itemsInEinkaufsliste.removeAt(position)
rvadapter.notifyItemRemoved(position)
// rvadapter.lebensmittelList.removeAt(position)
*/
//rvadapter.notifyDataSetChanged()
val inventarstueck = itemsInEinkaufsliste[position].copy()
//itemsGekauft.add(inventarstueck)
itemsGekauft.add(inventarstueck)
rvadapterGekauft.notifyItemInserted(itemsGekauft.size-1)
// rvadapterGekauft.notifyItemRangeChanged(0,itemsGekauft.size)
itemsInEinkaufsliste.removeAt(position)
rvadapter.notifyItemRemoved(position)
rvadapter.notifyItemRangeChanged(position,itemsInEinkaufsliste.size)
/* rvKaufen.adapter = null
rvKaufen.layoutManager = null
rvKaufen.adapter = rvadapter
val layoutmanager3 = GridLayoutManager(this#EinkaufslisteDetail, 3)
rvKaufen.layoutManager = layoutmanager3
rvadapter.notifyDataSetChanged()
//rvKaufen.removeViewAt(position)
// rvGekauft.add
rvGekauft.adapter = null
rvGekauft.layoutManager = null
rvGekauft.adapter = rvadapterGekauft
val layoutmanager4 = GridLayoutManager(this#EinkaufslisteDetail, 3)
rvKaufen.layoutManager = layoutmanager4
rvadapterGekauft.notifyDataSetChanged()*/
} else if (parentId == rvGekauft.id) {
Toast.makeText(this#EinkaufslisteDetail, "Zurück geschoben", Toast.LENGTH_SHORT).show()
/* val inventarstueck = rvadapterGekauft.lebensmittelList[position].copy()
rvadapter.lebensmittelList.add(inventarstueck)
rvadapter.notifyDataSetChanged()
rvadapterGekauft.lebensmittelList.removeAt(position)
rvadapterGekauft.notifyItemRemoved(position)*/
val inventarstueck = itemsGekauft[position].copy()
//itemsGekauft.add(inventarstueck)
itemsInEinkaufsliste.add(inventarstueck)
rvadapter.notifyItemInserted(itemsInEinkaufsliste.size-1)
// rvadapter.notifyItemRangeChanged(0,itemsInEinkaufsliste.size)
itemsGekauft.removeAt(position)
rvadapterGekauft.notifyItemRemoved(position)
rvadapterGekauft.notifyItemRangeChanged(position,itemsGekauft.size)
/* rvKaufen.adapter = null
rvKaufen.layoutManager = null
rvKaufen.adapter = rvadapter
val layoutmanager3 = GridLayoutManager(this#EinkaufslisteDetail, 3)
rvKaufen.layoutManager = layoutmanager3
rvadapter.notifyDataSetChanged()
rvGekauft.adapter = null
rvGekauft.layoutManager = null
rvGekauft.adapter = rvadapterGekauft
val layoutmanager4 = GridLayoutManager(this#EinkaufslisteDetail, 3)
rvKaufen.layoutManager = layoutmanager4
rvadapterGekauft.notifyDataSetChanged()*/
}
}
override fun onItemLongClick(position: Int, v: View) {
Log.d("Click", "onItemLongClick pos = $position")
Log.d("Click", "onItemClick position: $position")
val parentId = (v.parent as View).id
if (parentId == rvKaufen.id) {
Toast.makeText(this#EinkaufslisteDetail, "Verändern", Toast.LENGTH_SHORT).show()
val inventarstueck = rvadapter.lebensmittelList[position].copy()
val intent = Intent(this#EinkaufslisteDetail, EinkaufslisteProduktBearbeiten::class.java)
intent.putExtra("swissID",inventarstueck.swiss_id)
intent.putExtra("scandatum",inventarstueck.scandatum)
intent.putExtra("einkaufslistenID",einkaufslistenID)
startActivityForResult(intent,_resultCODE)
}else if (parentId == rvGekauft.id) {
Toast.makeText(this#EinkaufslisteDetail, "Keine Aktion möglich", Toast.LENGTH_SHORT).show()
}
}
})
When I call the SetItemClickListener on the second recyclerView as well, then the whole process doesn't work.
It is very rare that there are two recyclerViews used so I haven't found anything either.
EDIT
I found the solution for my Problem:
I set the ImageView to a default picture, in case there is no URL.,
if (!(lebensmittelList[position].fotoURL.isNullOrEmpty() || lebensmittelList[position].fotoURL.isNullOrBlank())) {
.
.
. GlideApp.with(this.context).load(fotoRef).into(holder.mProduktfoto)
}else{
holder.mProduktfoto.setImageResource(R.drawable.logo_384_384)
}