1

I have an issue creating a Button programmatically. The button is supposed to be inserted in a pre-existing layout.

And since I need the dimensions of a specific container I created a global layout listener for that container and in the onGlobalLayout callback i check for a valid size and then instantiate a new Button.

The context used is the context from the container.

final View container = activity.findViewById(...);
container.getViewTreeObserver().addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
    public void onGlobalLayout() {
        if (container.getWidth()>0 && container.getHeight()>0) {
            Button button = new Button(container.getContext());
        }
    }
});

However, in rare cases - specifically when switching from one activity to another - the Button instanciation fails with a NullPointerException in the Android framework code.

java.lang.NullPointerException: Attempt to read from field 'android.animation.Animator android.animation.AnimatorSet$Node.mAnimation' on a null object reference
at android.animation.AnimatorSet.clone(AnimatorSet.java:725)
at android.animation.AnimatorSet.clone(AnimatorSet.java:682)
at android.animation.StateListAnimator.clone(StateListAnimator.java:148)
at android.animation.StateListAnimator$StateListAnimatorConstantState.newInstance(StateListAnimator.java:328)
at android.animation.StateListAnimator$StateListAnimatorConstantState.newInstance(StateListAnimator.java:327)
at android.content.res.ConstantState.newInstance(ConstantState.java:53)
at android.content.res.ConstantState.newInstance(ConstantState.java:61)
at android.content.res.ConfigurationBoundResourceCache.getInstance(ConfigurationBoundResourceCache.java:40)
at android.animation.AnimatorInflater.loadStateListAnimator(AnimatorInflater.java:163)
at android.view.View.<init>(View.java:4815)
at android.widget.TextView.<init>(TextView.java:995)
at android.widget.Button.<init>(Button.java:113)
at android.widget.Button.<init>(Button.java:106)
at android.widget.Button.<init>(Button.java:102)
at android.widget.Button.<init>(Button.java:98)

My assumption is that somehow the Context ist not valid any more but I can't put my finger on it..

I do remove the listener when the activity gets deactivated.

Any ideas?

P.Melch
  • 8,066
  • 43
  • 40

2 Answers2

0

As docs says OnGlobalLayoutListener

Interface definition for a callback to be invoked when the global layout state or the visibility of views within the view tree changes.

So when UI is destroing, you are getting "dying" View that causes NPE. You can try to unregister listener in onStop() to prevent that. Or if you need just to handle fully created View use

container.post(new Runnable() {
        @Override
        public void run() {
            Button button = new Button(container.getContext());
        }
    });
Stanislav Bondar
  • 6,056
  • 2
  • 34
  • 46
  • a posted Runnable may be called on the UI thread and when the container is actually created, but it seems layouting must not necessarily be complete; i usually get the callbacks when getWidth() and getHeight() still return 0. – P.Melch Nov 08 '17 at 14:18
0

The only solution I've found so far is to not create the button by calling

Button button = new Button(container.getContext());

but by creating a small layout xml file only containing the button and then instantiate the button like so:

LayoutInflater.from(context).inflate(R.layout.button, null)
P.Melch
  • 8,066
  • 43
  • 40