I have a ListView with clickable ListItems that display a Friend objects name and birthday. The onClick is supposed to start a new Activity with an edit page where you can change the name and birthday of said friend. For this I start a new activity with an Intent containing a Serialized Friend object.
The Activity works perfectly as it should as long as I don't add the Friend object to the intent:
intent.putExtra("friend", friendList.get(position) as Serializable)
but as soon as I add this line of code before starting the activity the screen just turns black. The program doesn't crash and there are no error messages, just a black screen.
MainActivity
class MainActivity : Activity() {
private var friendList : ArrayList<Friend> = arrayListOf()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
initButtons()
}
private fun initButtons() {
val registerButton = findViewById<Button>(R.id.register_friend)
registerButton.setOnClickListener {
val name = findViewById<EditText>(R.id.name).text.toString()
val birthday = findViewById<EditText>(R.id.birthday).text.toString()
if(name != "" && birthday != "") {
friendList.add(Friend(name,birthday))
}
}
val showFriendsButton = findViewById<Button>(R.id.show_friends)
showFriendsButton.setOnClickListener {
val listView = findViewById<ListView>(R.id.friend_list_view)
val adapter = FriendListAdapter(this, friendList)
listView.adapter = adapter
listView.choiceMode = ListView.CHOICE_MODE_SINGLE
if(listView.getVisibility() == View.VISIBLE){
listView.setVisibility(View.INVISIBLE)
}else {
listView.setVisibility(View.VISIBLE)
}
listView.setOnItemClickListener { parent, view, position, id ->
val intent = Intent("android.intent.action.EDIT")
intent.putExtra("friend", friendList.get(position) as Serializable)
startActivity(intent)
}
}
}
}
EditActivity
class EditFriend : Activity(){
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.edit_friend)
val friend: Friend = intent.getSerializableExtra("friend") as Friend
findViewById<EditText>(R.id.name).setText(friend.name)
findViewById<EditText>(R.id.birthday).setText(friend.birthday)
val saveButton = findViewById<Button>(R.id.save)
saveButton.setOnClickListener {
val name = findViewById<EditText>(R.id.name).text.toString()
val birthday = findViewById<EditText>(R.id.birthday).text.toString()
if(name != "" && birthday != "") {
friend.name = name
friend.birthday = birthday
finish()
}
}
}
}
Friend
data class Friend(var name : String?, var birthday : String?) : Serializable
Related
I know this error is very common, but I couldn't find a solution to my problem in the way too many places this error is brought up.
I'm developing an app in order to store and sort TV shows. I've got a main activity with some fragments, with a HomeFragment which is the home page, with an 'Add show' button, and below a recyclerView with all my shows.
When clicking on the 'Add show' button, I start a new activity in order to fill a form and then create the show with the provided informations. No problem here, that works as it should. Now I'm trying to add the possibility to edit the shows by clicking on them in the recyclerView I talked about above. This also brings up the same activity as the 'Add show' button, but this time with the show informations.
And this is from this page that the problem seems to be coming. In the form activity, I have an button in which I pick an image for the show. When editing the show, if I change the image, I get no error, but if I change something else, for example the name, without changing this image, when clicking the confirm button, the show is correctly edited but the app crashes with the java.lang.IllegalStateException: Can not perform this action after onSaveInstanceState error.
The error seems to be coming from the fragment part, with the transaction being unable to commit (I've searched for a while so I began to understand why that wasn't working, but couldn't determine which part of the code makes it this way). Here is the fragment:
class HomeFragment(private val context: MainActivity) : Fragment() {
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
val view = inflater.inflate(R.layout.fragment_home, container, false)
view.findViewById<Button>(R.id.button_add_show).setOnClickListener{
startActivity(Intent(context, AddShowActivity::class.java))
}
val verticalRecyclerView = view.findViewById<RecyclerView>(R.id.vertical_recycler_view)
verticalRecyclerView.adapter = ShowAdapter(context, showList, R.layout.item_show)
return view
}
}
And here the MainActivity part where it's loaded:
private fun loadFragment(fragment: Fragment){
// Load repository
val repo = ShowRepository()
// Update shows list
repo.updateData{
// Inject fragment into fragment_container
val transaction = supportFragmentManager.beginTransaction()
transaction.replace(R.id.fragment_container, fragment)
transaction.addToBackStack(null)
transaction.commit()
}
}
Here is the code of my AddShowActivity, which renders the form to fill:
class AddShowActivity : AppCompatActivity() {
private var fileImage: Uri? = null
private lateinit var uploadedImage: ImageView
private lateinit var editTextName: EditText
private lateinit var editTextNote: EditText
private lateinit var editTextDescription: EditText
private lateinit var editTextReview: EditText
private lateinit var datePicker: DatePicker
private var currentShow: ShowModel? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_add_show)
setupComponents()
setupButtons()
// Get show when editing
if(intent.extras != null){
val position = intent.extras!!.getInt("position")
currentShow = showList[position]
}
initializeComponents()
}
private fun setupComponents() {
editTextName = findViewById(R.id.name_input)
editTextNote = findViewById(R.id.note_input)
editTextDescription = findViewById(R.id.description_input)
editTextReview = findViewById(R.id.review_input)
uploadedImage = findViewById(R.id.preview_image)
datePicker = findViewById(R.id.watch_date_input)
}
private fun setupButtons(){
val pickupImageButton = findViewById<Button>(R.id.upload_image_button)
pickupImageButton.setOnClickListener{
pickupImage()
}
val confirmButton = findViewById<Button>(R.id.confirm_button)
confirmButton.setOnClickListener{
sendForm()
val toastText = when(currentShow){
null -> "Show added"
else -> "Show edited"
}
Toast.makeText(this, toastText, Toast.LENGTH_SHORT).show()
this.finish()
}
}
#SuppressLint("NewApi")
private fun initializeComponents() {
if(currentShow != null){
editTextName.setText(currentShow!!.name)
editTextNote.setText(currentShow!!.note.toString())
editTextDescription.setText(currentShow!!.description)
editTextReview.setText(currentShow!!.review)
Glide.with(this).load(Uri.parse(currentShow!!.imageUrl)).into(uploadedImage)
}
}
private fun sendForm(){
val repo = ShowRepository()
if(fileImage == null)createShow(repo)
else{
if(currentShow != null)repo.deleteImage(currentShow!!)
repo.uploadImage(fileImage!!){
createShow(repo)
}
}
}
private fun createShow(repo: ShowRepository){
val showName = editTextName.text.toString()
val showNote = parseInt(editTextNote.text.toString())
val description = editTextDescription.text.toString()
val review = editTextReview.text.toString()
val showWatchDate = getWatchDate(datePicker)
val downloadImageUrl = downloadImageUri.toString()
val show = ShowModel(UUID.randomUUID().toString(), showName, showWatchDate, showNote, downloadImageUrl, description, review)
if(currentShow != null){
show.id = currentShow!!.id
repo.updateShow(show)
}
else repo.insertShow(show)
}
private fun getWatchDate(datePicker: DatePicker): String {
var day = datePicker.dayOfMonth.toString()
if(day.toInt() < 10)day = "0$day"
var month = (datePicker.month + 1).toString()
if(month.toInt() < 10)month = "0$month"
val year = datePicker.year.toString()
return "$day-$month-$year"
}
private fun pickupImage(){
val intent = Intent()
intent.type = "image/"
intent.action = Intent.ACTION_GET_CONTENT
startActivityForResult(Intent.createChooser(intent, "Select Picture"), 47)
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
if(resultCode == Activity.RESULT_OK && requestCode == 47){
if(data == null || data.data == null)return
fileImage = data.data
uploadedImage.setImageURI(fileImage)
}
}
}
Here the ShowRepository, which handles the communication with the Firebase database:
class ShowRepository {
object Singleton{
// Link to bucket
private val BUCKET_URL: String = "gs://tv-memories.appspot.com"
// Storage connexion
val storageReference = FirebaseStorage.getInstance().getReferenceFromUrl(BUCKET_URL)
// Database connexion
val databaseRef = FirebaseDatabase.getInstance().getReference("shows")
// List containing all shows
val showList = arrayListOf<ShowModel>()
// Contains current image link
var downloadImageUri: Uri? = null
}
fun updateData(callback: () -> Unit){
// Absorb data from databaseRef
databaseRef.addValueEventListener(object : ValueEventListener {
override fun onDataChange(snapshot: DataSnapshot) {
// Remove old shows
showList.clear()
// Get list
for(ds in snapshot.children){
//Build show object
val show = ds.getValue(ShowModel::class.java)
// Verify show isn't null
if(show != null){
// Add show to the list
showList.add(show)
}
}
// Activate callback
callback()
}
override fun onCancelled(p0: DatabaseError) {
}
})
}
// Upload files on storage
fun uploadImage(file: Uri, callback: () -> Unit){
val fileName = UUID.randomUUID().toString() + ".jpg"
val ref = storageReference.child(fileName)
val uploadTask = ref.putFile(file)
uploadTask.continueWithTask(Continuation<UploadTask.TaskSnapshot, Task<Uri>>{ task ->
if(!task.isSuccessful){
task.exception?.let{throw it}
}
return#Continuation ref.downloadUrl
}).addOnCompleteListener{ task ->
if(task.isSuccessful){
downloadImageUri = task.result
callback()
}
}
}
fun deleteImage(show: ShowModel){
val photoRef: StorageReference = FirebaseStorage.getInstance().getReferenceFromUrl(show.imageUrl)
photoRef.delete()
}
fun updateShow(show: ShowModel) = databaseRef.child(show.id).setValue(show)
fun insertShow(show: ShowModel) = databaseRef.child(show.id).setValue(show)
fun deleteShow(show: ShowModel){
databaseRef.child(show.id).removeValue()
deleteImage(show)
}
}
And the full traceback of the error:
E/AndroidRuntime: FATAL EXCEPTION: main
Process: fr.steph.showmemories, PID: 18296
java.lang.IllegalStateException: Can not perform this action after onSaveInstanceState
at androidx.fragment.app.FragmentManager.checkStateLoss(FragmentManager.java:1844)
at androidx.fragment.app.FragmentManager.enqueueAction(FragmentManager.java:1884)
at androidx.fragment.app.BackStackRecord.commitInternal(BackStackRecord.java:329)
at androidx.fragment.app.BackStackRecord.commit(BackStackRecord.java:294)
at fr.steph.showmemories.MainActivity$loadFragment$1.invoke(MainActivity.kt:49)
at fr.steph.showmemories.MainActivity$loadFragment$1.invoke(MainActivity.kt:44)
at fr.steph.showmemories.ShowRepository$updateData$1.onDataChange(ShowRepository.kt:61)
at com.google.firebase.database.core.ValueEventRegistration.fireEvent(ValueEventRegistration.java:75)
at com.google.firebase.database.core.view.DataEvent.fire(DataEvent.java:63)
at com.google.firebase.database.core.view.EventRaiser$1.run(EventRaiser.java:55)
at android.os.Handler.handleCallback(Handler.java:873)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loop(Looper.java:214)
at android.app.ActivityThread.main(ActivityThread.java:7078)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:494)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:964)
I finally found a solution to my problem, which is to replace transaction.commit() by transaction.commitAllowingStateLoss() in my loadFragment() method in case the state has been saved.
I then get
private fun loadFragment(fragment: Fragment){
// Load repository
val repo = ShowRepository()
// Update shows list
repo.updateData{
// Inject fragment into fragment_container
val transaction = supportFragmentManager.beginTransaction()
transaction.replace(R.id.fragment_container, fragment)
transaction.addToBackStack(null)
if(supportFragmentManager.isStateSaved)transaction.commitAllowingStateLoss()
else transaction.commit()
}
}
I am a beginner at Android Studio and KOTLIN. Please check out my problem.
Code:
class getOTP : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_get_otp)
val inputCode1 = findViewById<EditText>(R.id.inputCode1).text.toString()
val inputCode2 = findViewById<EditText>(R.id.inputCode2).text.toString()
val inputCode3 = findViewById<EditText>(R.id.inputCode3).text.toString()
val inputCode4 = findViewById<EditText>(R.id.inputCode4).text.toString()
val verifyOTPButtonOne = findViewById<Button>(R.id.verifyOTPButtonOne)
verifyOTPButtonOne.setOnClickListener {
if(inputCode1 == "" || inputCode2 == "" || inputCode3 == "" || inputCode4 == ""){
Toast.makeText(applicationContext,"Please Enter Correct OTP",Toast.LENGTH_SHORT).show()
}
else {
val intent = Intent(this, VerifySuccess::class.java)
startActivity(intent)
}
}
}
}
}
PROBLEM: Here, after inputting all 4 text fields the toast is still appearing and Activity is not starting.
You are reading the value of the text fields only once, at the moment you call .toString() on them. So the if in the onClickListener only checks the initial values of the text fields.
You would have to call .toString() inside the listener for it to react appropriately, or you could just call isEmpty() directly on the Editable since it implements CharSequence:
class getOTP : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_get_otp)
val inputCode1 = findViewById<EditText>(R.id.inputCode1)
val inputCode2 = findViewById<EditText>(R.id.inputCode2)
val inputCode3 = findViewById<EditText>(R.id.inputCode3)
val inputCode4 = findViewById<EditText>(R.id.inputCode4)
val verifyOTPButtonOne = findViewById<Button>(R.id.verifyOTPButtonOne)
verifyOTPButtonOne.setOnClickListener {
if(inputCode1.text.isEmpty() || inputCode2.text.isEmpty() || inputCode3.text.isEmpty() || inputCode4.text.isEmpty()){
Toast.makeText(applicationContext,"Please Enter Correct OTP",Toast.LENGTH_SHORT).show()
} else {
val intent = Intent(this, VerifySuccess::class.java)
startActivity(intent)
}
}
}
}
My title is not displaying in the app.
I think there is a problem with the intent.
So, my main file is the login page and the code is:
class MainActivity : AppCompatActivity()
{
lateinit var username: EditText
lateinit var password1: EditText
lateinit var logIn: Button
lateinit var signup: TextView
lateinit var forgotpassword: TextView
val user="DiyaK"
val pass=arrayOf("Diya#2826","Sidd#2826","Prat#2826","Prash#2826")
var titlename: String? = "DiyaK"
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_scroll)
println("Create called")
title = "Login Page"
username=findViewById(R.id.username)
password1=findViewById(R.id.password1)
logIn=findViewById(R.id.logIn)
signup=findViewById(R.id.signup)
forgotpassword=findViewById(R.id.forgotpassword)
logIn.setOnClickListener {
val user1= username.text.toString()
val pass1=password1.text.toString()
var name="Diya K"
if(user1==user) {
if (pass1 == pass[0]) {
val intent = Intent(this#MainActivity, NewActivity::class.java)
startActivity(intent)
name="Diya K"
intent.putExtra("Name",name)
} else if (pass1 == pass[1]) {
val intent2 = Intent(this#MainActivity, NewActivity6::class.java)
startActivity(intent2)
name="Siddhant K"
intent2.putExtra("Name",name)
} else if (pass1 == pass[2]) {
val intent3 = Intent(this#MainActivity, NewActivity3::class.java)
startActivity(intent3)
name="Pratibha K"
intent3.putExtra("Name",name)
} else if (pass1 == pass[3]) {
val intent4 = Intent(this#MainActivity, NewActivity4::class.java)
startActivity(intent4)
name="Prashant K"
intent4.putExtra("Name",name)
}
} else {
Toast.makeText(this#MainActivity, "Incorrect! Try again", Toast.LENGTH_SHORT)
.show()
}
}
}
}
And, I have created 4 new activities, like for each different page and the code for all are similar, so I am showing the code for one only.
class NewActivity : AppCompatActivity() {
lateinit var img: ImageView
lateinit var state: TextView
lateinit var btn: Button
lateinit var btn1: Button
lateinit var header: TextView
lateinit var subheader: TextView
lateinit var hobby: TextView
var title1: String? = "Diya K"
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_new1)
img = findViewById(R.id.img)
state = findViewById(R.id.state)
btn = findViewById(R.id.btn)
btn1 = findViewById(R.id.btn1)
header = findViewById(R.id.header)
subheader = findViewById(R.id.subheader)
hobby=findViewById(R.id.hobby)
btn1.setOnClickListener {
Toast.makeText(this#NewActivity, "Hurray", Toast.LENGTH_SHORT).show()
val intent1 = Intent(this#NewActivity, MainActivity::class.java)
startActivity(intent1)
}
if(intent != null) {
title1=intent.getStringExtra("Name")
}
title=title1
}
}
My title is not displaying in the app. I think there is a problem with the intent.
The problem is you are setting the title after starting the activity
startActivity(intent)
name="Diya K"
intent.putExtra("Name",name)
Solution is set the extras first before starting the activity. Just swap the lines like below:
name="Diya K"
intent.putExtra("Name",name)
startActivity(intent)
if(intent != null) {
title1=intent.getStringExtra("Name")
}
title=title1
header.setText(title)// you get value but not set
I am having a ton of trouble passing the product of two EditTexts to a TextView in another activity. Here is my code for MainActivity.
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val button1: Button = findViewById(R.id.button1)
val editText1: EditText = findViewById(R.id.editText1)
val editText2: EditText = findViewById(R.id.editText2)
val firstNumber = editText1.toString().toInt()
val secondNumber = editText2.toString().toInt()
val product = firstNumber * secondNumber
button1.setOnClickListener{
val intent = Intent(this, Activity2::class.java)
intent.putExtra("RESULT_PRODUCT", product)
startActivity(intent)
}
}
}
Here is my code for Activity2:
class Activity2 : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_2)
val product = intent.getIntExtra("RESULT_SUM", 0)
textView1.text = product.toString()
}
}
I am relatively new to Kotlin and Android Studio but this has caused crashes left and right.
First of all, You have to calculate the product inside OnClickListener to get correct result.
button1.setOnClickListener{
val firstNumber = editText1.text.toString().trim()
val secondNumber = editText2.text.toString().trim()
if(!(firstNumber.isEmpty() or secondNumber.isEmpty())) {
val product = firstNumber.toInt() * secondNumber.toInt()
val intent = Intent(this, Activity2::class.java)
intent.putExtra("RESULT_PRODUCT", product)
startActivity(intent)
} else {
//Show messages
}
}
And then you have to use the exact key RESULT_PRODUCT that you use in your activity to pass data through intent
val product = intent.getIntExtra("RESULT_PRODUCT", 0)
You are passing "RESULT_PRODUCT" from MainActivity but getting "RESULT_SUM" in your Activity2. You should use intent.getIntExtra("RESULT_PRODUCT", 0) in you second activity.
I am trying to pass two strings from AddNote to MainActivity. But it keeps getting null.
Unable to start activity (MainActivity)
java.lang.IllegalStateException: callingIntent.getStringExtra("intentTitle") must not be null
class MainActivity : AppCompatActivity() {
private val notes = arrayListOf<Note>()
private val db by lazy {
Room.databaseBuilder(this
,NoteDatabase::class.java
,"NoteDatabase.db")
.allowMainThreadQueries()
.build() }
lateinit var adapter: adapter
lateinit var title: String
lateinit var content: String
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
notes.addAll(db.dao().getNotes())
AddNote.setOnClickListener {
val i = Intent(this#MainActivity,AddNote::class.java)
startActivity(i)
}
// startActivity(Intent(this, AddNote::class.java))
val callingIntent = intent
title = callingIntent.getStringExtra("intentTitle")
content = callingIntent.getStringExtra("intentContent")
val note = Note(title,content)
val id = db.dao().insert(note)
note.id = id.toInt()
notes.add(note)
adapter = adapter(notes, db)
rootView.layoutManager = LinearLayoutManager(this)
rootView.adapter = adapter
}
override fun onResume() {
super.onResume()
notes.clear()
notes.addAll(db.dao().getNotes())
adapter.notifyDataSetChanged()
}
}
class AddNote : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.add_note)
var intentTitle = "Title"
var intentContent = "Content"
saveNote.setOnClickListener {
intentTitle = addTitle.text.toString()
intentContent = addContent.text.toString()
}
val i = Intent()
i.putExtra("title",intentTitle)
i.putExtra("content",intentContent)
startActivity(i)
}
}
You must start activity like this...
val intent = Intent(this, SecondActivity::class.java)
intent.putExtra("key", value)
startActivity(intent)
You must put the code that starts MainActivity inside saveNote.setOnClickListener:
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.add_note)
var intentTitle = "Title"
var intentContent = "Content"
saveNote.setOnClickListener {
intentTitle = addTitle.text.toString()
intentContent = addContent.text.toString()
val i = Intent(this, MainActivity::class.java)
i.putExtra("title",intentTitle)
i.putExtra("content",intentContent)
startActivity(i)
}
}
The way your code worked was to start MainActivity as soon as AddNote activity was loaded, so I'm not sure what you are trying to do.