Base Activity extending Activity ViewBinding Crash

2.8k Views Asked by At

I have Base Activity extends My all other activities. The Base Activity has features like toolbar, progress bar, and other logic methods that are similar across the activity

After Migrating from ButterKnife to ViewBinding The child activity that is extended from the parent base is not able to access the methods in it And the app gets crashed.

Below is Base Activity Code

pubic class BaseActivity extends AppCompatActivity {


    private ActivityBaseBinding activityBaseBinding;

 

    @Override
    public void setContentView(int layoutResID) {
        activityBaseBinding = ActivityBaseBinding.inflate(getLayoutInflater());

        View  view = getLayoutInflater().inflate(layoutResID, activityBaseBinding.container, false);

        if (layoutResID == R.layout.activity_home) {
            activityBaseBinding.toolbarTitle.setVisibility(View.GONE);
        } else if (layoutResID == R.layout.activity_my_account) {
            activityBaseBinding.toolbarTitle.setVisibility(View.VISIBLE);
        } else {
            activityBaseBinding.toolbarTitle.setVisibility(View.GONE);
        }

        activityBaseBinding.container.addView(view);
        setContentView(activityBaseBinding.getRoot());
        activityBaseBinding.imgBackArrow.setOnClickListener(v -> onBackPressed());

    }

If I try to access the toolbar in the base from the child activity this is the error I get

java.lang.RuntimeException: Unable to start activity ComponentInfo{com.iowave.scheduler/com.example.myaccount.views.MyAccount}: java.lang.NullPointerException: Attempt to read from field 'com.google.android.material.appbar.MaterialToolbar com.example.databinding.ActivityBaseBinding.toolbarTitle' on a null object reference

This is the code of my child activity

public class MyAccount extends BaseActivity implements MyAccountImpl {

    private NavController navController;


    private ActivityMyAccountBinding activityMyAccountBinding;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        activityMyAccountBinding = ActivityMyAccountBinding.inflate(getLayoutInflater());
        View view = activityMyAccountBinding.getRoot();
        setContentView(view);
             }
}

From what I understand the view binding is only binding the child view and removing all other bindings from the background.

2

There are 2 best solutions below

0
On

This could be solve your problem:

I just migrated the project from Butterknife to ViewBinding and face problems related implementing encapsulated activities view into base activity' container, too. Before implementing ViewBinding, I was using getLayoutInflater().inflate(layoutResID, activityContainer, true); where layoutResID was id of encapsulated activities' xml layout (R.layout.activity_main) in BaseActivity so I could inflate encapsulated activities' xml inside activityContainer which is corresponding to FrameLayout inside BaseActivity's xml. But then I figure out that, with ViewBinding, there is no ViewGroup id (xml) but View. So I couldn't use getLayoutInflater().inflate(layoutResID, activityContainer, true); because layoutResID is a ViewGroup id, not View. Here is how I solved.:

abstract class BaseActivity<VB : ViewBinding> : AppCompatActivity() {

    private var _binding: VB? = null
    
    val childActivityBinding: VB
            get() = _binding as VB

    abstract fun inflateLayout(
        parent: FrameLayout,
        inflater: LayoutInflater
    ): VB

    lateinit var baseActivityBinding: ActivityBaseBinding

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        baseActivityBinding = ActivityBaseBinding.inflate(layoutInflater)
        _binding = inflateLayout(baseActivityBinding.root.layout_container, layoutInflater)

        val coordinatorLayout = baseActivityBinding.root
        .
        .// some base activity processes like setting Snackbar etc.
        val snack = coordinatorLayout.snack
        .

        super.setContentView(coordinatorLayout)
    }
}

Here is Main Activity: [ in Java :) ]

public class MainActivity extends BaseActivity<ActivityMainBinding> {

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

    @NonNull
    @Override
    public ActivityMainBinding inflateLayout(@NonNull FrameLayout parent, @NonNull LayoutInflater inflater) {
        return ActivityMainBinding.inflate(inflater, parent, true);
    }
}

With this approach, I was able to inflate the encapsulated activities view inside base activity like a subview.

0
On

Use this approach for base activity patterns.

    abstract class  BaseActivity1<Binding extends ViewBinding> extends AppCompatActivity {

    Binding binding;

    abstract  Binding getBinding();

    void  initBinding(){
        binding=getBinding();
    }
    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        this.initBinding();
        setContentView(binding.getRoot());
    }  
}

then

class MainActivity extends BaseActivity1<ActivityMainBinding>{
    @Override
    ActivityMainBinding getBinding(){
        return ActivityMainBinding.inflate(getLayoutInflater());
    }
}