Home » Android » android – How to handle butter knife bind in both Baseactivity and sub activities?

android – How to handle butter knife bind in both Baseactivity and sub activities?

Posted by: admin June 15, 2020 Leave a comment

Questions:

There is a BaseActivity with a layout and a sub Activity that extends this BaseActivity.

How do you bind views so that views in BaseActivity are binded in BaseActivity and views in Sub activity are binded there ?

Here is a sample code explaining the current scenario,
Note: Sample code was taken from here

BASE ACTIVITY

public class BaseActivity extends AppCompatActivity {

    protected void onCreate(Bundle savedInstanceState, int layout) {
        super.onCreate(savedInstanceState);
        super.setContentView(layout);
        ButterKnife.bind(this);
    }


@Override
public void setContentView(int layoutResID) {

    //I added my own implementation here

}
}

SUB ACTIVITY

public class SplashActivity extends BaseActivity {

    @BindView(R.id.txtName)
    TextView txtName;

    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        ButterKnife.bind(this);

    }
}

The above scenario produced many errors such as unable to find view with the id,

After tons of researching I found a lot of topics discussing the same,

Link 1

Link 2

Link 3

Link 4

So after going through all the links I tried the following combinations but none of them worked

  1. Call ButterKnife.bind(this) in BaseActivity but not in SplashActivity
  2. Call ButterKnife.bind(this) in both the Activity.
  3. Call ButterKnife.bind(this) in SplashActivity, with this combination I was unable to access the BaseActivity’s view items as they turned out to be null.

My Question
How do you bind both the BaseActivity and SplashActivity View items ?

Exact Error line

Caused by: java.lang.IllegalStateException: Required view '' with ID 2131296567 for field '' was not found. If this view is optional add '@Nullable' (fields) or '@Optional' (methods) annotation.

For your kind note, the Views exist in the layout.

Edit 1:

I have added code that overrides the setContentView();

How to&Answers:

Its simple.

Have your abstract BaseActivity as:

public abstract class BaseActivity extends AppCompatActivity {

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(getLayoutResource());
        ButterKnife.bind(this);
    }

    protected abstract int getLayoutResource();
}

Then simply extend your activity with BaseActivity as:

public class SplashActivity extends BaseActivity {

    @BindView(R.id. txtName)
    TextView textView;

    @Override
    protected int getLayoutResource() {
        return R.layout.activity_splash;
    }
}

I prefer to have multiple Base classes in my project. You can have a BaseToolbarActivity that will extend BaseActivity as:

public abstract class BaseToolBarActivity extends BaseActivity {

    protected static final int RESOURCE_NO_MENU = 0;

    @BindView(R.id.toolbar)
    Toolbar mToolbar;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setToolBar();
    }

    public void setToolBar() {
        setSupportActionBar(mToolbar);
    }

    protected abstract int getMenuResource();

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        if (getMenuResource() == RESOURCE_NO_MENU)
            return super.onCreateOptionsMenu(menu);
        else {
            getMenuInflater().inflate(getMenuResource(), menu);
            return true;
        }
    }
}

So, if your Activity has layout Toolbar, then extend it to BaseToolbarActivity, else extend it to BaseActivity

For more such classes, you can refer my project at

https://github.com/chintansoni202/Android-Master-Project

Answer:

I just realized looking at one of the comments.. I think your real problem isn’t to do with the subclassing at all.. it’s the layout.
I think you are wanting to include the subclass layout in the superclass layout.. unfortunately you’re clobbering the superclass layout in the subclass.
I think you need to include the base activity layout in your subclass (probably wrapped in a merge tag in the layout).. see
Re-using Layouts with include – include your baseactivity layout (toolbar etc) in your subclass layout (splash screen)..

(I think the below information is now irrelevant, but leaving it there in case you might be interested)

So, if I understand the problem correctly, it seems that essentially the butterknife bindng in the super class (base activity) is happening before the subclass (splashactivity) can set up its layout.

The idea of using a getLayourResourceId (overridden in the subclass) probably works for most people as it is a way of allowing the subactivity to specify its layout to the super activity just before it calls butterknife.bind.

So, assuming this is correct, another way of approaching the problem would be to delay the binding until the layout has been specified.

in other words;

public class BaseActivity extends AppCompatActivity {

    // Removed layout parameter
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        // It may not make sense to set the content view if it is always
        // replaced by the subclasses, but maybe you want a default?
        setContentView(R.layout.baseactivitylayout);

… bind will be called later when onStart is called….
}

   ...

   public void onStart() {
       super.onStart();
       ButterKnife.bind(this);
   }

   ...

}

and

public class SplashActivity extends BaseActivity {

    @BindView(R.id.txtName)
    TextView txtName;

    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.splashLayout);
        // butterknife.bind will be called when BaseActivity's onStart is called.
    }
}

Of course it isn’t always convenient (or correct) to bind from onStart, but it might help to shed light on how the other solutions work.

Good luck,
CJ

Answer:

The message in the exception tells you what to do:

Required view ‘txtName’ with ID 2131296567 for field ‘txtName’ was not found. If this view is optional add ‘@nullable’ annotation.

Either add it to your layout or make the field binding optional with a @Nullable annotation.

See this Link

Answer:

in Activity A you are set the instance of activity A. remove the butterknife.bind in BaseActivity.

 super.setContentView(layout);
    ButterKnife.bind(this);

assign the View instance in childActivity with ButterKnife.bind working fine .

   super.onCreate(savedInstanceState);
    ButterKnife.bind(this);

for fragment

ButterKnife.Bind(View,this);

you are remove the butterknife in base activity.
reason in that :- in base activity are no view are present butterknife assign unnecessary suppose there are more than a 10 Activity there are not easy to manage.

Answer:

Update your BaseActivity as:

public class BaseActivity extends AppCompatActivity {

    protected void onCreate(Bundle savedInstanceState, int layout) {
        super.onCreate(savedInstanceState);
        setContentView(layout);
        ButterKnife.bind(this);
    }

}

And SubActivity as:

public class SplashActivity extends BaseActivity {

    @BindView(R.id.txtName)
    TextView txtName;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState, R.layout.activity_splash);
    }
}