Lessons Learned - Jenuma/bias GitHub Wiki
Toggling strike-through
Text effects like strike-through are applied to text if certain bit flags belonging to them are turned on. These bits are stored in a property called paintFlags
. Since the property is an int, you can set it to whatever you want, but you have to be careful to only change the paint flags that you actually want changed.
In my case, I wanted to toggle strike-through dynamically if the user touched an item in my ListView
. I found this answer on Stack Overflow:
txtView.setPaintFlags(txtView.getPaintFlags() | Paint.STRIKE-THRU-TEXT_FLAG);
This uses the logical OR bit-wise operator |
to compare the strike-through bit flag against the flags the text already has turned on. This way, if the strike-through bit is turned off, this operation will turn it on without affecting the other bits.
However, I needed to be able to toggle the bit. In the Stack Overflow question I linked above, there is another answer for turning only this bit off, but it requires checking to see if the bit is already activated or not. Remembering some of my binary from college, I knew their had to be a single operator that would toggle only this bit. Sure enough, it's XOR:
txtView.setPaintFlags(txtView.getPaintFlags() ^ Paint.STRIKE-THRU-TEXT_FLAG);
The XOR operator ^
will turn only the strike-through bit on if it's off, and will turn only the strike-through bit off if it's on. The XOR, or 'exclusive OR' operator, works by resolving to 1
if exclusively one of the bits are on, and 0
otherwise. So when the bit is off, XOR will compare 0
and 1
and resolve to 1
. But if the bit is on, XOR will compare 1
and 1
and resolve to 0
.
Extending ArrayAdapter
I've actually learned a lot trying this, but the biggest thing I want to document right now is that you must pass your list to the superclass constructor as well or the list won't populate.
I also learned that trying to iterate through a ListView
directly will make you have a bad time, since the only children views you can access are the ones that are currently visible. Extending ArrayAdapter
is the correct way to apply logic to the list.
I need to delve deeper into this, but I now know that Android recycles views when doing things like scrolling so that the experience is smoother.
Threads
I ran into a great deal of pain trying to get a simple ProgressBar
to display over my ListView
while it loaded data from the database. Turns out I didn't read the documentation closely enough, and there were plenty of awful Stack Overflow answers to lead me astray.
I was trying to set the visibility of the progress bar in the onPre/PostExecute()
blocks of the AsyncTask
, but I thought I was going to be clever and run them on the UI thread explicitly by implementing a new Runnable
and starting it.
...you don't have to do that. Everything but doInBackground()
is already running on the main thread. I also forgot to place my adapter.notifyDataSetChanged()
in the right place, so the list wouldn't update until I clicked on a form field. Spent the whole time thinking my list was just in the way of the progress bar. Man.
Passing Objects
I thought Java used exclusively pass-by-value. But the behavior I got trying to pass around copies of my global item array sure seemed to be pass-by-reference. Spent an entire day trying to figure out why my assignment wasn't working.
Basically, Java passes values when you pass objects, true - but it's the 4-byte address. There's no copying implicitly. So while I was passing my global list property to all the activities listening to it, I was basically passing a reference. That's why there was a race condition when adding or removing items too quickly.