Question

I have a custom View to represent an ImageView with a parallax effect, adapted from the one found in the answer to this SO question: How to have a wider image scrolling in the background.

The View has a setBackgroundDrawable() method to set its background image that does some calculations to know the size that the bitmap should have in order to be wide enough to be scrolled with a parallax effect, if cropping is needed to keep the aspect ratio, etc.

I'm getting an "IllegalArgumentException: width and height must be > 0" in

(...)

int width = this.getWidth();
int height = this.getHeight();

Bitmap mCurrentBackground=Bitmap.createBitmap((int) (width*FACTOR), height, Config.ARGB_8888);

(...)

because getWidth() and getHeight() are returning zero.

I understand that this is happening because the View doesn't know its own size yet when I call the said function, for example, in an Activity's onCreate method. Same thing happens if I try to set the background in onStart or onResume...

I've managed to set the background if I call the function in onWindowFocusChanged(), but I still get the error sometimes (e.g. if I switch the screen orientation when an EditText is focused).

Is there a way to get around this? Any kind of callback or phase in the lifecycle where the whole View was created (like the onViewCreated from the Fragment class)? I can only call my custom View's setBackgroundDrawable after the View has been assigned a height and a width.

EDIT:

I have also tried adding a GlobalLayoutListener to the View's ViewTreeObserver:

ViewTreeObserver vto = parallaxBackground.getViewTreeObserver();
vto.addOnGlobalLayoutListener(new OnGlobalLayoutListener() {

    @SuppressLint("NewApi")
    @Override
    public void onGlobalLayout() {

                    ...

        parallaxBackground.setBackgroundDrawable(d1);

            ViewTreeObserver obs = parallaxBackground.getViewTreeObserver();

            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
                obs.removeOnGlobalLayoutListener(this);
            } else {
                obs.removeGlobalOnLayoutListener(this);
            }
        }

    });

but it results in the same behavior I get from setting the background in the onWindowFocusChanged() method. I.e. the gedtWidth()/getHeight() return zero inside the listener's onGlobalLayout if I switch the phone's orientation when an EditText is selected.

EDIT 2:

It probably has something to do with the keyboard as I observed some behavior like the height being calculated taking the keyboard's existence into account.

Deleting:

            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
                obs.removeOnGlobalLayoutListener(this);
            } else {
                obs.removeGlobalOnLayoutListener(this);
            }

from my onGlobalLayout shown in the first EDIT seems to do the trick.

Was it helpful?

Solution

I'm not sure in your case, but I've used this some time ago. Simply use a ViewObserver. In my case I've used something like this:

final ViewTreeObserver treeObs = dataView.getViewTreeObserver();

treeObs.addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
  @Override
  public void onGlobalLayout() {
    // This will be called when the layout is finished, just before displaying
    ...
  }
}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top