0

I want to feed the recyclerview with a list of elements and livedata. I used data binding adapter and when I want to send data to adapter the list return null from livedata. the whole code are below. thanks

I debugged the code and I saw when the data came from the AppDbHelper.java to HomeViewmodel.java and set to the live data the bindingadapter didn't change. thus, although the livedata is not null, the arraylist at databinding adapter(heartRateResultsModels) is null and made the app crashed.

1.fragment_home.xml

<?xml version="1.0" encoding="utf-8"?>
<layout 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">

    <data>

        <variable
            name="viewModel"
            type="ir.basamadazmanovin.heartrate.ui.main.home.HomeViewModel" />
    </data>

    <androidx.swiperefreshlayout.widget.SwipeRefreshLayout
        android:id="@+id/swipe_refresh_layout"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        app:refreshing="@{viewModel.isLoading}">

        <FrameLayout
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            tools:context=".ui.main.home.HomeFragment">

            <androidx.recyclerview.widget.RecyclerView
                android:id="@+id/fragment_home_recyclerView"
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                app:adapter="@{viewModel.heartRateResultsLiveData}"
                tools:listitem="@layout/row_fragment_home" />


        </FrameLayout>
    </androidx.swiperefreshlayout.widget.SwipeRefreshLayout>
</layout>

2.BaseFragment.java

public abstract class BaseFragment<T extends ViewDataBinding, V extends BaseViewModel> extends Fragment {

    protected FragmentNavigation mFragmentNavigation;
    private BaseActivity mActivity;
    private View mRootView;
    private T mViewDataBinding;
    private V mViewModel;

    public abstract int getBindingVariable();

    public abstract
    @LayoutRes
    int getLayoutId();

    public abstract V getViewModel();

    @Override
    public void onAttach(Context context) {
        super.onAttach(context);
        if (context instanceof BaseActivity) {
            BaseActivity activity = (BaseActivity) context;
            this.mActivity = activity;
            activity.onFragmentAttached();
        }
        if (context instanceof FragmentNavigation) {
            mFragmentNavigation = (FragmentNavigation) context;
        } else {
            throw new RuntimeException(context.toString()
                    + " must implement OnFragmentInteractionListener");
        }
    }

    @Override
    public void onCreate(@Nullable Bundle savedInstanceState) {
        performDependencyInjection();
        super.onCreate(savedInstanceState);
        mViewModel = getViewModel();
        setHasOptionsMenu(false);
    }

    @Override
    public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        mViewDataBinding = DataBindingUtil.inflate(inflater, getLayoutId(), container, false);
        mRootView = mViewDataBinding.getRoot();
        return mRootView;
    }

    public BaseActivity getBaseActivity() {
        return mActivity;
    }

    public T getViewDataBinding() {
        return mViewDataBinding;
    }

    public void hideKeyboard() {
        if (mActivity != null) {
            mActivity.hideKeyboard();
        }
    }

    public boolean isNetworkConnected() {
        return mActivity != null && mActivity.isNetworkConnected();
    }

    public void openActivityOnTokenExpire() {
        if (mActivity != null) {
            mActivity.openActivityOnTokenExpire();
        }
    }

    private void performDependencyInjection() {
        AndroidSupportInjection.inject(this);
    }

    public interface Callback {

        void onFragmentAttached();

        void onFragmentDetached(String tag);
    }
}

3.HomeFragment.java

public class HomeFragment extends BaseFragment<FragmentHomeBinding,HomeViewModel> implements HomeNavigator {

    FragmentHomeBinding mFragmentHomeBinding;
    @Inject
    LinearLayoutManager mLayoutManager;
    @Inject
    HeartRateResultsAdapter mAdapter;
    @Inject
    ViewModelProviderFactory factory;




    private HomeViewModel mHomeViewModel;

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

    @Override
    public int getBindingVariable() {
        return BR.viewModel;
    }

    @Override
    public int getLayoutId() {
        return R.layout.fragment_home;
    }

    @Override
    public HomeViewModel getViewModel() {
        mHomeViewModel = ViewModelProviders.of(this, factory).get(HomeViewModel.class);
        return null;
    }

    @Override
    public void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        mHomeViewModel.setNavigator(this);

    }

    @Override
    public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);
        mFragmentHomeBinding = getViewDataBinding();
        setUp();
    }

    private void setUp() {
        mLayoutManager.setOrientation(RecyclerView.VERTICAL);
        mFragmentHomeBinding.fragmentHomeRecyclerView.setLayoutManager(mLayoutManager);
        mFragmentHomeBinding.fragmentHomeRecyclerView.setItemAnimator(new DefaultItemAnimator());
        mFragmentHomeBinding.fragmentHomeRecyclerView.setAdapter(mAdapter);
    }

    @Override
    public void handleError(Throwable throwable) {

    }
}

4.BaseViewModel.java

public class BaseViewModel<N> extends ViewModel {
    private final DataManager mDataManager;

    private final ObservableBoolean mIsLoading = new ObservableBoolean(false);

    private final SchedulerProvider mSchedulerProvider;

    private CompositeDisposable mCompositeDisposable;

    private WeakReference<N> mNavigator;

    public BaseViewModel(DataManager dataManager,
                         SchedulerProvider schedulerProvider) {
        this.mDataManager = dataManager;
        this.mSchedulerProvider = schedulerProvider;
        this.mCompositeDisposable = new CompositeDisposable();
    }

    @Override
    protected void onCleared() {
        mCompositeDisposable.dispose();
        super.onCleared();
    }

    public CompositeDisposable getCompositeDisposable() {
        return mCompositeDisposable;
    }

    public DataManager getDataManager() {
        return mDataManager;
    }

    public ObservableBoolean getIsLoading() {
        return mIsLoading;
    }

    public void setIsLoading(boolean isLoading) {
        mIsLoading.set(isLoading);
    }

    public N getNavigator() {
        return mNavigator.get();
    }

    public void setNavigator(N navigator) {
        this.mNavigator = new WeakReference<>(navigator);
    }

    public SchedulerProvider getSchedulerProvider() {
        return mSchedulerProvider;
    }
}

5.HomeViewModel.java

public class HomeViewModel extends BaseViewModel<HomeNavigator> {
    private  MutableLiveData<ArrayList<HeartRateResultsModel>> heartRateResultsLiveData;

    public HomeViewModel(DataManager dataManager, SchedulerProvider schedulerProvider) {
        super(dataManager, schedulerProvider);
        heartRateResultsLiveData = new MutableLiveData<>();
        fetchRepos();
    }
    public void fetchRepos() {
        setIsLoading(true);
        getCompositeDisposable().add(getDataManager()
                .getHeartRateResults()
                .subscribeOn(getSchedulerProvider().io())
                .observeOn(getSchedulerProvider().ui())
                .subscribe(heartRateResultsModels -> {
                    heartRateResultsLiveData.setValue(heartRateResultsModels);
                    setIsLoading(false);
                }, throwable -> {
                    setIsLoading(false);
                    getNavigator().handleError(throwable);
                }));
    }

    public LiveData<ArrayList<HeartRateResultsModel>> getHeartRateResultsLiveData() {
        return heartRateResultsLiveData;
    }
}

6.BindingUtils.java

public final class BindingUtils {

    private BindingUtils() {
        // This class is not publicly instantiable
    }


    @BindingAdapter("imageUrl")
    public static void setImageUrl(ImageView imageView, String url) {
        Context context = imageView.getContext();
        Glide.with(context).load(url).into(imageView);
    }

    @BindingAdapter("onNavigationItemSelected")
    public static void setOnNavigationItemSelectedListener(
            BottomNavigationView view, OnNavigationItemSelectedListener listener) {
        view.setOnNavigationItemSelectedListener(listener);
    }


    @BindingAdapter({"adapter"})
    public static void addHeartRateResultsItems(RecyclerView recyclerView,
                                                ArrayList<HeartRateResultsModel> heartRateResultsModels) {
        HeartRateResultsAdapter adapter = (HeartRateResultsAdapter) recyclerView.getAdapter();
        if (adapter != null) {
            adapter.clearItems();
            adapter.addItems(heartRateResultsModels);
        }
    }
}

7.DataManager.java

public interface DataManager extends DbHelper, PreferencesHelper, ApiHelper {

}

8.DbHelper.java

public interface DbHelper {
    Single<ArrayList<HeartRateResultsModel>> getHeartRateResults();

}

9.AppDataManager.java

public class AppDataManager implements DataManager{

    private final ApiHelper mApiHelper;

    private final Context mContext;

    private final DbHelper mDbHelper;

    private final Gson mGson;

    private final PreferencesHelper mPreferencesHelper;

    @Inject
    public AppDataManager(Context context,
                          DbHelper dbHelper,
                          PreferencesHelper preferencesHelper,
                          ApiHelper apiHelper,
                          Gson gson) {
        mContext = context;
        mDbHelper = dbHelper;
        mPreferencesHelper = preferencesHelper;
        mApiHelper = apiHelper;
        mGson = gson;
    }

    @Override
    public Single<ArrayList<HeartRateResultsModel>> getHeartRateResults() {
        return mDbHelper.getHeartRateResults();
    }


}

10.AppDbHelper.java

@Singleton
public class AppDbHelper implements DbHelper{


    private final AppDatabase mAppDatabase;

    @Inject
    public AppDbHelper(AppDatabase appDatabase) {
        this.mAppDatabase = appDatabase;

    }



    @Override
    public Single<ArrayList<HeartRateResultsModel>> getHeartRateResults() {
        return Single.fromCallable(new Callable<ArrayList<HeartRateResultsModel>>() {
            @Override
            public ArrayList<HeartRateResultsModel> call() throws Exception {
                ArrayList<HeartRateResultsModel> models = new ArrayList<>();
                models.add(new HeartRateResultsModel("","1"));
                models.add(new HeartRateResultsModel("","2"));
                return models;
            }
        });
    }
}

0

3 Answers 3

4

I was missing on setting

mViewDataBinding.vm = viewModel

where "vm" is the variable in layout and "viewModel" is the local variable in activity or fragment

Furthermore, as @Menma suggested, don't forget to add

mViewDataBinding.setLifecycleOwner(this);

EDIT: If the view is not updating in terms of Visibility logic, then make sure you have the 'id' attribute assigned (especially if you are including a layout)

1

1. Always make sure to set your XML binding variables in your UI controller(Fragment or Activity)

In your XML layout

<data>
<variable
    name="viewModel"
    type="com.felixfavour.ViewModel" />
</data>

In your UI controller (Fragment in this case)

listViewModel = ViewModel(this).get(ListViewModel::class)
bindingObj.viewModel = listViewModel

2. It is necessary that you set the setLifecycleOwner() to the LifeCycleOwner which is inherently your UI controller, without this your LiveData will lose its Live effect as it will not be able to be observed, essentially your LiveData will not be propagated to the UI.

bindingObj.setLifeCycleOwner = this

where this = UI Controller(again Activity/Fragment)

0

add mViewDataBinding.setLifecycleOwner(this); on method : onViewCreated() at class BaseFragment, like this :

@Override
    public void onViewCreated(@NonNull View view, Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);
        mViewDataBinding.setVariable(getBindingVariable(), mViewModel);
        mViewDataBinding.setLifecycleOwner(this);
        mViewDataBinding.executePendingBindings();
    }

Not the answer you're looking for? Browse other questions tagged or ask your own question.