Search code examples
androidandroid-databinding

Using data binding to run method on button click


Trying to run method in a layout file from my view model but, I keep getting error: cannot find symbol class ViewModels from my layout file. The layout file is a fragment. I tried invalidating the cache and resyncing but, it did not help. Do i need to add anything to the fragment itself? I seen people use data binding to launch the fragment but, I've read its optional.

Update: Took out OnClick method to test and it is still throwing error. I guess the problem is with my deceleration but, idk why. When i am editing the layout the path shows up when I type the view model name in.

Update2: Tried setting type in variable equal to path of activity that launches fragment for testing and it built just fine. There must be a way to add an import that is not my activity.

Layout file

<?xml version="1.0" encoding="utf-8"?>
<layout>
<data>
    <variable
        name="viewTest"
        type="rangers.socmanv2.ViewModels.BattleRhythmViewModel" />
</data>
<android.support.constraint.ConstraintLayout  xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/battleRhythmMenu"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".Fragments.BattleRhythmFrag">

<Button
    android:id="@+id/newBattle"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_marginStart="76dp"
    android:layout_marginLeft="76dp"
    android:layout_marginTop="88dp"
    android:text="New Battle Rhythm"
    app:layout_constraintStart_toStartOf="parent"
    app:layout_constraintTop_toTopOf="parent"
  />


<Button
    android:id="@+id/editBattle"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_marginStart="76dp"
    android:layout_marginLeft="76dp"
    android:layout_marginTop="50dp"
    android:text="Edit Battle Rhythm"
    app:layout_constraintStart_toStartOf="parent"
    app:layout_constraintTop_toBottomOf="@+id/newBattle" />

<Button
    android:id="@+id/deleteBattle"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_marginStart="76dp"
    android:layout_marginLeft="76dp"
    android:layout_marginTop="50dp"
    android:text="Delete Battle Rhythm"
    app:layout_constraintStart_toStartOf="parent"
    app:layout_constraintTop_toBottomOf="@+id/editBattle" />

</android.support.constraint.ConstraintLayout>

</layout>

Fragment

public class BattleRhythmFrag extends Fragment {

private BattleRhythmViewModel mViewModel;
private View view;
private Button test;

public static BattleRhythmFrag newInstance() {
    return new BattleRhythmFrag();
}

@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container,
                         @Nullable Bundle savedInstanceState) {


    view =  inflater.inflate(R.layout.battle_rhythm_fragment, container, false);

    mViewModel = new BattleRhythmViewModel();



    return view;
}

@Override
public void onActivityCreated(@Nullable Bundle savedInstanceState) {
    super.onActivityCreated(savedInstanceState);
    mViewModel = ViewModelProviders.of(this).get(BattleRhythmViewModel.class);
    // TODO: Use the ViewModel
}


}

View model


public class BattleRhythmViewModel extends ViewModel {


    public void launchNewFragment(FragmentManager fragM)
    {
        Log.d("viewmodel","hitting launchNewFrag");
        HomeFrag test = new HomeFrag();
        fragM.beginTransaction().replace(R.id.homeContent,test,"launched"+test);

    }

    public void test()
    {Log.d("viewmodel","hitting test");
    }


    }


Solution

  • Your error is in viewModel constructor. you must add a default constructor beacuse you don't added any factory ViewModelProvider.Factory but your BattleRhythmViewModel dosen't have any default constructor.

    ANSWER 1:

    
    public class BattleRhythmViewModel extends ViewModel {
    
        public void launchNewFragment(){
        }
    
        public void test()
        {Log.d("viewmodel","hitting test");
        }
        }
     }
    
    

    ANSWER 2: better than answer 1 beacuse you don't need add default and can use your fragment in model

    
    public class Factory implements ViewModelProvider.Factory {
        private FragmentManager fragM;
    
        public Factory(FragmentManager fragM) {
            this.fragM= fragM;
        }
    
        @NonNull
        @Override
        public <T extends ViewModel> T create(@NonNull Class<T> modelClass) {
            if (modelClass.isAssignableFrom(BattleRhythmViewModel.class)) {
                return (T) new BattleRhythmViewModel(fragM);
            }
            throw new IllegalArgumentException("Unknown ViewModel class");
        }
    }
    

    change your viewmodel creator line to

        Factory factory = new Factory(this)
        mViewModel = ViewModelProviders.of(this,factory).get(BattleRhythmViewModel.class);