Search code examples
androidandroid-databindingandroid-viewmodel

Generated ViewDataBinding class in Android Studio 3.2.1 has an error when view layout data variable type is contained in a subpackage


I have my viewmodels contained in a "ViewModels" package. When setting one of them as a data variable type in my fragment layout xml file the generated ViewDataBinding class attempts to import the package as if it were a file. For example:

import com.xyz.myapp.ViewModels;

Rather than:

import com.xyz.myapp.ViewModels.*;

It then goes on to reference the viewmodel as ViewModels.MyFragmentViewModel causing further errors. I found that a workaround for this is to put all my viewmodel files in my com.xyz.myapp directory. When doing this everything works fine.

This is the same as asked here. I don't have enough reputation to comment. Am I missing something? Is there some option I need to set? Or is this just a bug?


TestFragment.java

public class TestFragment extends Fragment {
    @Inject
    MyViewModelFactory viewModelFactory;

    private MyFragmentViewModel vm;

    public TestFragment() {
        // Required empty public constructor
    }

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        vm = ViewModelProviders.of(this, viewModelFactory).get(MyFragmentViewModel.class);
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        // Inflate the layout for this fragment
        FragmentTestBinding binding = DataBindingUtil.inflate(inflater, R.layout.fragment_test, container, false);
        View view = binding.getRoot();
        binding.setVm(vm);
        return view;
    }

    @Override
    public void onAttach(Context context) {
        AndroidSupportInjection.inject(this);
        super.onAttach(context);
    }

    @Override
    public void onDetach() {
        super.onDetach();
    }
}


fragment_test.xml

<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">
    <data>
        <variable
            name="vm"
            type="com.xyz.myapp.ViewModels.MyFragmentViewModel"/>
    </data>
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical">


        <EditText
            android:id="@+id/editText"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:ems="10"
            android:inputType="textPersonName"
            android:text="@{vm.TEMP}" />
    </LinearLayout>
</layout>


MyFragmentViewModel.java

public class MyFragmentViewModel extends ViewModel {
    public MyFragmentViewModel (){}
    public String TEMP = "TEST STRING";
}


FragmentTestBinding.java

This is the generated class. Error lines are commented. 4 errors

import com.xyz.myapp.ViewModels; //Error

public abstract class FragmentTestBinding extends ViewDataBinding {
  @NonNull
  public final CheckBox checkBox;

  @NonNull
  public final EditText editText;

  @Bindable
  protected ViewModels.ProfileViewModel mVm; //Error

  protected FragmentTestBinding(DataBindingComponent _bindingComponent, View _root,
      int _localFieldCount, CheckBox checkBox, EditText editText) {
    super(_bindingComponent, _root, _localFieldCount);
    this.checkBox = checkBox;
    this.editText = editText;
  }

  public abstract void setVm(@Nullable ViewModels.ProfileViewModel vm); //Error

  @Nullable
  public ViewModels.ProfileViewModel getVm() { //Error
    return mVm;
  }

  @NonNull
  public static FragmentTestBinding inflate(@NonNull LayoutInflater inflater,
      @Nullable ViewGroup root, boolean attachToRoot) {
    return inflate(inflater, root, attachToRoot, DataBindingUtil.getDefaultComponent());
  }

  @NonNull
  public static FragmentTestBinding inflate(@NonNull LayoutInflater inflater,
      @Nullable ViewGroup root, boolean attachToRoot, @Nullable DataBindingComponent component) {
    return DataBindingUtil.<FragmentTestBinding>inflate(inflater, com.camapps.linkshare.R.layout.fragment_test, root, attachToRoot, component);
  }

  @NonNull
  public static FragmentTestBinding inflate(@NonNull LayoutInflater inflater) {
    return inflate(inflater, DataBindingUtil.getDefaultComponent());
  }

  @NonNull
  public static FragmentTestBinding inflate(@NonNull LayoutInflater inflater,
      @Nullable DataBindingComponent component) {
    return DataBindingUtil.<FragmentTestBinding>inflate(inflater, com.camapps.linkshare.R.layout.fragment_test, null, false, component);
  }

  public static FragmentTestBinding bind(@NonNull View view) {
    return bind(view, DataBindingUtil.getDefaultComponent());
  }

  public static FragmentTestBinding bind(@NonNull View view,
      @Nullable DataBindingComponent component) {
    return (FragmentTestBinding)bind(component, view, com.camapps.linkshare.R.layout.fragment_test);
  }
}



Compiler Output

Java compiler: (4 errors)
C:/Users/user/AndroidStudioProjects/myapp   
    app/build/generated/data_binding_base_class_source_out/debug/dataBindingGenBaseClassesDebug/out 
        com/xyz/myapp/databinding/FragmentTestBinding.java  
            error: cannot find symbol class ViewModels  
            error: package ViewModels does not exist    
            error: package ViewModels does not exist    
            error: package ViewModels does not exist    

Solution

  • This is an interesting issue that shows the need to follow conventions. As stated in Android - Package Name convention,

    Android follows normal java package conventions

    and Oracle suggests that

    Package names are written in all lower case to avoid conflict with the names of classes or interfaces.

    Now the Android Data Binding Library or another dependent library tries to create clean code and considers the class name to be ViewModels.ProfileViewModel, following and expecting the general Java naming conventions.

    To fix your issue, start to follow the given conventions and rename your package to viewmodels.