So I've got this code (updated for solution).
#Override
public View getView(int position, View convertView, ViewGroup parent) {
...
final Direction d = directions.get(position);
if (d != null) {
TextView direction = (TextView) row.getTag(R.id.directionTextView);
TextView departure1 = (TextView) row.getTag(R.id.departure1);
TextView departure2 = (TextView) row.getTag(R.id.departure2);
TextView departure3 = (TextView) row.getTag(R.id.departure3);
direction.setText(d.getName());
if (d.getTimeStamps().size() == 0) {
departure1.setText(R.string.nodepartures);
departure1.setTextColor(R.color.grey);
} else {
for (int i = 0; i < d.getTimeStamps().size(); i++) {
switch (i) {
case 0:
departure1.setText(d.getTimeStamps().get(i));
break;
case 1:
departure2.setText(d.getTimeStamps().get(i));
break;
case 2:
departure3.setText(d.getTimeStamps().get(i));
break;
default:
break;
}
}
}
}
return row;
}
}
The problem I was having was that one of the TextViews would turn grey when it wasn't supposed to. I tried fixing it by always setting the text to black again, but that turned every single one of them grey. Until I tried:
setTextColor(context.getResources().getColor(R.color.black));
instead of just
setTextColor(R.color.black);
Don't know why the latter works when setting text grey, but there it is. I guess I'm just a little retarded. :)
What you think is happening simply cannot happen (*, **). What you need to do is prove to yourself that it is not happening.
I'd do this by adding some traceprints to the code. Put one before the if statement, one at the starts of the "then" and "else" clauses, and one after the if statement. Then run it. I expect that this will reveal that the if statement is actually being run twice, and running the "then" clause the first time and the "else" clause the second time.
(* In theory, it might happen if there was a serious bug in the emulator. But you should only consider that as a possibility if you have irrefutable evidence.)
(** Another possibility is that there might be a significant difference between the sample code above and the actual code you are testing. It happens ...)
AsLanFromNarnia is on the right track. ListView recycles its child views. You can never assume that convertView is in any sort of default state for its type. Set every relevant field every time getView is called. In your case it means setting the text color when you set the text.
There is another way to handle cases like this where you want to have heterogenous lists: use view types. Your adapter can return the number of types you have for getViewTypeCount and then report the type of each item from getItemViewType. If you do this, you will always get a convertView passed into your getView method of the proper type, alleviating the need to change otherwise static layout each time.
Hmm... I can't see how that would happen. Are you sure your brackets are in the right place? Does it still occur if you switch the statement around?
I agree with Aircule! This code is pretty nutty! How about this?
if(d.getTimeStamps().isEmpty())
{
departure1.setText(R.string.nodepartures);
departure1.setTextColor(R.color.grey);
}
else
{
departure1.setText(d.getTimeStamps().get(0));
departure2.setText(d.getTimeStamps().get(1));
departure3.setText(d.getTimeStamps().get(2));
}
It should do the exact same thing an it's a lot less complicated. Also, let's say you run this code once and the list is empty, so you set the color of departure1 to gray and run it again. This time you get data and fill in the items, but you never change the color of departure1, so it will stay gray. Similarly, if you take that scenario the other way around, you don't empty the TextViews when the list is empty. Another tip, if there are only ever going to be three items (or any small fixed number of items) then you're probably better off just using a normal layout instead of a list. That way you won't have to go through making a custom adapter, you can just call the items by name.
Have you set a default color or styling in your layout XML file that is loaded by LayoutInflator?
I believe it's turning gray because at some point in your program the method is being called with 0 timestamps.
It only has to happen once for it to set the departure1.textColor attribute, then it will persist until changed back.
You mentioned you tried setting it to black in the else statement but it turned everything gray. This doesn't make sense at all. Try adding the command to turn text to black at each case statement, so:
case 0:
departure1.setTextColor(R.color.black);
departure1.setText(d.getTimeStamps().get(i));
break;
case 1:
departure2.setTextColor(R.color.black);
departure2.setText(d.getTimeStamps().get(i));
break;
case 2:
departure3.setTextColor(R.color.black);
departure3.setText(d.getTimeStamps().get(i));
break;
Based on what you describe we can only give hints at best.
Are you sure you recompiled all your code? I have seen debugging yield funny results because part of code was not in synch with the bytecode beng debugged
Is this an event based system? Could it be that your code is called twice, once with teh empty list followed directly by the list with 1 entry after that entry is added to the list?
As code improvement I would do the following to refactor the switch (assumption here is the control type is Text which is probably wrong but easily fixed):
if (d.getTimeStamps().isEmpty()) {
departure1.setText(R.string.nodepartures);
departure1.setTextColor(R.color.grey);
} else {
Text[] fields = new Text[] { departure1, departure2, departure3 };
for (int i = 0; i < fields.length && i < d.getTimeStamps().size(); i++) {
fields[i].setText(d.getTimeStamps().get(i));
fields[i].setTextColor(R.color.black);
}
}
update
Seeing that you do not set the color for departure values, I think you assume that the lines are freshly created when this code runs. If that assumption is incorrect, your situation might be that a line that previously held a 'no departure' line, now gets re-used for a departure line therefore inheriting the grey color.
Related
I have an Adapter that points to items that have a "Time" attribute.
If the user taps a button on one item and on another I'd need both Time values to be summed. I can't achieve this.
I've tried stuff like time += time, separate those values etc
This was the last thing I was trying. Yeah it makes no sense but idk, nothing had worked.
[...]
add.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
GetTime(gameItem.getTime());
}
});
}
private int GetTime(int timeplayed) {
time += timeplayed;
return time;
}
Which should have been like at the start there is an empty variable time.
timeplayed is added to it and is returned as time.
So eveytime you tap the button the timeplayed gets added and returned as time
Actually tho, instead of summing the old variable with the new one it either does just a 0 + number or it sums the new variable with itself...
From what you said it sounds to me that the field value is being altered or actually not initialized at all before you get to actually do your mathematical operations on it. So the answer to your question is simple: make sure the value of the field is not changed before your code reaches the private int GetTime(int timeplayed) method.
In any case your code absolutely should work as the syntax of the operation is correct. time += timeplayed is equivalent to time = time + timeplayed. I can't see the rest of your code so I can't really say whats going on underneath the hood but if you are willing to share the rest of your code, pretty much anywhere the time variable is being altered (including the place where it's being actually initialized) I could help you out further.
But first you need to make sure the variable has an expected value (not the same as timeplayed and not 0). You can do this through debugging either with using breakpoints or logging the value in console before the operation gets executed.
Hope this helps, let me know how it goes.
switch (Category.categorize(input)) {
case MONEY:
amount += input.amount();
state = ADDING_MONEY;
break;
case SHUT_DOWN:
state = TERMINAL;
// why ???
default:
}
these codes come from ,my question is :
why set default,it's nonsense ?? i think it's good to code like this:
switch (Category.categorize(input)) {
case MONEY:
amount += input.amount();
state = ADDING_MONEY;
break;
case SHUT_DOWN:
state = TERMINAL;
// why ???
}
It's not necessary, it is a style thing. In the Google Java style guide it says:
4.8.4.3 The default case is present
Each switch statement includes a default statement group, even if it
contains no code.
In the event you have a different case added that isn't handled in the switch, it would be nice to have a default that did something, whether to throw an exception, do logging, or whatever, to inform someone there was a case that isn't handled. Having a default label makes it more apparent that there is a spot where some code like this needs to go.
It may be Bruce Eckel decided to make his code comply with the Google style guide, and added this as a result. Following agreed-on conventions can be a good thing even if you don't agree with all of them, there is an argument that "form is liberating".
I'm writing a code for a treasure hunt game, a grid of buttons and one of them hide the treasure behind it. I want to ad a proximity checker: if the button clicked touch the button with the treasure behind a message appear telling the player that he is near.
This is what i managed to write but i always get a mistake for the arrays going out of bound (this happens because the last row and column aren't surrounded by buttons).
As you can see i put a lot of "if" to contain the chance of goig out of bound but it keeps happening. Any suggestion?
You can use switch-cases here.
switch(grid){
case treasure[x1][y1]:
// your logic
break;
// other cases
default:
// default scenario
}
You need to check the length of the array before you try to access a row beyond it. It appears that you are doing this for values that too low like this:
if (y1 < 0){
y1 = 0;
}
But you need to have a similar check for values that are too high.
Alternatively,
This design could be much cleaner/simpler if you just passed in the X/Y location of the treasure. Instead of the actual JButtons. Then you could just subtract the values to determine if you were close or not.
The way I would do it is to write a function to do the check for a match that also checks for the bounds. For example:
private boolean isTreasureAt(int x, int y) {
if (x<0 || y<0 || x>width || y>height)
return false;
return treasure[x][y]; // need your correct checking logic here
}
Now you can just call isTreasureAt() for whatever co-ordinates you like and its always safe, no need to put range checks everywhere in your code.
I am new to Android development, and there is one thing I do not understand, probably this is a bug, but I really cannot believe that this one is present for such a long time. Please have a look here:
http://grepcode.com/file/repository.grepcode.com/java/ext/com.google.android/android/4.2.2_r1/android/widget/TextView.java?av=f
The code in setMinHeight(int minHeight) {
sets the mMinimum = minHeight; to minHeight. Why? Whouldn't this method instead set mMinHeight = minHeight?
Also have a look at
setMinWidth(int minpixels) {
**mMinWidth** = minpixels;
which is correct in my eyes.
Edit: I need to programatically set the mMinHeight member, but I find no method to do set, so how can this be done?
Min height is done in a slightly different manner because you can customise the view to be so many lines tall. With width, you can do it the normal way which is what you've pointed out.
I think it's because you can also set minLines as a measure of how "high" the view can be, and it uses mMinimum as a field that can refer to either pixels or lines.
If you look at getMinHeight, it becomes clear:
public int getMinHeight() {
return mMinMode == PIXELS ? mMinimum : -1;
}
the minimum height of this TextView expressed in pixels, or -1 if the minimum height was set in number of lines instead using or .setLines(int).
So mMinimum is the correct field to use, it is dual-purpose.
Kudos to you for pre-emptively looking through the source-code though :)
I'm creating an android app which will call a method which parses the selected item of multiple spinner objects. I'm very new to Java, and I'm not sure which statement is best to use in terms of program flow in this instance. Should I just use multiple If statements? Such as:
If(spinner1.getSelectedItemPosition() == 0 && spinner2.getSelectedItemPosition() == 2 && spinner3.getSelectedItemPosition() == 4)
/* do some stuff
Or would it be better to use switch statements?
switch(spinner1.getSelectedItem())
case 1:
switch(spinner2.getSelectedItem())
case 1:
switch(spinner3.getSelectedItem())
case 1:
/* do something
Essentially what I'm trying to do here is evaluate the selected item of each spinner object, then create a new Activity based on that evaluation. However the initial activity consists of several spinner boxes, each with several options, so I'm not sure the best way to go about designing the control statements. Using an if statement for every single possible combination of selected items seems a bit unwieldy. Additionally, I'm not sure which option will actually make the code work the way I intend it to.
Choose readability above anything else, because in performance or program flow there are no big differences.
Probably a switch statement will be more effective, because you can send multiple results into the same section of code (sounds like that might be necessary for your application) like this
switch(value) {
case 0:
case 1:
//Do THIS
break;
case 2:
//Do THAT
break;
default:
//Doesn't match any
}
If value evaluates to either 0 or 1, the first section will be executed and you only had to write the operation code once. As for specifics (now I'm just getting creative), you could shift the three spinner values into a single integer and switch on the result. If you write the case statements in hexadecimal, it will be more readable as to the state of each.
int result = 0;
result += (byte)(spinner1.getSelectedItemPosition() << 16);
result += (byte)(spinner2.getSelectedItemPosition() << 8);
result += (byte)(spinner3.getSelectedItemPosition());
switch(result) {
case 0x000000: //All spinners 0
case 0x010201: //S1 = 1, S2 = 2, S3 = 1
//Do something
break;
case 0x010101: //S1 = 1, S2 = 1, S3 = 1
//Do something else
break;
default:
//Everything else (good if most of your options go to the same Activity)
}
Hope that Helps!
Possible drawback to this example: Only 256 options allowed for each spinner ;)
I think that almost depends on how many choices the user has, if they are too many, the switch may be the best choice.
However, you cans also arrange them in categories, and each one in different modules, in something like a progression page, every time the user has selected some of one categories, show the next one and so on
So each of four spinners has x states? Or do you want to capture just one specific state and have some 'else' branch for the others?
In the end this will all become very lengthy, so that cascaded switch statements may be easier to read.
Depending on the input data you could also do something else:
If you have 4 spinners, then multiply the position of the first by 1000, of the 2nd by 100 , the third by 10 and the last by 1. Then add the numbers together and have fund with a few thousand case: statements ):-)
From a purely performance standpoint, switch statements are faster. The initial expression passed into the switch is evaluated once and then checked against the case values. For an if statement the both the left and right hand side of the if check is evaluated each time. So using if statements would be a bit slower.