【问题标题】:databinding recyclerview adapter received null list from livedata数据绑定 recyclerview 适配器从 livedata 接收到空列表
【发布时间】:2019-08-19 06:19:19
【问题描述】:

我想为 recyclerview 提供元素列表和实时数据。我使用了数据绑定适配器,当我想向适配器发送数据时,列表从 livedata 返回 null。整个代码如下。 谢谢

我调试了代码,我看到数据何时来自 AppDbHelper.java 到 HomeViewmodel.java 并设置为绑定适配器未更改的实时数据。因此,虽然 livedata 不为空,但数据绑定适配器(heartRateResultsModels)处的数组列表为空,导致应用程序崩溃。

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;
            }
        });
    }
}

【问题讨论】:

    标签: android mvvm android-recyclerview android-databinding android-livedata


    【解决方案1】:

    我错过了设置

    mViewDataBinding.vm = viewModel

    其中“vm”是布局中的变量,“viewModel”是活动或片段中的局部变量

    此外,正如@Menma 建议的那样,不要忘记添加

    mViewDataBinding.setLifecycleOwner(this);

    编辑: 如果视图在可见性逻辑方面没有更新,请确保分配了“id”属性(尤其是在包含布局时)

    【讨论】:

      【解决方案2】:

      在方法onViewCreated() 上添加mViewDataBinding.setLifecycleOwner(this); BaseFragment,像这样:

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

      【讨论】:

        【解决方案3】:

        1。始终确保在 UI 控制器(片段或活动)中设置 XML 绑定变量

        在您的 XML 布局中

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

        在您的 UI 控制器中(本例中为 Fragment)

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

        2。有必要将 setLifecycleOwner() 设置为 LifeCycleOwner,这本质上是您的 UI 控制器,否则您的 LiveData 将失去其 Live 效果,因为它无法被观察到,本质上您的 LiveData 将不会传播到用户界面。

        bindingObj.setLifeCycleOwner = this
        

        this = UI 控制器(同样是 Activity/Fragment)

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2016-03-05
          • 1970-01-01
          • 2020-09-27
          • 2017-12-15
          • 1970-01-01
          • 2019-02-04
          • 2018-05-29
          • 1970-01-01
          相关资源
          最近更新 更多