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.

有帮助吗?

解决方案

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
    ...
  }
}
许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top