So I have a bit of confusion with trying to set the background drawable of a view as it is displayed. The code relies upon knowing the height of the view, so I can't call it from onCreate()
or onResume()
, because getHeight()
returns 0. onResume()
seems to be the closest I can get though. Where should I put code such as the below so that the background changes upon display to the user?
TextView tv = (TextView)findViewById(R.id.image_test);
LayerDrawable ld = (LayerDrawable)tv.getBackground();
int height = tv.getHeight(); //when to call this so as not to get 0?
int topInset = height / 2;
ld.setLayerInset(1, 0, topInset, 0, 0);
tv.setBackgroundDrawable(ld);
Use
OnPreDrawListener()
instead ofaddOnGlobalLayoutListener()
, because it is called earlier.You can override onMeasure for TextView:
I am almost sure that you can do it without any line of code also. I think there is a chance to create layer-list.
I didn't know about
ViewTreeObserver.addOnPreDrawListener()
, and I tried it in a test project.With your code it would look like this:
In my test project
onPreDraw()
has been called twice, and I think in your case it may cause an infinite loop.You could try to call the
setBackgroundDrawable()
only when the height of theTextView
changes :But that sounds a bit complicated for what you are trying to achieve and not really good for performance.
EDIT by kcoppock
Here's what I ended up doing from this code. Gautier's answer got me to this point, so I'd rather accept this answer with modification than answer it myself. I ended up using the ViewTreeObserver's addOnGlobalLayoutListener() method instead, like so (this is in onCreate()):
Seems to work perfectly; I checked LogCat and didn't see any unusual activity. Hopefully this is it! Thanks!
Concerning the view tree observer:
Even if its a short lived reference (only used once to measure the view and do the same sort of thing you're doing) I've seen it not be "alive" anymore and throw an exception. In fact, every function on ViewTreeObserver will throw an IllegalStateException if its no longer 'alive', so you always have to check 'isAlive'. I had to do this:
This seems pretty brittle to me. Lots of things - albeit rarely - seem to be able to go wrong.
So I started doing this: when you post a message to a View, the messages will only be delivered after the View has been fully initialized (including being measured). If you only want your measuring code to run once, and you don't actually want to observe layout changes for the life of the view (cause it isn't being resized), then I just put my measuring code in a post like this:
I haven't had any problems with the view posting strategy, and I haven't seen any screen flickers or other things you'd anticipate might go wrong (yet...)
I have had crashes in production code because my global layout observer wasn't removed or because the layout observer wasn't 'alive' when I tried to access it
In my projects I'm using following snippet:
So, inside provided
Runnable#run
you can be sure thatView
was measured and execute related code.You can get all of view measures in method of class main activity below onCreate() method:
So you can redrawing, sorting... your views. :)