0

I'm implementing a custom ImageView that have to load a Bitmap from disk. The Bitmap should be scaled exactly so, that its width must be equal to the final width a parent layout has been allocated to my ImageView and height is calculated to keep an aspect ratio.

So, the question is what is the proper place during the View life cycle to put heavy operations that depend on View's dimensions known after the layout is done?

(I'd like to keep it simple and not use threads, however.)

  1. Most likely it's a bad idea to put that code in onDraw() method, since it should be as efficient as possible. From the other hand, my ImageView is unlikely to be resized, so it would kill only the first onDraw()'s performance and it wouldn't affect any subsequent calls to onDraw(). So I'm not sure about this option.
  2. I could put it at the end of onLayout(boolean changed, int l, int t, int r, int b) executing my heavy code if (changed == true). Is this a good idea?
  3. The 3rd option I see is to do it in the onSizeChanged() callback. My only concern here is whether this callback is guaranteed to be called only once per actual view's size change. I'm inclined to use this option, since in my tests it works fine.

Could someone give an insight on this?

Alexander Abakumov
  • 13,617
  • 16
  • 88
  • 129

1 Answers1

0

Both the option 2 and 3 works for you to get the View's correct dimensions.

onDraw() is called only after onLayout(). You can get the dimensions in the onLayout() itself and it is better to avoid doing any unnecessary works inside onDraw(). So I would choose either option 2 or option 3.

And also be aware that onSizeChanged() is sometimes called with zero. Ian in this thread:

Be aware too that onSizeChanged() is sometimes called with parameters of zero -- check for this case, and return immediately (so you don't get a divide by zero when calculating your layout, etc.). Some time later it will be called with the real values.

Another option is to add a runnable to the View's queue, using View.post()

Quote and example from this thread:

The UI event queue will process events in order. After setContentView() is invoked, the event queue will contain a message asking for a relayout, so anything you post to the queue will happen after the layout pass

final View view=//smth;
...
view.post(new Runnable() {
            @Override
            public void run() {
                view.getHeight(); //height is ready
            }
        });

Using this method, we can be sure that our code is executed only once.

Bob
  • 13,447
  • 7
  • 35
  • 45
  • Please keep in mind that I need to get it executed not once per lifetime of the `View`, but once _per actual size change_, so making a `view.post()` doesn't seem to be right here. – Alexander Abakumov Aug 11 '17 at 15:29
  • Oh. Then using the `onSizeChanged()` method would be a better option. – Bob Aug 11 '17 at 16:45
  • Ok, from the link you've provided one can infer that `onSizeChanged()` is NOT guaranteed to be called only once per actual `View`'s size change. How to handle it then? Would comparing the old and the new w\h to filter out multiple calls on the same size change be correct in this case? – Alexander Abakumov Aug 11 '17 at 16:58
  • Yes. That would be the best way to filter out the duplicate calls on the same size. – Bob Aug 11 '17 at 17:35