Lifecycle in Activity is not triggered in ViewModel - android

I am trying to execute a method(fun onCreateViewModel()) in viewModel whenever the onCreate of the Activity is invoked. But the method is not getting executed.
ViewModel
class MainActivityViewModel(startingCount : Int) : ViewModel(), LifecycleObserver {
var count = 0
init {
count = startingCount
}
fun getCurrentCount(): Int{
return count
}
fun getUpdatedCount(): Int {
count ++
return count
}
#OnLifecycleEvent(Lifecycle.Event.ON_CREATE)
public fun onCreateViewModel(){
Log.i("Jts"," ViewModel created")
}
}
Activity
class MainActivity : AppCompatActivity() {
private lateinit var binding: ActivityMainBinding
private var user = User("Jeffin T", "26")
private lateinit var mainActivityViewModel: MainActivityViewModel
private lateinit var mainActivityViewModelFactory: MainActivityViewModelFactory
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// mainActivityViewModel = ViewModelProviders.of()
mainActivityViewModelFactory = MainActivityViewModelFactory(123)
mainActivityViewModel = ViewModelProviders.of(this, mainActivityViewModelFactory)
.get(MainActivityViewModel::class.java)
binding = DataBindingUtil.setContentView(this, R.layout.activity_main)
binding.tvName.text = mainActivityViewModel.getCurrentCount().toString()
binding.user = user
binding.btnName.setOnClickListener {
binding.tvName.text = mainActivityViewModel.getUpdatedCount().toString()
}
}
private fun displayGreeting() {
binding.apply { tvName.setText("Hello! " + user?.name) }
}
}

You have to add the viewmodel as an observer of the lifecycle of your activity. You need to add this in your Activity
getLifecycle().addObserver(viewModel)
More info here

Related

How can i get my args.field from my activity to my viewmodel

I want to use my args.breweryName from my reviewActivity in my reviewViewModel but i don't know what's the best way to do it. I have tried using savestatehandle but i'm not experienced with that. I'm new to programming with kotlin so i would appreciate the help!
My ReviewActivity
class ReviewActivity : AppCompatActivity() {
val breweryText: TextView
get() = findViewById(R.id.breweryName)
private val args: ReviewActivityArgs by navArgs<ReviewActivityArgs>()
private var shownFragment: Fragment? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_review)
initView()
if (savedInstanceState == null) {
showFragment(ReviewFragment.newInstance())
}
breweryText.text = args.breweryName
}
fun test(): String {
return args.breweryName
}
private fun initView() {
val button : FloatingActionButton = findViewById(R.id.add)
showFragment(ReviewFragment.newInstance())
button.setOnClickListener { showSaveDialog() }
}
private fun showFragment(fragment: Fragment) {
val fragmentTransaction: FragmentTransaction = supportFragmentManager.beginTransaction()
fragmentTransaction.replace(R.id.fragmentHolder, fragment)
fragmentTransaction.commitNow()
shownFragment = fragment
}
private fun showSaveDialog() {
val dialogFragment: DialogFragment
val tag: String
dialogFragment = ReviewDialog.newInstance(null, null, args.breweryName)
tag = ReviewDialog.TAG_DIALOG_REVIEW_SAVE
dialogFragment.show(supportFragmentManager, tag)
}
My ReviewViewModel
class ReviewViewModel constructor(application: Application) : AndroidViewModel(application) {
private val reviewDao: ReviewDao = ReviewsDatabase.getDatabase(application).reviewDao()
private val userDao: UserDao = ReviewsDatabase.getDatabase(application).userDao()
val data = MutableLiveData<String>()
val reviewList: LiveData<List<Review>>
val userList: LiveData<List<User>>
fun data(item: String) {
data.value = item
}
init {
reviewList = reviewDao.allReviews
userList = userDao.allUsers
}
fun insert(vararg reviews: Review) {
reviewDao.insert(*reviews)
}
fun update(review: Review) {
reviewDao.update(review)
}
fun deleteAll() {
reviewDao.deleteAll()
}
}

How to get an instance of ViewModel

I'm learning Kotlin from Java, and I'm trying to figure out how to create and store the ViewModel. Storing it outside the main activity will cause it to crash, but storing it inside the main activity means my private variables cannot access them, so I get an error.
Here's the code I have
class MainActivity : AppCompatActivity() {
private lateinit var trueButton: Button
private lateinit var falseButton: Button
private lateinit var nextButton: Button
private lateinit var prevButton: Button
private lateinit var questionTextView: TextView
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val quizViewModel: QuizViewModel = ViewModelProvider(this).get(QuizViewModel::class.java)
trueButton = findViewById(R.id.true_button)
falseButton = findViewById(R.id.false_button)
nextButton = findViewById(R.id.next_button)
prevButton = findViewById(R.id.back_button)
questionTextView = findViewById(R.id.question_text)
trueButton.setOnClickListener {
checkAnswer(true)
}
falseButton.setOnClickListener {
checkAnswer(false)
}
nextButton.setOnClickListener {
quizViewModel.moveToNext()
updateQuestion()
}
prevButton.setOnClickListener {
quizViewModel.moveToBack()
updateQuestion()
}
updateQuestion()
}
private fun checkAnswer(userAnswer: Boolean){
val correctAnswer = quizViewModel.currentQuestionAnswer
val messageResId = if (userAnswer == correctAnswer) {
R.string.correct_toast
} else {
R.string.incorrect_toast
}
Toast.makeText(
this,
messageResId,
Toast.LENGTH_SHORT
).show()
}
private fun updateQuestion() {
val questionTextResID = quizViewModel.currentQuestionText
questionTextView.setText(questionTextResID)
}
}
Unfortunately, putting the ViewModel inside my onCreate means I can't access them in my functions, namely checkAnswer and updateQuestion.
import androidx.lifecycle.ViewModel
class QuizViewModel : ViewModel() {
private val questionBank = listOf(
Question(R.string.question_aus, true),
Question(R.string.question_ocean, true),
Question(R.string.question_mideast, false),
Question(R.string.question_africa, false),
Question(R.string.question_america, true),
Question(R.string.question_asia, true)
)
var currentIndex = 0
val currentQuestionAnswer: Boolean
get() = questionBank[currentIndex].answer
val currentQuestionText: Int
get() = questionBank[currentIndex].textResId
fun moveToNext(){
currentIndex = (currentIndex + 1) % questionBank.size
}
fun moveToBack(){
currentIndex = (currentIndex - 1) % questionBank.size
}
}
And my error
e: C:\Users\Jacob\AndroidStudioProjects\GeoQuiz\app\src\main\java\com\jacobjumper\geoquiz\MainActivity.kt: (54, 29): Unresolved reference: quizViewModel
just move the quizViewModel to the rest of the variables.
so like
class MainActivity : AppCompatActivity() {
private lateinit var trueButton: Button
private lateinit var falseButton: Button
private lateinit var nextButton: Button
private lateinit var prevButton: Button
private lateinit var questionTextView: TextView
private lateinit var quizViewModel: QuizViewModel
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
quizViewModel = ViewModelProvider(this).get(QuizViewModel::class.java)

How to keep fragment in activity during orientation change?

I have framelayout in activity and two fragments that I need to attach to the activity depending on the user's choise. When I attach one fragment and change orientation, my fragment gest destrotroyed with activity. How to keep the state of fragment inside activity?
This is my menuActivity which has framelayout.
class MenuActivity : AppCompatActivity(), OptionsFragment.PassFragment {
private val TAG = "Menu"
private lateinit var binding: ActivityMenuBinding
private lateinit var informationViewModel: InformationViewModel
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityMenuBinding.inflate(layoutInflater)
setContentView(binding.root)
informationViewModel = ViewModelProvider(this).get(InformationViewModel::class.java)
Log.i(TAG, "onCreate: code: ${informationViewModel.hashCode()}")
if (informationViewModel.fragment != null) {
showFragment(informationViewModel.fragment!!)
} else {
showFragment(OptionsFragment())
}
}
private fun showFragment(fragment: Fragment) {
supportFragmentManager.beginTransaction().replace(R.id.container, fragment)
.commit()
}
override fun onPassFragment(fragment: Fragment) {
informationViewModel.fragment = fragment
}
}
This is my first fragment which should appear first in activity and it has two textviews for two fragments.
class OptionsFragment : Fragment(R.layout.options_fragment) {
private lateinit var txtPersonal: TextView
private lateinit var txtSecond: TextView
private lateinit var informationViewModel: InformationViewModel
private lateinit var passFragment: PassFragment
private val TAG = "OptionsFragment"
interface PassFragment {
fun onPassFragment(fragment: Fragment)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
txtPersonal = view.findViewById(R.id.person_det)
txtSecond = view.findViewById(R.id.trading_currency)
informationViewModel = ViewModelProvider(this).get(InformationViewModel::class.java)
Log.i(TAG, "onViewCreated: code: ${informationViewModel.hashCode()} ")
txtPersonal.setOnClickListener {
passFragment(PersonalInfFragment())
requireActivity().supportFragmentManager.beginTransaction()
.replace(R.id.container, PersonalInfFragment()).commit()
}
txtSecond.setOnClickListener {
passFragment(TradingFragment())
requireActivity().supportFragmentManager.beginTransaction()
.replace(R.id.container, TradingFragment()).commit()
}
}
private fun passFragment(fragment: Fragment) {
try {
passFragment = activity as PassFragment
passFragment.onPassFragment(fragment)
} catch (ex: ClassCastException) {
}
}
}
Let's say from above options fragment, I opened my second TradingFragment and showed some information from retrofit using the same viewmodel.
class TradingFragment : Fragment(R.layout.currency_trading) {
private val TAG = "TradingFragment"
private lateinit var informationViewModel: InformationViewModel
private lateinit var rec_view: RecyclerView
private lateinit var btnSearch: Button
private lateinit var adapter: TradingAdapter
private lateinit var rangeSeek: RangeSlider
private lateinit var txtStartDate: TextView
private lateinit var txtFinishData: TextView
private lateinit var spinner: Spinner
private var from = 0
private var to = 0
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
btnSearch = view.findViewById(R.id.btnSearch)
rec_view = view.findViewById(R.id.rec_view)
rangeSeek = view.findViewById(R.id.rangeSlider)
txtStartDate = view.findViewById(R.id.txtFrom)
txtFinishData = view.findViewById(R.id.txtTo)
spinner = view.findViewById(R.id.spinner)
informationViewModel = ViewModelProvider(this).get(InformationViewModel::class.java)
val cur_time = "${System.currentTimeMillis()}".substring(0, 10)
rangeSeek.valueTo = Integer.valueOf(cur_time) * 1.0f
initRecyclerView()
rangeSeek.addOnSliderTouchListener(object : RangeSlider.OnSliderTouchListener {
override fun onStartTrackingTouch(slider: RangeSlider) {
}
override fun onStopTrackingTouch(slider: RangeSlider) {
changeDateValueForText(slider.values.get(0).toInt(), slider.values.get(1).toInt())
}
})
btnSearch.setOnClickListener {
startSearchingCurrencyTrading()
}
}
private fun startSearchingCurrencyTrading() {
val pair = spinner.selectedItem.toString()
informationViewModel.startRequestingCurrency(pair, from, to)
}
private fun changeDateValueForText(start: Int, finish: Int) {
from = start
to = finish
val startDate = Date(start * 1000L)
val finishDate = Date(finish * 1000L)
val df = SimpleDateFormat("dd:MM:yyyy")
txtStartDate.text = df.format(startDate)
txtFinishData.text = df.format(finishDate)
}
private fun initRecyclerView() {
adapter = TradingAdapter()
rec_view.adapter = adapter
rec_view.layoutManager = LinearLayoutManager(requireActivity())
informationViewModel.returnCurrentTrading().observe(this) {
if (it != null) {
adapter.updateTradingItems(it)
} else {
adapter.updateTradingItems(listOf())
}
}
}
}
This is my current solution it worked, but I lost my previous query result to retrofit that was being showed on the tradingfragment before orientation change.
Rotation of the screen is defined a configuration change and If you want to preserve the state of your UI I suggest you to implent a ViewModel as described here
However, you have several options to preserve the state of the UI, the choose depends on your needs, all possible scenarios are well explained in the official documentation.

Firebase data changing forever

im new to making app and trying to make a task list app using android studio, firebase, and kotlin. In MainActivity, i show the list of task that stored in fire base. then there is button to add a new task and store it into firebase. if i click on a task at MainActivity, the app will move to EditTaskActivity, which contain some EditText element, and if user fill that EditText, then press button "Save Change" in the EditTaskActivity, it supposed to store the data change in firebase. But the problem is, when i press the "Save Change" button, the data in firebase keep changing forever between the old data and new data, and make the app can't stopped working.
This is my MainActivity code:
class MainActivity : AppCompatActivity() {
private lateinit var titlePage: TextView
private lateinit var subtitlePage: TextView
private lateinit var endPage: TextView
private lateinit var rvTasks: RecyclerView
private var list: ArrayList<Task> = arrayListOf()
private lateinit var reference: DatabaseReference
private lateinit var btnAddNew: Button
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
titlePage=findViewById(R.id.title_page)
subtitlePage=findViewById(R.id.subtitle_page)
endPage=findViewById(R.id.end_page)
btnAddNew=findViewById(R.id.btn_addnew)
rvTasks = findViewById(R.id.rv_taskList)
rvTasks.setHasFixedSize(true)
rvTasks.layoutManager = LinearLayoutManager(this)
reference = FirebaseDatabase.getInstance().reference.child("TaskListApp")
reference.addValueEventListener( object: ValueEventListener {
override fun onDataChange(dataSnapshot: DataSnapshot){
for(dataSnapshot1 in dataSnapshot.children){
var task: Task? = dataSnapshot1.getValue<Task>(Task::class.java)
if (task != null) {
list.add(task)
}
}
val taskAdapter = TaskAdapter(list,assets)
rvTasks.adapter = taskAdapter
taskAdapter.notifyDataSetChanged()
}
override fun onCancelled(databaseError: DatabaseError){
Toast.makeText(applicationContext, "No Data", Toast.LENGTH_SHORT).show()
}
} )
btnAddNew.setOnClickListener { v ->
if(v.id == R.id.btn_addnew){
val moveToAddNew = Intent(this#MainActivity, AddNewTask::class.java)
startActivity(moveToAddNew)
}
}
}
}
This is My TaskAdapter code:
class TaskAdapter(private val taskList: ArrayList<Task>, assets: AssetManager): RecyclerView.Adapter<TaskAdapter.ListViewHolder>() {
lateinit var someContext: Context
inner class ListViewHolder(itemView: View): RecyclerView.ViewHolder(itemView){
var taskTitle: TextView = itemView.findViewById(R.id.task_title)
var taskDesc: TextView = itemView.findViewById(R.id.task_desc)
var taskDate: TextView = itemView.findViewById(R.id.task_date)
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ListViewHolder {
val view: View = LayoutInflater.from(parent.context).inflate(R.layout.item_tasks, parent, false)
someContext = parent.context
return ListViewHolder(view)
}
override fun getItemCount(): Int {
return taskList.size
}
override fun onBindViewHolder(holder: ListViewHolder, position: Int) {
val task = taskList[position]
holder.taskTitle.text = task.taskTitle
holder.taskDesc.text = task.taskDesc
holder.taskDate.text = task.taskDate
holder.itemView.setOnClickListener {
val moveToEdit = Intent(someContext, EditTask::class.java)
moveToEdit.putExtra("tasktitle", task.taskTitle)
moveToEdit.putExtra("taskdesc", task.taskDesc)
moveToEdit.putExtra("taskdate", task.taskDate)
moveToEdit.putExtra("taskkey", task.taskKey)
someContext.startActivity(moveToEdit)
}
}
}
and this is my EditTaskActivity code:
class EditTask : AppCompatActivity() {
private lateinit var pageTitle: TextView
private lateinit var addNewTaskTitle: TextView
private lateinit var inputTaskTitle: EditText
private lateinit var addNewTaskDesc: TextView
private lateinit var inputNewTaskDesc: EditText
private lateinit var addNewTaskDueDate: TextView
private lateinit var inputNewTaskDueDate: EditText
private lateinit var btnSaveChange: Button
private lateinit var btnDeleteTask: Button
private lateinit var btnCancel: Button
private lateinit var reference: DatabaseReference
private lateinit var taskKey: String
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_edit_task)
taskKey = intent?.getStringExtra("taskkey").toString()
pageTitle=findViewById(R.id.add_new_task_title_page)
pageTitle.typeface=CircularBlack
addNewTaskTitle=findViewById(R.id.new_task_title)
addNewTaskTitle.typeface=CircularMedium
inputTaskTitle=findViewById(R.id.add_task_title)
inputTaskTitle.typeface=CircularBook
inputTaskTitle.setText(intent.getStringExtra("tasktitle"))
addNewTaskDesc=findViewById(R.id.new_task_desc)
addNewTaskDesc.typeface=CircularMedium
inputNewTaskDesc=findViewById(R.id.add_task_desc)
inputNewTaskDesc.typeface=CircularBook
inputNewTaskDesc.setText(intent.getStringExtra("taskdesc"))
addNewTaskDueDate=findViewById(R.id.new_task_time)
addNewTaskDueDate.typeface=CircularMedium
inputNewTaskDueDate=findViewById(R.id.add_task_time)
inputNewTaskDueDate.setText(intent.getStringExtra("taskdate"))
reference = FirebaseDatabase.getInstance().reference.child("TaskListApp").child("Task$taskKey")
btnSaveChange=findViewById(R.id.btn_save_change)
btnSaveChange.setOnClickListener{ v ->
if(v.id==R.id.btn_save_change) {
reference.addValueEventListener(object : ValueEventListener {
override fun onDataChange(dataSnapshot: DataSnapshot) {
dataSnapshot.ref.child("taskTitle").setValue(inputTaskTitle.text.toString())
dataSnapshot.ref.child("taskDesc").setValue(inputNewTaskDesc.text.toString())
dataSnapshot.ref.child("taskDate").setValue(inputNewTaskDueDate.text.toString())
startActivity(Intent(this#EditTask, MainActivity::class.java))
}
override fun onCancelled(databaseError: DatabaseError) {
Toast.makeText(
applicationContext,
"Action Cancelled, Error Occured",
Toast.LENGTH_SHORT
).show()
startActivity(Intent(this#EditTask, MainActivity::class.java))
}
})
}
}
btnDeleteTask=findViewById(R.id.btn_delete_task)
btnDeleteTask.setOnClickListener{v ->
if(v.id==R.id.btn_delete_task){
reference.removeValue().addOnCompleteListener {
if(it.isSuccessful){
startActivity(Intent(this#EditTask, MainActivity::class.java))
} else {
Toast.makeText(applicationContext, "Failed to delete task", Toast.LENGTH_SHORT).show()
}
}
}
}
btnCancel=findViewById(R.id.btn_cancel_edit)
btnCancel.setOnClickListener{ v ->
if(v.id == R.id.btn_cancel_edit) {
startActivity(Intent(this#EditTask, MainActivity::class.java))
}
}
}
}
and this is my AddNewTask activity code:
class AddNewTask : AppCompatActivity(), View.OnClickListener {
private lateinit var pageTitle: TextView
private lateinit var addNewTaskTitle: TextView
private lateinit var inputTaskTitle: EditText
private lateinit var addNewTaskDesc: TextView
private lateinit var inputNewTaskDesc: EditText
private lateinit var addNewTaskDueDate: TextView
private lateinit var inputNewTaskDueDate: EditText
private lateinit var btnCreateTask: Button
private lateinit var btnCancel: Button
private lateinit var reference: DatabaseReference
var taskNum: Int = Random.nextInt()
private var taskKey: String = taskNum.toString()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_add_new_task)
pageTitle=findViewById(R.id.add_new_task_title_page)
pageTitle.typeface=CircularBlack
addNewTaskTitle=findViewById(R.id.new_task_title)
inputTaskTitle=findViewById(R.id.add_task_title)
addNewTaskDesc=findViewById(R.id.new_task_desc)
inputNewTaskDesc=findViewById(R.id.add_task_desc)
addNewTaskDueDate=findViewById(R.id.new_task_time)
inputNewTaskDueDate=findViewById(R.id.add_task_time)
btnCreateTask=findViewById(R.id.btn_save_task)
btnCreateTask.setOnClickListener(this)
btnCancel=findViewById(R.id.btn_cancel)
btnCancel.setOnClickListener(this)
}
override fun onClick(v: View) {
when(v.id){
R.id.btn_save_task -> {
reference = FirebaseDatabase.getInstance().reference.child("TaskListApp").
child("Task$taskNum")
reference.addValueEventListener(object: ValueEventListener{
override fun onDataChange(dataSnapshot: DataSnapshot) {
dataSnapshot.ref.child("taskTitle").setValue(inputTaskTitle.text.toString())
dataSnapshot.ref.child("taskDesc").setValue(inputNewTaskDesc.text.toString())
dataSnapshot.ref.child("taskDate").setValue(inputNewTaskDueDate.text.toString())
dataSnapshot.ref.child("taskKey").setValue(taskKey)
startActivity(Intent(this#AddNewTask, MainActivity::class.java))
}
override fun onCancelled(databaseError: DatabaseError) {
Toast.makeText(applicationContext, "Action Cancelled", Toast.LENGTH_SHORT).show()
startActivity(Intent(this#AddNewTask, MainActivity::class.java))
}
})
}
R.id.btn_cancel -> {
startActivity(Intent(this#AddNewTask, MainActivity::class.java))
}
}
}
}
Try to use single value event listener instead of value event listener
Use
reference.addListenerForSingleValueEvent
Instead of
reference.addValueEventListener
Because value event listener is call all the time when data changed into Firebase child but single value event listener is only call one time. So, maybe it was generated the issue for you.
you can use ChildEventListener also with ValueEventListener.
Value listener will trigger method every time when data are changed in db and return all data
ChildEventListener also will will trigger method every time when data are changed in db but it's will returned item which is changes.
ChildEventListener listener= new ChildEventListener(){
onCancelled(DatabaseError error) {
// This method will be triggered in the event that this listener either failed at the server, or is removed as a result of the security and Firebase rules.
}
onChildAdded(DataSnapshot snapshot, String previousChildName){
// triggered when a new child is added to the location to which this listener was added.
//check and add new item to your adapter here
}
onChildChanged(DataSnapshot snapshot, String previousChildName){
// triggered when the data at a child location has changed.
}
onChildMoved(DataSnapshot snapshot, String previousChildName) {
// triggered when a child location's priority changes.
}
onChildRemoved(DataSnapshot snapshot) {
// triggered when a child is removed from the location to which this listener was added.
}
}
Then remove your value listener after read all items and add child listener to observe new items and changes
reference.addValueEventListener( object: ValueEventListener {
override fun onDataChange(dataSnapshot: DataSnapshot){
for(dataSnapshot1 in dataSnapshot.children){
var task: Task? = dataSnapshot1.getValue<Task>(Task::class.java)
if (task != null) {
list.add(task)
}
}
val taskAdapter = TaskAdapter(list,assets)
rvTasks.adapter = taskAdapter
taskAdapter.notifyDataSetChanged()
reference.removeListener(this);
reference.addValueEventListener(listener);
}

Android Studio Kotlin - Change EditText text from a fragment

I need to edit a editText which is on main_layout from a fragment instead of MainActivity().
I've tried inflating main_layout to fragment but that doesn't worked (editText doesn't change), then I've tried to create method
fun changeEditText(){
editText.setText(R.string.name)
}
but when I call it in my Fragment using
MainActivity().changeEditText()
it gives me this error:
java.lang.NullPointerException: Attempt to invoke virtual method 'android.content.pm.ApplicationInfo android.content.Context.getApplicationInfo()' on a null object reference
How can I edit this editText from fragment?
You can't initialize an activity via its constructors. You can call the method from fragment like below
((MainActivity)getActivity()).changeEditText();
You can call getActivity() from the Fragment to get a reference to its parent activity:
(activity as? MainActivity)?.changeEditText()
However, a better approach would be to use a listener so the fragment doesn't care what it's parent activity has implemented:
interface Listener {
fun onTextChanged()
}
fun changeEditText() {
editText.setText(R.string.name)
listener?.onTextChanged()
}
class MainActivity : AppCompatActivity() {
//override ......
fun changeText() {
EditText editText = findViewById(R.id.my_edittext)
edittext.text = "something"
}
}
class MyFragment : Fragment() {
private lateinit var hostActivity: AppCompatActivity
override fun onAttach(context: Context){
hostActivity = context as AppCompatActivity
}
override fun onViewCreated(){
hostActivity.changeText()
}
}
Or
class MainActivity :AppCompatActivity(), MyCallback() {
// override ......
override fun onTextChange(){
val editText = findViewById(R.id.my_edittext)
edittext.text = "something"
}
}
class MyFragment : Fragment() {
private lateinit var myCallback: MyCallback
override fun onAttach(context: Context){
myCallback = context as MyCallback
}
override fun onViewCreated(){
myCallback.onTextChange()
}
}
interface MyCallback {
fun onTextChange()
}
Or
class MainActivity :AppCompatActivity(), MyCallback() {
override fun onCreate(savedInstanceState: Bundle?){
val sharedViewmodel = ViewmodelProviders.of(this).get(SharedViewModel.class)
sharedViewmodel.text.observe(this, object: Observer<String> {
override fun onChanged(text: String?){
val editText = findViewById(R.id.my_edittext)
edittext.text = text
}
})
}
}
class MyFragment : Fragment() {
private lateinit var hostActivity: AppCompatActivity
override fun onAttach(context: Context){
hostActivity = context as AppCompatActivity
}
override fun onViewCreated(){
val sharedViewmodel = ViewmodelProviders.of(hostActivity).get(SharedViewModel.class)
sharedViewmodel.text = "My new text"
}
}
class sharedViewModel: ViewModel(){
private val textHolderLiveData = MutableLiveData<String>()
fun getText(): LiveData<String> {
return textHolderLiveData
}
fun setText(text: String) {
textHolderLiveData.value = text
}
}

Categories

Resources