How to pass string from activity to another activity in kotlin - android-studio

I am trying to pass over a text to my second activity but it does not seem to be working correctly.
For my main activity i have a a textview and the text says 'Cat'. When the image on the first activity is pressed on, I want "Cat" to be passed over to the second activity. The second activity's textview should also display "Cat", however when I do this, it will show up as "Info(name=Cat)" rather then just "Cat".
I am not sure where i went wrong.
This is my main activity
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val image = findViewById<ImageView>(R.id.imageView)
val text = findViewById<TextView>(R.id.textView)
image.setOnClickListener {
val intent= Intent(this, MainActivity2::class.java).apply {
putExtra("name", Info("Cat"))
}
startActivity(intent)
}
}
}
My parcelable class
#Parcelize
data class Info(val name : String) : Parcelable {
}
This is my second activity
class MainActivity2 : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main2)
val info = intent.getParcelableExtra<Info>("name")
val name = findViewById<TextView>(R.id.textView2)
name.text = info.toString()
}
}

You're converting the whole Info data class to its string representation. You only want the name property from the data class.
name.text = info.name

Related

Update method of ViewModel class is not working

I am making this basic Notes App named Scribble
I wrote ViewModel class and all of its methods that is insert and delete are working fine but for some reason update method is not working, Its just not updating the passed note object
Please find code below :
First of all this is my Note i.e. Entity Class :
#Entity(tableName = "NotesTable")
data class Note constructor(
#ColumnInfo(name = "title") var noteTitle: String,
#ColumnInfo(name = "description") var noteDescription: String,
#ColumnInfo(name = "timeStamp") var timeStamp: String
):java.io.Serializable {
#PrimaryKey(autoGenerate = true)
#ColumnInfo(name = "id") var id : Int = 0
}
NoteViewModel Class :
class NoteViewModel (application: Application) : AndroidViewModel (application){
val allNotes: LiveData<List<Note>>
val repository: NoteRepository
init {
val dao = NoteDatabase.getDatabase(application).getNotesDao()
repository = NoteRepository(dao)
allNotes = repository.allNotes
}
fun insertNote(note: Note) {
viewModelScope.launch(Dispatchers.IO){
repository.insertNote(note)
}
}
fun deleteNote(note: Note) = viewModelScope.launch(Dispatchers.IO){repository.deleteNote(note)}
fun updateNote(note: Note) = viewModelScope.launch(Dispatchers.IO){repository.updateNote(note)}
Repository class :
class NoteRepository (private val noteDao: NoteDao) {
val allNotes : LiveData<List<Note>> = noteDao.getAllNotes()
suspend fun insertNote(note : Note){
noteDao.insert(note)
}
suspend fun deleteNote(note : Note){
noteDao.delete(note)
}
suspend fun updateNote(note : Note){
noteDao.update(note)
}
DAO class :
#Dao
public interface NoteDao {
#Insert(onConflict = OnConflictStrategy.IGNORE)
fun insert(note: Note)
#Update
fun update(note: Note)
#Delete
fun delete(note: Note)
#Query("Select * from NotesTable order by id ASC")
fun getAllNotes() : LiveData<List<Note>>
Now this is my EditNote Activity on which I come after clicking on a note card in my recycler view I am saving note when user presses back button after editing the note thus called updateNote method inside onDestroy :
class EditNoteActivity : AppCompatActivity() {
lateinit var titleOfNote : EditText
lateinit var bodyOfNote : EditText
lateinit var noteToUpdate: Note
lateinit var noteViewModel: NoteViewModel
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_edit_note)
noteViewModel = ViewModelProvider(this, ViewModelProvider.AndroidViewModelFactory.getInstance(application))[NoteViewModel::class.java]
titleOfNote = findViewById(R.id.titleEt)
bodyOfNote = findViewById(R.id.bodyEt)
noteToUpdate = intent.getSerializableExtra("noteObject") as Note
titleOfNote.setText(noteToUpdate.noteTitle)
bodyOfNote.setText(noteToUpdate.noteDescription)
}
fun updateNote(){
Log.d("AppLogs", "updateNote: called")
noteToUpdate.noteTitle = titleOfNote.text.toString()
noteToUpdate.noteDescription = bodyOfNote.text.toString()
val current = LocalDateTime.now()
val formatter = DateTimeFormatter.ofPattern("dd-MM-yyyy \nHH:mm")
noteToUpdate.timeStamp = current.format(formatter)
noteViewModel.updateNote(noteToUpdate)
}
override fun onDestroy() {
super.onDestroy()
Log.d("AppLogs", "onDestroy(updateNote): called")
updateNote()
}
I've used literally the same logic in AddNoteActivity and it works, it saves my note when i press back button and recycler view shows it perfectly
AddNoteActivity class :
class AddNoteActivity : AppCompatActivity() {
lateinit var noteTitle : EditText
lateinit var noteBody : EditText
lateinit var noteViewModel: NoteViewModel
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_add_note)
noteTitle = findViewById(R.id.noteTitleEt)
noteBody = findViewById(R.id.noteBodyEt)
noteViewModel = ViewModelProvider(this,ViewModelProvider.AndroidViewModelFactory.getInstance(application))[NoteViewModel::class.java]
}
fun saveNote(){
Log.d("AppLogs", "saveNote: called")
var titleOfNote : String = noteTitle.text.toString()
var bodyOfNote : String = noteBody.text.toString()
val current = LocalDateTime.now()
val formatter = DateTimeFormatter.ofPattern("dd-MM-yyyy \nHH:mm")
val timeStamp = current.format(formatter)
var note = Note(titleOfNote,bodyOfNote,timeStamp)
noteViewModel.insertNote(note)
}
override fun onStop() {
super.onStop()
Log.d("AppLogs", "onStop: called !")
saveNote()
}
}
means insertNote method from NoteViewModel works fine, similarly deleteNote also works fine, it deletes the note when I press delete button its just something wrong with updateNote method that I am not able to figure out
OTHER CLASSES ::::
NoteViewAdapter Class :
class NoteViewAdapter( allNotes : List<Note>, context: Context, clickListeners: ClickListeners)
: RecyclerView.Adapter<NoteViewAdapter.NoteViewHolder>() {
private val clickListeners : ClickListeners
private val context : Context
private var allNotes : List<Note>
init {
this.clickListeners = clickListeners
this.context = context
this.allNotes = allNotes
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int)
: NoteViewAdapter.NoteViewHolder {
val itemView = LayoutInflater.from(parent.context).inflate(R.layout.note_row,parent,false)
return NoteViewHolder(itemView)
}
override fun onBindViewHolder(holder: NoteViewHolder, position: Int) {
holder.noteTitleTV.text = allNotes[position].noteTitle
holder.noteBodyTV.text = allNotes[position].noteDescription
holder.timeStampTV.text = allNotes[position].timeStamp
holder.deleteButton.visibility = View.INVISIBLE
holder.deleteButton.setOnClickListener {
clickListeners.onDeleteButtonClick(allNotes[position])
}
holder.itemView.setOnClickListener{
clickListeners.onNoteClick(allNotes[position])
}
holder.itemView.setOnLongClickListener{
holder.deleteButton.visibility = View.VISIBLE
val timer = Timer("schedule", true);
timer.schedule(2000) {
holder.deleteButton.visibility = View.INVISIBLE
}
return#setOnLongClickListener true
}
}
override fun getItemCount(): Int {
return allNotes.size
}
inner class NoteViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView){
val noteTitleTV: TextView = itemView.findViewById(R.id.titleTV)
val noteBodyTV: TextView = itemView.findViewById(R.id.bodyTV)
val deleteButton: FloatingActionButton = itemView.findViewById(R.id.deleteButton)
val timeStampTV : TextView = itemView.findViewById(R.id.timeStamp)
}
}
interface ClickListeners {
fun onNoteClick(note: Note)
fun onDeleteButtonClick(note: Note)
}
MainActivity class :
class MainActivity : AppCompatActivity(), ClickListeners {
lateinit var recyclerView : RecyclerView
lateinit var addButton : FloatingActionButton
lateinit var viewModel : NoteViewModel
lateinit var adapter : NoteViewAdapter
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
recyclerView = findViewById(R.id.NotesRV)
addButton = findViewById(R.id.addButton)
viewModel = ViewModelProvider(this,ViewModelProvider.AndroidViewModelFactory.getInstance(application))[NoteViewModel::class.java]
viewModel.allNotes.observe(this, Observer {list ->
adapter = NoteViewAdapter(list ,this, this)
recyclerView.adapter = adapter
})
recyclerView.layoutManager = GridLayoutManager(this,2,GridLayoutManager.VERTICAL,false)
addButton.setOnClickListener{
val intent : Intent = Intent(this,AddNoteActivity::class.java)
startActivity(intent)
}
}
override fun onNoteClick(note: Note) {
val intent : Intent = Intent(this,EditNoteActivity::class.java)
intent.putExtra("noteObject",note)
startActivity(intent)
}
override fun onDeleteButtonClick(note: Note) {
viewModel.deleteNote(note)
}
Here I am passing note object with intent Extras because I implemented java.io.Serializable in Note class
When I click on note it opens edit note activity then I change the previous text set there, but when i press back button it doesnt update note
I used debug logs and found that both onDestroy and updateNote are getting called but still it is note updating my note
Also these lines from EditNoteActivity.kt are setting previous entered text in note after clicking on note card
titleOfNote.setText(noteToUpdate.noteTitle)
bodyOfNote.setText(noteToUpdate.noteDescription)
but for some reason this line isn't working
noteViewModel.updateNote(noteToUpdate)

How to display data in the form of images from the intent process sent using arraylist and parcelable?

class DetailActivity : AppCompatActivity() {
companion object {
const val EXTRA_USER = "extra_user"
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_detail)
val imgAvatar:CircleImageView = findViewById(R.id.img_dtlavtr)
val txtName:TextView = findViewById(R.id.txt_names)
val txtUname:TextView = findViewById(R.id.txt_uname)
val users = intent.getParcelableArrayListExtra<User>(EXTRA_USER) as ArrayList<User>
imgAvatar.setImageResource(users)
txtName.text
txtUname.text
}
}
It's my first time learning Android Studio and Kotlin,
Above is the second activity DetailActivity.kt, in the section I will display an image (imgAvatar.setImageresource(users)), an error appears
I use arraylist and parcelable to send data sets in the form of images and text
"Type missmatch
Required: Int
Found: kotlin.collection. ArrayList "
and here's the code for MainActivity.kt
class MainActivity : AppCompatActivity() {
private lateinit var adapter: UserAdapter
private lateinit var dataName: Array<String>
private lateinit var dataUname: Array<String>
private lateinit var dataAvatar: TypedArray
private var users = arrayListOf<User>()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val listView: ListView = findViewById(R.id.lv_list)
adapter = UserAdapter(this)
listView.adapter = adapter
prepare()
addItem()
listView.onItemClickListener = AdapterView.OnItemClickListener {
_, _, position, _ ->
//Toast.makeText(this#MainActivity, users[position].name, Toast.LENGTH_SHORT).show()
val detailIntent = Intent(this#MainActivity, DetailActivity::class.java)
detailIntent.putParcelableArrayListExtra(EXTRA_USER, users)
startActivity(detailIntent)
}
}
private fun addItem() {
for (position in dataName.indices) {
val user = User(
dataAvatar.getResourceId(position, -1),
dataName[position],
dataUname[position]
)
users.add(user)
}
adapter.users = users
}
private fun prepare() {
dataName = resources.getStringArray(R.array.name)
dataUname = resources.getStringArray(R.array.username)
dataAvatar = resources.obtainTypedArray(R.array.avatar)
}
}
Firstly, please check the android documentations about imageview setImageResource
It only accepts a single resource integer. i.e. you can give image sources such as
setImageResource(R.drawable.yourImage)
R.drawable.yourImage actually is an int. That is why exception says you that it expects int however found list.

Make a TabBar inside a specific NavBar fragment

my app use bottomNavBar with 3 fragments [Settings / Home / Gallery].
Now I'm trying to make a TabBar with 3 new Tabs inside of "Settings". [setting01 / setting02 / setting03]
I followed carefully this tutorial : https://www.youtube.com/watch?v=qfFANw7nPMU
and I used viewPager2 when the video uses viewPager.
but I'm still stuck in MainActivity.kt because for some reasons these two line (from the tutorial) didn't work for me :
viewPager2.adapter = PageAdapter(supportFragmentManager)
tabLayout.setupWithViewPager(viewPager2)
here is my entire MainActivity.kt file :
package com.example.appname
import android.os.Bundle
import com.google.android.material.bottomnavigation.BottomNavigationView
import androidx.appcompat.app.AppCompatActivity
import androidx.navigation.findNavController
import androidx.navigation.ui.AppBarConfiguration
import androidx.navigation.ui.setupActionBarWithNavController
import androidx.navigation.ui.setupWithNavController
import com.example.appname.ui.settings.PageAdapter
import kotlinx.android.synthetic.main.fragment_settings.*
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val navView: BottomNavigationView = findViewById(R.id.nav_view)
val navController = findNavController(R.id.nav_host_fragment)
val appBarConfiguration = AppBarConfiguration(setOf(
R.id.navigation_settings, R.id.navigation_swapper, R.id.navigation_gallery))
setupActionBarWithNavController(navController, appBarConfiguration)
navView.setupWithNavController(navController)
viewPager2.adapter = PageAdapter(supportFragmentManager)
tabLayout.setupWithViewPager(viewPager2)
}
}
I'm almost certain that this error exists because these two lines should be somewhere else because of the particular situation of my application.
To find yourself in a situation similar to mine I recommend you to start a new project under android studio using the template "Bottom Navigation Activity" and then follow the tutorial.
A friend of mine give me the solution.
Nothing new in MainActivity.kt like I was thinking.
Here is the new code to make tabBar in the Settings fragment :
settingFragment.kt
class SettingsFragmentAdapter(fragment: Fragment) : FragmentStateAdapter(fragment) {
private val fragments =
listOf(
fragment_setting_teams(),
fragment_setting_general(),
fragment_setting_movies()
)
override fun getItemCount(): Int = fragments.size
override fun createFragment(position: Int): Fragment = fragments[position]
}
class SettingsFragment : Fragment() {
private lateinit var settingsViewModel: SettingsViewModel
private lateinit var settingsAdapter: SettingsFragmentAdapter
private lateinit var viewPager: ViewPager2
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
val tabLayout = view.findViewById<TabLayout>(R.id.tabLayout)
settingsAdapter =
SettingsFragmentAdapter(
this
)
viewPager = view.findViewById(R.id.viewPager2)
viewPager.adapter = settingsAdapter
TabLayoutMediator(tabLayout, viewPager) { tab, position ->
when (position) {
0 -> tab.text = "Teams"
1 -> tab.text = "General"
2 -> tab.text = "Movies"
}
}.attach()
}
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
settingsViewModel =
ViewModelProviders.of(this).get(SettingsViewModel::class.java)
val root = inflater.inflate(R.layout.fragment_settings, container, false)
return root
}
}

Error when calling the second activity using a button

I have a problem calling a second activity using a password recovery button. I get the app stopped.
import android.content.Intent
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.view.View
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
}
fun goToRecover(view: View) {
val intent = Intent(this,RecoverPasswordActivity::class.java)
this.startActivity(intent)
}
}
Did you try adding the onClick in the XML-layout like this?
android:onClick="/*your method name*/"
If so, make sure that there are no typos. In your case it would probably be:
android:onClick="addOne"
OR
You can also listen for the click events programmatically. like below:
set the Button in OnCreate method:
var goToRecoverBtn = findViewById(R.id./*id name here*/)
after that add the ClickListener to the goToRecoverBtn:
goToRecoverBtn.setOnClickListener{
// do anything whatever you want
}
OR
MainActivity code looks like below
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
var goToRecoverBtn = findViewById(R.id./*id name here*/)
goToRecoverBtn.setOnClickListener{
val intent = Intent(this#MainActivity, RecoverPasswordActivity::class.java)
startActivity(intent)
}
}
}
BTW could you please show us your .xml file?

Can I Inject activity's viewmodel and use It inside Dialog Fragment

I am trying to Inject activity's view model and I want use it inside Dialog Fragment, how to inject it with Kodein? and use the view model which I've inject before in other activity or fragments.
I've try tutorial from medium here is the tutorial
https://proandroiddev.com/android-viewmodel-dependency-injection-with-kodein-249f80f083c9
I am trying to access injection result from activity's view model
but when I try access some object inside the viewModel from Dialog Fragment the value is null not the same with activity's viewModel
// This is from Activity
private val viewModelFactory: TriplogisticViewModelFactory by instance()
private val viewModel: TriplogisticViewModel by lazy {
ViewModelProviders
.of(this#ContactDetailBottomSheetDialogFragment, viewModelFactory)
.get(TriplogisticViewModel::class.java)
}
Log.e("VIEWMODEL_ACTIVITY", viewModel.mode.get().toString) // result is SENDER_MODE
I am expecting when I access some object inside the dialog fragment's viewModel, I got same value as activity's viewModel
// This is from Dialog Fragment
private val viewModelFactory: TriplogisticViewModelFactory by instance()
private val viewModel: TriplogisticViewModel by lazy {
ViewModelProviders
.of(this#ContactDetailBottomSheetDialogFragment, viewModelFactory)
.get(TriplogisticViewModel::class.java)
}
I want same result as activity's viewModel object but I got null result
Log.e("VIEWMODEL_FRAGMENT", viewModel.mode.get().toString) // result is null
You have to use shared ViewModel. Use activity owner for instantiating the ViewModel. Looks like:
class SharedViewModel : ViewModel() {
val selected = MutableLiveData<Item>()
fun select(item: Item) {
selected.value = item
}
}
class MasterFragment : Fragment() {
private lateinit var itemSelector: Selector
private lateinit var model: SharedViewModel
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
model = activity?.run {
ViewModelProviders.of(this)[SharedViewModel::class.java]
} ?: throw Exception("Invalid Activity")
itemSelector.setOnClickListener { item ->
// Update the UI
}
}
}
class DetailFragment : Fragment() {
private lateinit var model: SharedViewModel
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
model = activity?.run {
ViewModelProviders.of(this)[SharedViewModel::class.java]
} ?: throw Exception("Invalid Activity")
model.selected.observe(this, Observer<Item> { item ->
// Update the UI
})
}
}
For details click here

Resources