Search code examples
javaandroidderived-class

Strange behavior with ImageView reference and derived class


I created some custom component based on ConstraintLayout in Android Studio. First I created base abstract class called MyButton where I do some basic stuff (e.g. get references to my components). Next I created derived class called MySpecialButton that extends MyButton, but I have strange behavior when I attach onClickListener to my Button, that is a part of my custom component, and call method that modify element (reference) that exists only in MySpecialButton from onClickListener.

In a present code, when i try call setImage() from onClickListener, this end up with log: E/NULL: ImageView reference is null! which means that from point of view onClickListener, reference vImageViev is null, however it is initialized in inflateView call. But when I call setImage() not from onClickListener but directly from init() method after inflateView(R.layout.my_special_button) everything is OK. Also when I move protected ImageView vImageView = null; declaration from MySpecialButton to MyButton everything is OK.

This is my MyButton class:

public abstract class MyButton extends ConstraintLayout
{
    protected Context context = null;

    protected View rootView = null;
    protected Button vButton = null;
    protected Switch vSwitch = null;

    public MyButton(Context context)
    {
        super(context);
        this.context = context;
        init();
    }

    public MyButton(Context context, AttributeSet attrs)
    {
        this(context);
    }

    protected abstract void init();

    protected void inflateView(int res)
    {
        rootView = inflate(context, res, this);

        vButton = (Button)rootView.findViewById(R.id.vButton);
        vSwitch = (Switch)rootView.findViewById(R.id.vSwitch);
    }
}

and this is my MySpecialButton class:

public class MySpecialButton extends MyButton
{
    protected ImageView vImageView = null;

    public MySpecialButton(Context context)
    {
        super(context);
    }

    public MySpecialButton(Context context, AttributeSet attr)
    {
        this(context);
    }


    @Override
    protected void inflateView(int res)
    {
        super.inflateView(res);

        vImageView = (ImageView)rootView.findViewById(R.id.vImageView);
    }

    protected void init()
    {
        inflateView(R.layout.my_special_button);

        vButton.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View v) {
                setImage();
            }
        });
    }

    protected void setImage()
    {
        if(vImageView == null)
            Log.e("NULL", "ImageView reference is null!");
        else
            vImageView.setImageResource(R.drawable.ic_window);
    }
}

What's going on? What I should do to be able call setImage() from onClickListener without null reference?


Solution

  • I think you are doing wrong super calls in your constructors:

    This

    public MySpecialButton(Context context, AttributeSet attr)
    {
        this(context);
    }
    

    Should be:

    public MySpecialButton(Context context, AttributeSet attr)
    {
        super(context, attr);
    }
    

    Same for the other class. And move your custom init in a different function you call from each constructor.