Jim Cullen
Jim Cullen

Reputation: 472

Data binding "error: cannot find symbol class Models"

First, I need to acknowledge the clearly very similar but not duplicate issue here. None of the proposed solutions in that thread work.

My application file structure is as follows:

app
  java
    [mydomain].[myapplication]
      Models
        DataModel.java
      MainActivity.java
  res
    layout
      activity_main.xml
      content_main.xml
      my_inner_layout.xml

My app build.gradle contains

dataBinding {
    enabled = true
}

In MainActivity.java I have

import [mydomain].[myapplication].Models.DataModel;

public class MainActivity extends AppCompatActivity
        implements NavigationView.OnNavigationItemsSelectedListener {

    DataModel dataModel;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        ... <other layout creation template code> ...

        ActivityMainBinding binding = DataBindingUtil.setContentView(this, R.layout.activity_main);
        dataModel = new DataModel();
        binding.setValues(dataModel);
    }

    <navigation and auto-generated methods>
}

My my_inner_layout.xml contains

<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">
    <data>
        <variable
            name="values"
            type="[mydomain].[myapplication].Models.DataModel" />
    </data>
    <android.support.constraint.ConstraintLayout
        xmlns:app="http://schemas.android.com/apk/res-auto"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:visibility="visible"
        >

        <TextView
            android:id="@+id/intro_text"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_marginBottom="20dp"
            android:text="@{values.valueOne}"/>

        <TextView
            android:id="@+id/buying_recommendation"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_below="@id/intro_text"
            app:layout_constraintTop_toBottomOf="@id/intro_text"
            android:text="@{values.valueTwo}"/>
    </android.support.constraint.ConstraintLayout>
</layout>

I am passing bind:values="@{values}" through from activity_main to its included app_bar_main to its content_main to its my_inner_layout with that same <data> value in each. Android Studio is telling me "namespace 'bind' is not bound".

If I try to run this, I get "Compilation failed; see the compiler error output for details." Looking in the build output, I see:

Java compiler errors when building

In text, the errors are variously error: cannot find symbol class Models and error: package Models does not exist

If I move DataModel.java out of the Models package and directly in to [mydomain].[myapplication], then I get a different result. It does build and run in the emulator, but with much of the layout information failing to appear. No hamburger menu in the top left, no title text in the header, and no settings button in the top right values previously automatically included by the autogenerated code in Android Studio. I am unable to set the title in code using setTitle, either.

Swiping from the left does bring in the navigation drawer however.

I have tried invalidating caches and restarting, cleaning, rebuilding both with the model file in Models and separately.

What I want, chiefly, is to be able to use the project structure I want. To put my models classes in a models sub-package. Once that is complete, I want to make sure the full layout information comes through, including the hamburger menu icon, settings icon, and title. How can I achieve this?

Upvotes: 5

Views: 12183

Answers (3)

Praful
Praful

Reputation: 17

Just convert your existing layouts to data binding layouts (Don't forget to add variable in your xml with your activity mentioned in type)

Example:

<variable name="navdrawer" type="com.example.sampleapp.HomeScreenActivity" />

This will generate a data binding class for NavigationHeaderView in this format(May differ) NavHeaderYourActivityName.

When you bind your parent activity, you will use that binding instance to get DrawerLayout and your NavigationView, respectively.

A sample code to reduce the boilerplate for initializing views in your code:

NavHeaderHomeScreenBinding navHeaderHomeScreenBinding = 
DataBindingUtil.setContentView(this, R.layout.nav_header_home_screen);
AppBarHomeScreenBinding appBarHomeScreenBinding = 
DataBindingUtil.setContentView(this, R.layout.app_bar_home_screen);
ActionBarDrawerToggle toggle = 
new ActionBarDrawerToggle(this, binding.drawerLayout, appBarHomeScreenBinding.toolbar, 
R.string.navigation_drawer_open, R.string.navigation_drawer_close);
binding.drawerLayout.addDrawerListener(toggle);
toggle.syncState();
binding.navView.setNavigationItemSelectedListener(this);

I hope this helps someone! Thank you!

Upvotes: -1

Jim Cullen
Jim Cullen

Reputation: 472

Okay, I realised where the "class Models does not exist" thing comes from. I don't know whether to blame my own stupidity or the stupidly nitpicky way this binding is implemented on Android. The package needed to be called models with a lower case "m", not Models. The binding auto-name-conversion thing must have thought Models was a class, not a package.

To fix the layout, the onCreate method had to be changed to

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

    setContentView(R.layout.activity_main);

    ActivityMainBinding binding = DataBindingUtil.setContentView(this, R.layout.activity_main);
    dataModel = new DataModel();
    cycleInformationBinding.setRecommendation(dataModel);

    // set toolbar
    Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
    setSupportActionBar(toolbar);

    // Drawer layout setting
    DrawerLayout drawer = (DrawerLayout) findViewById(R.id.drawer_layout);
    ActionBarDrawerToggle toggle = new ActionBarDrawerToggle(
            this, drawer, toolbar, R.string.navigation_drawer_open, R.string.navigation_drawer_close);
    drawer.addDrawerListener(toggle);
    toggle.syncState();

    NavigationView navigationView = (NavigationView) findViewById(R.id.nav_view);
    navigationView.setNavigationItemSelectedListener(this);
}

Specifically, things had to happen in the order:

  1. setContentView to the main activity
  2. Set up the data model binding
  3. Layout concerns like drawer and toolbar.

Any other order would cause either the model binding to fail or the toolbar to not display correctly.

Upvotes: 26

Onix
Onix

Reputation: 692

First try to remove redurant setContentView(R.layout.activity_main);

ActivityMainBinding binding = DataBindingUtil.setContentView(this, R.layout.activity_main);

Second add to end of "onCreate" function

binding.included.setValues(dataModel);
binding.executePendingBindings();

if you use include than you may add to your include id for example

<include
    android:id="@+id/included"
    layout="@layout/content_main"
    app:values="@{DataModel}"/>

and use

binding.included.setValues(dataModel);

research about using databinding with included layouts

Upvotes: -3

Related Questions