Hello this is my first app with kotlin i am trying to make annual rate calculation app the problem is i have 4 activities every activity own button and edit's texts
i wan't when The User click the button, the program get the numbers from Edit's texts and only make the calculation and save it somewhere and same work for the activity 2 and 3.
but when he click the last button of the last activity i want to call all the results and show it in ViewText
The Question is:How to save data Every time somewhere and call when i need it?
First Activity
class st {
var int_P: Double? = null
var ctl_P: Double? = null
public constructor(int_P: Any, ctl_P: Any) {
this.int_P = int_P.toString().toDouble() //Physique
this.ctl_P = ctl_P.toString().toDouble()
public fun GetMP(): Double {
return (this.int_P!! + (this.ctl_P!! * 2)) / 3
}
}
Btn_Next1.setOnClickListener ({
var int_P = java.lang.Double.parseDouble(edit_IP.text.toString()) //Physique
var ctl_P = java.lang.Double.parseDouble(edit_CP.text.toString())
var ss = st(int_P,ctl_P)
val ic = Intent(this, Sec_Act::class.java)
startActivity(ic)
})
(Secend and Third Activity Same)
Activity 4
btn1.setOnClickListener ({
var act1 = MainActivity.st().GetMC()
Textv.text = act1.toString()
})
With this method i got problem (no value passed for parameter int_P , ctl_P)
There are many different ways to send information back to an Activity:
onActivityResult(),
having a singleton class,
use Shared Preferences,
headless fragments,
sqlite database,
store the information in a file.
Intents
receivers
You need to determine which will be the best solution for you. Whether it's kotlin or java, the methodology will be the same.
Related
I'm working on a simple calorie counter app using two fragments and a ViewModel. I'm a beginner and this is a modification of an app I just created for a course (this app is not a homework assignment). It uses ViewModel and has a fragment that collects user input and a fragment that displays the input as a MutableList of MutableLiveData. I would like for the list screen to initially be empty except for a TextView with instructions, and I'd like the instructions to disappear once an entry has been added to the list. My class instructor told me to use an if-else statement in the fragment with the list to achieve this, but it's not working. He didn't tell me exactly where to put it. I tried a bunch of different spots but none of them worked. I don't get errors - just no change to the visibility of the TextView.
Here is the code for the ViewModel with the list:
val entryList: MutableLiveData<MutableList<Entry>>
get() = _entryList
init {
_entry = MutableLiveData<Entry>()
_entryList.value = mutableListOf()
}
fun addEntry(entryInfo: Entry){
_entry.value = entryInfo
_entryList.value?.add(_entry.value!!)
}
}
And this is the code for the observer in the list fragment:
Observer { entryList ->
val entryListView: View = inflater.inflate(R.layout.fragment_entry_list, null, false)
if (entryList.isNullOrEmpty()) {
entryListView.instructions_text_view.visibility = View.VISIBLE
} else {
entryListView.instructions_text_view.visibility = View.GONE
}
entryList.forEach {entry ->
val view: View = inflater.inflate(R.layout.entry_list_item, null, false)
view.date_entry_text_view.text = String.format(getString(R.string.date), entry.date)
view.calories_entry_text_view.text =
view.line_divider
binding.entryList.addView(view)
}
Thanks for any help.
I guess you are expecting your observer to get notified of the event when you are adding entryInfo to your event list (_entryList.value?.add(_entry.value!!).
But this won't happen as you are just adding an element to the same mutable list, and as the list reference hasn't changed, live data won't emit any update.
To solve this, you have two options.
Create a new boolean live data which controls when to show and hide the info text. Set its initial value to false, and update it to true in addEntry() function.
Instead of updating the same mutable list, create of copy of it, add the element and set the entryList.value equal to this new list. This way your observer will be notified of the new list.
Additionally, its generally not a good practice to expose mutable data unless there is no alternative. Here you are exposing a mutable list of Entry and that too in the form of a mutable live data. Ideally, your should be exposing LiveData<List<Entry>>.
This is one possible implementation of all the points that I mentioned:
private val _entryList = MutableLiveData(listOf<Entry>()) // Create a private mutable live data holding an empty entry list, to avoid the initial null value.
val entryList: LiveData<List<Entry>> = _entryList // Expose an immutable version of _entryList
fun addEntry(entryInfo: Entry) {
_entryList.value = entryList.value!! + entryInfo
}
I haven't used the _entry live data here, but you can implement it the same way.
set your viewModel to observe on entry added.
I think you have gotten your visibility toggle in the your if else blocks wrong.
if (entryList.isNullOrEmpty()) {
entryListView.instructions_text_view.visibility = View.GONE // OR View.INVISIBLE
} else {
entryListView.instructions_text_view.visibility = View.VISIBLE
}
Your Observer should get notified of changes to entryList when _entryList has changed. Make sure you are calling addEntry() function to trigger the notification.
I am trying to display several download progress bars at once via a list of data objects containing the download ID and the progress value. The values of this list of objects is being updated fine (shown via logging) but the UI components WILL NOT update after their initial value change from null to the first progress value. Please help!
I see there are similar questions to this, but their solutions are not working for me, including attaching an observer.
class DownLoadViewModel() : ViewModel() {
...
private var _progressList = MutableLiveData<MutableList<DownloadObject>>()
val progressList = _progressList // Exposed to the UI.
...
//Update download progress values during download, this is called
// every time the progress updates.
val temp = _progressList.value
temp?.forEach { item ->
if (item.id.equals(download.id)) item.progress = download.progress
}
_progressList.postValue(temp)
...
}
UI Component
#Composable
fun ExampleComposable(downloadViewModel: DownloadViewModel) {
val progressList by courseViewModel.progressList.observeAsState()
val currentProgress = progressList.find { item -> item.id == local.id }
...
LinearProgressIndicator(
progress = currentProgress.progress
)
...
}
I searched a lot of text to solve the problem that List in ViewModel does not update Composable. I tried three ways to no avail, such as: LiveData, MutableLiveData, mutableStateListOf, MutableStateFlow
According to the test, I found that the value has changed, but the interface is not updated. The document says that the page will only be updated when the value of State changes. The fundamental problem is the data problem. If it is not updated, it means that State has not monitored the data update.
The above methods are effective for adding and deleting, but the alone update does not work, because I update the element in T, but the object has not changed.
The solution is to deep copy.
fun agreeGreet(greet: Greet) {
val g = greet.copy(agree = true) // This way is invalid
favourites[0] = g
}
fun agreeGreet(greet: Greet) {
val g = greet.copy() // This way works
g.agree = true
favourites[0] = g
}
Very weird, wasted a lot of time, I hope it will be helpful to those who need to update.
As far as possible, consider using mutableStateOf(...) in JC instead of LiveData and Flow. So, inside your viewmodel,
class DownLoadViewModel() : ViewModel() {
...
private var progressList by mutableStateOf(listOf<DownloadObject>()) //Using an immutable list is recommended
...
//Update download progress values during download, this is called
// every time the progress updates.
val temp = progress.value
temp?.forEach { item ->
if (item.id.equals(download.id)) item.progress = download.progress
}
progress.postValue(temp)
...
}
Now, if you wish to add an element to the progressList, you could do something like:-
progressList = progressList + listOf(/*item*/)
In your activity,
#Composable
fun ExampleComposable(downloadViewModel: DownloadViewModel) {
val progressList by courseViewModel.progressList
val currentProgress = progressList.find { item -> item.id == local.id }
...
LinearProgressIndicator(
progress = currentProgress.progress
)
...
}
EDIT,
For the specific use case, you can also use mutableStateListOf(...)instead of mutableStateOf(...). This allows for easy modification and addition of items to the list. It means you can just use it like a regular List and it will work just fine, triggering recompositions upon modification, for the Composables reading it.
It is completely fine to work with LiveData/Flow together with Jetpack Compose. In fact, they are explicitly named in the docs.
Those same docs also describe your error a few lines below in the red box:
Caution: Using mutable objects such as ArrayList or mutableListOf() as state in Compose will cause your users to see incorrect or stale data in your app.
Mutable objects that are not observable, such as ArrayList or a mutable data class, cannot be observed by Compose to trigger recomposition when they change.
Instead of using non-observable mutable objects, we recommend you use an observable data holder such as State<List> and the immutable listOf().
So the solution is very simple:
make your progressList immutable
while updating create a new list, which is a copy of the old, but with your new progress values
This question already exists:
How to get value from two livedata at the same time
Closed 1 year ago.
private var purchaseArrayList = ArrayList<PurchaseModelWithId>() //initialize 1 st arraylist
private var saleArrayList = ArrayList<SaleModelWithId>() //initialize 2 nd arraylist
purchaseViewModel.showPurchaseItems(requireContext()).observe(viewLifecycleOwner, { purchaseList->
purchaseArrayList = purchaseList as ArrayList<PurchaseModelWithId>
saleViewModel.saleLiveData(requireContext()).observe(viewLifecycleOwner, saleList -> {
saleArrayList = saleList as ArrayList<SaleModelWithId>
//here I want two arraylist value but above code not works for me
})
})
Thanks in advance
when you call showPurchaseItems(),return will be executed before set the Value to your live data.
So avoid return live data value, when you set value for live data within call back methods.
First make showPurchaseItems() method and saleLiveData() method as void method.
Then create getter methods to get value of finalPurchaseList,saleList in viewmodel.
public MutableLiveData<yourReurnType> getFinalPurchaseList(){
return finalPurchaseList;
}
public MutableLiveData<yourReurnType> getSaleList(){
return saleList;
}
Then in your activity call your two methods to set value for your live data.
After that observe live data.
showPurchaseItems();
saleLiveData();
viewmodel.getFinalPurchaseList().(requireContext()).observe(viewLifecycleOwner){}
viewmodel.getSaleList().(requireContext()).observe(viewLifecycleOwner){
}
Please note :using two viewModels in one activity/fragment is not good practice.
I was looking for my problem on the internet, but the solutions given still do not work.
How to transfer data from first Activity to second?(plain text)
MainActivity
btn.setOnClickListener {
val player1= findViewById<EditText>(R.id.et1) as EditText
val player2= findViewById<EditText>(R.id.et2) as EditText
val intent1= Intent(this, MainActivity3::class.java).apply {
putExtra("player1",player1.getText().toString())
putExtra("player2",player2.getText().toString())
}
startActivity(intent1)
}
Second Activity
fun PlayGame(cellID:Int,buSelected:Button){
val playe1= intent.getStringExtra(EXTRA_MESSAGE)
val playe2= intent.getStringExtra(EXTRA_MESSAGE)
val textView2= findViewById<TextView>(R.id.textView2).apply{
text= playe1
text=playe2
}
if(ActivePlayer==1){
textView2.setText(": $playe1")
buSelected.text="0"
buSelected.setBackgroundResource(R.color.blue)
player1.add(cellID)
ActivePlayer=2
}else{
textView2.setText(": $playe2")
buSelected.text="X"
buSelected.setBackgroundResource(R.color.green)
player2.add(cellID)
ActivePlayer=1
}
buSelected.isEnabled=false
CheckWiner()
}
I want the player's name from MainActivity goes to Second ;p
The problem is in the way you get your values. Try this:
val playe1= intent.getStringExtra("player1")
val playe2= intent.getStringExtra("player2")
Use a sharedpreferences, in your next activity after saving the values to a Val or var delete the shared preferences continue.
It’s not ideal but it just may get you to where you can at least interact with that data.
I used this method for something similar last night. If I have time tonight I was going to post a how to on this on my website. If I can I’ll swing by here and edit my answer. With code and explanation unless you get it figured out by then.
So I'm still working on my first little app here, new to Android and Java, so I'm stuck on a basic little problem here. Answers to my first questions were really helpful, so after researching and not coming up with anything, I thought I'd ask for some more help!
The idea is that on another screen the user makes a choice A, B, C, or D, and that choices is passed as a string through the intent. OnResume checks if the choice is not null and sets an integer that corresponds to that string. Later when the user pushes another button, some if else logic checks that int and performs and action based on which was chosen. The problem is that the App crashed at onResume.
I learned that I have to use equals(string) to compare string reference, but maybe the problem is that I am trying to compare a string in reference to a literal string? Any help would be greatly appreciated.
Thanks!
protected void onResume() {
super.onResume();
// Get the message from the intent
Intent intent = getIntent();
String choice = intent
.getStringExtra(ExtensionSetupSlector.TORQUE_SETUP);
// Create the text view
TextView displayChoice = (TextView) findViewById(R.id.displayChoice);
if (!choice.equals("")){
displayChoice.setText(choice);
if (choice.equals("A")) {
myChoice = 1;
}
if (choice.equals("B")) {
myChoice = 2;
}
if (choice.equals("C")) {
myChoice = 3;
}
if (choice.equals("D")) {
myChoice = 4;
}
}
}
myChoice is declare right after ...extends Activity{ Also I'm not quite sure If this should really be in onResume, but it was working before I started try to set myChoice in the onResume (when I was just displaying the choice). Thanks again!
Change if (!choice.equals("")) to check for null instead. Otherwise your app attempts to access an empty reference and crashes.