I have a MutableLiveData variable in my AppRepository which is updated and contains my data. This I have no issues with. I also have the following observable to trigger a UI update with the data it holds in my onCreateView function:
viewModel.projectWithContent.observe(viewLifecycleOwner, {
pwc = it
counterList = it.counterList
})
When I tap either to increase or decrease the counter count and then try to push the update to my Room database, it skips it. I have the following check currently:
if(counterList != null) {
try {
for(counter: Counter in counterList!!) {
if(counter.counter_count != pwc?.counterList!![
pwc?.counterList!!.indexOf(counter)
].counter_count) {
Log.i(LOG_TAG, "Hello")
} else {
Log.i(LOG_TAG, "Goodbye")
}
}
} catch(e: IndexOutOfBoundsException) {
e.printStackTrace()
}
}
It'll always go to Goodbye.
Now. If I put the following just below try
Log.i(LOG_TAG, "PWC: ${pwc?.counterList!![0].counter_count}, " +
"CPWC: ${counterList!![0].counter_count}," +
"VMPWC: ${viewModel.projectWithContent.value?.counterList!![0].counter_count}")
It provides the following output:
PWC: 70, CPWC: 70,VMPWC: 70
Is this a side effect of what I'm doing or?
Thanks
Like #Tenfour04 says, your condition is actually checking they don't match, so "Goodbye" is the output when they do match.
If you don't mind (this is a little long), I just want to recommend some stuff because I feel like you're making life hard for yourself with all the null-checking that's going on - the logic of the code was really hard to read, and I'm guessing that's why you didn't notice the flipped logic too!
First: the ? null safety stuff (and !! which is the opposite of safe, never use it unless you know you have good reason) is there because you have nullable variable types. Normally the IDE would smart cast them to non-null once you've done a null check (like on your first line) - but because they're vars, they can be changed at any time.
That means that a variable that wasn't null before could be now, so you're forced to null-check every single time you access it. But even if the types weren't nullable, because they're vars, they can still change, and the thing you were looking at a moment ago is something different now.
The simple solution is to just make a new variable:
val counters = counterList
if (counters != null) {
...
}
// or if you want to use one of kotlin's scope functions
counterList?.let { counters ->
...
}
Because that new one is a val, it's not going to change what it's pointing at! Once it's null-checked, it's always going to be non-null, so you don't need to use ? anymore.
You have a couple of variables to make - you want to make sure pwc isn't null, and also their counterLists. A quick way to do that is with pwc?.counterList - if pwc is null, it will return null. Otherwise it will move to the next step, and return counterList, which may be null. (Using !! is saying that it definitely never will be null, in which case it shouldn't be nullable at all!)
And you don't actually care about pwc anyway - you're just comparing its counterList to the other, so why don't we pare it back to just those?
val counters = counterList
val pwcCounters = pwc?.counterList
if (counters != null && pwcCounters != null) {
try {
for(counter: Counter in counters) {
if(counter.counter_count != pwcCounters[
pwcCounters.indexOf(counter)
].counter_count) {
Log.i(LOG_TAG, "Hello")
} else {
Log.i(LOG_TAG, "Goodbye")
}
}
} catch(e: IndexOutOfBoundsException) {
e.printStackTrace()
}
}
There's more we could do here, but just by cleaning up those nulls and using the specific variables we want to work with, does that feel easier to read? And more importantly, easier to understand what's happening and what could happen?
Might be worth throwing it in a function too, stops the call site getting cluttered with these temp variables:
fun doThing(counters: List<Counter>?, pwcCounters: List<Counter>?) {
if (counters == null || pwcCounters == null) return
// do the stuff
}
// when you want to do the thing:
doThing(counterList, pwc?.counterList)
So all your null checking is out of the way, your "temp variables" are the fixed parameters passed to the function, it's all nice and neat.
I know this is a long post for such a short bit of code, but it's a good habit to get into - if you're writing code where you're working with nullable vars and you're wrestling with the null safety system, or you keep repeating yourself to access a particular variable nested inside another object, you can make things a lot easier for yourself! You can imagine how wild this could all get for more complex code.
Also if you care, this is how I'd personally write it, if it helps!
fun doThing(counters: List<Counter>?, pwcCounters: List<Counter>?) {
if (counters == null || pwcCounters == null) return
// for (counter in Counters) is fine too I just like this version
counters.forEach { counter ->
// find returns the first item that matches the condition, or null if nothing matches,
// so no need to handle any exceptions, just handle the potential null!
// (this is a really common Kotlin pattern, lots of functions have a "returns null on failure" version)
val pwcCounter = pwcCounters.find { it == counter }
// remember pwcCounter can be null, so we have to use ? to access its count safely.
// If it evaluates to null, the match just fails
if (counter.count == pwcCounter?.count) Log.i(LOG_TAG, "Hello")
else Log.i(LOG_TAG, "Goodbye")
}
}
I also renamed counter_count to just count since it's a property on a Counter anyway. I feel like counter.count is easier to read than counter.counter_count, y'know? It's the little things
Here I have a sample
I am wondering when when is better solution than if ?
when {
uri.isNullOrEmpty() -> Log.i()
else -> display(uri)
}
if(uri.isNullOrEmpty()){
Log.i()
}else{
display(uri)
}
As the answer #Juan Ortiz Couder gave, it depends to your case that which one is better. Maybe when is better when you have several cases. If is better when you have just 1 condition or two cases that you can use if else for it. For example you use if(text.isEmpty()){...
}
else{...
}
Here if has better readability than when.
Both of them are useful and we can't say one is better than another. It depends.
When is used more similar as a switch statement. It is used instead of having to write several else if statements.
when (x) {
3 -> print("x == 3")
8 -> print("x == 8")
else -> {
print("x is neither 3 nor 8")
}
This is very helpful instead of having to write
if (x == 3) {
print("x == 3")
} else if (x == 8){
print("x == 8")
} else {
print("x is neither 3 nor 8")
}
I am working with FireBase Notifications and I can send a notification which will send the user to the webview page I input on the console.
The problem is that when it matches the IF statement is fires the else statement too, what could be the cause of this?
if(getIntent().getExtras()!=null) {
for (String key : getIntent().getExtras().keySet()) {
if (key.equals("url")){
mwebView.loadUrl("http://example.com/" + getIntent().getExtras().getString(key));
}else {
mwebView.loadUrl("http://example.com");
}
}
}
Because it executes both at the same time the app crashes.
Also when I load the app the usual way it matches the with:
if(getIntent().getExtras()!=null)
and then loads the else statement. Shouldnt getExtras be null?
When I first install a new instance of the app it uses the following statement:
if(getIntent().getExtras()==null) {
if (haveNetworkConnection()) {
mwebView.loadUrl("http://example.com");
} else {
mwebView.loadUrl("file:///android_asset/myerrorpage.html");
}
}
Update:
As I cannot find out why this it happening I am trying another approach, How would I get the variable outside of the loop to use like the following:
if(getIntent().getExtras()!=null) {
for (String key : getIntent().getExtras().keySet()) {
String valuex = getIntent().getExtras().getString(key);
}
}
if (haveNetworkConnection()) {
mwebView.loadUrl("http://example.com/" + valuex);
} else {
mwebView.loadUrl("file:///android_asset/myerrorpage.html");
}
If and Else can not both be executed in one go.
You should check your other code and ensure, that this code section is not executed twice for some reason (once with TRUE and once with FALSE).
I want to add a function in my app that checks if a menu item is disabled or enabled, if disabled I want my app to do tasl a, if enabled I want my app to do task b.
what I have tried so far;
if (menuItem.setEnabled()==false){
//do stuff
} else {
//do stuff
}
if (menuItem.setEnabled(false)){
//do stuff
} else {
//do stuff
}
if (menuItem.setEnabled().equals(false)){
//do stuff
} else {
//do stuff
}
I am not sure how I can do this, as whatever I tried doesn't seem to work.
Try this:
MenuItem mymenu = menu.findItem(R.id.mine_menu);
And:
if(mymenu.isEnabled()){
//do something
}
I am trying to test for am or pm in a if else statement..
if(am){
//Do something
else{
//Do something else
Ive tried
int am = cld.get(Calendar.AM_PM);
but the if else
wont take it as a parameter to test. Maybe because its not boolean.
How would i go about testing this?
You're correct that the if-else won't accept it because it is not boolean. Calendar.AM_PM only ever holds the value 0 or 1. A language like C would accept 0 or 1 as boolean; Java won't.
You want to do something more like this:
int am = cld.get(Calendar.AM_PM);
if (am == 0) {
// Do whatever for the AM
} else {
// Do whatever because it must be PM
}
Surely your if clause cannot accept integer as it is. You need something (comparison perhaps) to get boolean out of it.
if(am > 0)
{
//its PM
else { //its AM }
Calendar.AM_PM is an int. To evaluate it in an if statement, cast it to a boolean:
if((bool)am) {
//It's AM
} else {
//It's PM
}