每次

时间:2017-06-13 09:14:31

标签: android android-view rx-android android-databinding

我有一个ViewModel,旨在控制ActivityFragment的状态。此ViewModel包含4个ObservableBoolean,在我的布局中使用它来判断哪个视图必须可见:

public class ContentLoaderViewModel extends BaseObservable {

    public static final int LOADING_LAYOUT = 0;
    public static final int CONTENT_LAYOUT = 1;
    public static final int NO_DATA_LAYOUT = 2;
    public static final int ERROR_LAYOUT = 3;

    public final ObservableBoolean loading = new ObservableBoolean();
    public final ObservableBoolean loaded = new ObservableBoolean();
    public final ObservableBoolean noDataFound = new ObservableBoolean();
    public final ObservableBoolean hasErrors = new ObservableBoolean();

    @Bindable
    public int displayedLayout;

    public ContentLoaderViewModel() {
        setDisplayedLayout(LOADING_LAYOUT);
    }

    public ContentLoaderViewModel(int displayedLayout) {
        setDisplayedLayout(displayedLayout);
   }

    public int getDisplayedLayout() {
        return displayedLayout;
    }

    public void setDisplayedLayout(int displayedLayout) {
        this.displayedLayout = displayedLayout;
        notifyPropertyChanged(BR.displayedLayout);

        loading.set(displayedLayout == LOADING_LAYOUT);
        loaded.set(displayedLayout == CONTENT_LAYOUT);
        noDataFound.set(displayedLayout == NO_DATA_LAYOUT);
        hasErrors.set(displayedLayout == ERROR_LAYOUT);
    }
}

我在我的一个片段中使用它。这个片段旨在提供一个简单的界面来显示列表。一旦创建了适配器,它就会显示一个RecyclerView。适配器是异步创建的,然后初始化RecyclerView。所以起初片段处于" loading"当它收到适配器时,它会改变为" content"的状态。这是它的样子:

XML

<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    xmlns:android="http://schemas.android.com/apk/res/android">

    <data>

        <import type="android.view.View" />
        <variable
            name="viewModel"
            type="app.viewmodels.ContentLoaderViewModel" />
        <variable
            name="noDataText"
            type="String" />
        <variable
            name="bottomButtonText"
            type="String" />
        <variable
            name="showBottomButton"
            type="boolean" />

    </data>

    <android.support.constraint.ConstraintLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent">

        <Button
            android:text="@{bottomButtonText}"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:id="@+id/bottomButton"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintRight_toRightOf="parent"
            app:layout_constraintLeft_toLeftOf="parent"
            android:visibility="@{showBottomButton ? View.VISIBLE : View.GONE}"/>

        <ProgressBar
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:visibility="@{viewModel.loading ? View.VISIBLE : View.GONE}"
            tools:layout_constraintTop_creator="1"
            tools:layout_constraintRight_creator="1"
            tools:layout_constraintBottom_creator="1"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintRight_toRightOf="@+id/textView"
            tools:layout_constraintLeft_creator="1"
            app:layout_constraintTop_toTopOf="parent"
            app:layout_constraintLeft_toRightOf="@+id/textView"
            android:id="@+id/progressBar2" />

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@{noDataText}"
            android:visibility="@{viewModel.noDataFound ? View.VISIBLE : View.GONE}"
            android:id="@+id/textView"
            tools:layout_constraintTop_creator="1"
            tools:layout_constraintRight_creator="1"
            app:layout_constraintRight_toRightOf="parent"
            android:layout_marginTop="16dp"
            tools:layout_constraintLeft_creator="1"
            app:layout_constraintLeft_toLeftOf="parent"
            app:layout_constraintTop_toTopOf="@+id/progressBar2" />

        <android.support.v7.widget.RecyclerView
            android:id="@+id/list"
            android:layout_width="0dp"
            android:layout_height="0dp"
            android:layout_marginBottom="8dp"
            android:visibility="@{viewModel.loaded ? View.VISIBLE : View.GONE}"
            app:layout_constraintBottom_toTopOf="@+id/bottomButton"
            app:layout_constraintHorizontal_bias="1.0"
            app:layout_constraintLeft_toLeftOf="parent"
            app:layout_constraintRight_toRightOf="parent"
            app:layout_constraintTop_toTopOf="parent"
            app:layout_constraintVertical_bias="0.0" />

        <include
            layout="@layout/error_layout"
            android:layout_width="0dp"
            android:layout_height="0dp"
            android:layout_marginRight="8dp"
            app:layout_constraintRight_toRightOf="parent"
            android:layout_marginLeft="8dp"
            app:layout_constraintLeft_toLeftOf="parent"
            app:layout_constraintBottom_toBottomOf="parent"
            android:layout_marginBottom="8dp"
            app:layout_constraintTop_toTopOf="parent"
            android:layout_marginTop="8dp"
            app:isVisible="@{viewModel.hasErrors}"/>


    </android.support.constraint.ConstraintLayout>

</layout>

JAVA

@FragmentWithArgs
public class BaseListFragment extends BaseFragment {

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        inflateBinding(inflater, container);
        init(binding);
        return binding.getRoot();
    }

    protected int getLayoutId() {
        return R.layout.fragment_base_list;
    }

    protected void inflateBinding(LayoutInflater inflater, ViewGroup container) {
        binding = DataBindingUtil.inflate(inflater, getLayoutId(), container, false);
        onAttachToBinding(binding);
    }

    @Override
    protected void init(final FragmentBaseListBinding binding) {
        selectedItemPosition = -1;
        restoreState();
        initBinding(binding);
        refresh();
    }

    private void initBinding(FragmentBaseListBinding binding) {
        binding.setViewModel(new ContentLoaderViewModel());
        binding.setNoDataText(noDataText);
        binding.setBottomButtonText(bottomButtonText);
        binding.setShowBottomButton(showBottomButton);
    }

    public void refresh() {
        binding.getViewModel().setDisplayedLayout(ContentLoaderViewModel.LOADING_LAYOUT);
        Observable<RecyclerView.Adapter> observableAdapter = Observable.fromCallable(new Callable<RecyclerView.Adapter>() {
            @Override
            public RecyclerView.Adapter call() throws Exception {
                return hostAction.getAdapterAsync();
            }
        });

        Observer<RecyclerView.Adapter> observerAdapter = new Observer<RecyclerView.Adapter>() {
            @Override
            public void onCompleted() {

            }

            @Override
            public void onError(Throwable e) {
                e.printStackTrace();
                binding.getViewModel().setDisplayedLayout(ContentLoaderViewModel.ERROR_LAYOUT);
            }

            @Override
            public void onNext(RecyclerView.Adapter adapter) {
                initializeList(adapter);
            }
        };

        adapterSubscription = observableAdapter
                .subscribeOn((Schedulers.newThread()))
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(observerAdapter);
    }

    protected void initializeList(RecyclerView.Adapter adapter) {
        this.adapter = adapter;

        binding.list.setLayoutManager(hostAction.getLayoutManager());
        binding.list.setAdapter(getFinalAdapter());
        //Select first item by default
        if(selectedItemPosition >= 0 && selectedItemPosition < adapter.getItemCount())
            onItemClick(binding.list, null, selectedItemPosition, 0, null);

        //Set item decoration (simple line under each items)
        if(!hideDecorationItem)
            binding.list.addItemDecoration(new SimpleItemDecoration(getContext(), 0));

        binding.getViewModel().setDisplayedLayout(adapter.getItemCount() > 0
                ? ContentLoaderViewModel.CONTENT_LAYOUT
                : ContentLoaderViewModel.NO_DATA_LAYOUT
        );

        Log.d(TAG, "initializeList: " + "viewModel.displayLayout[" + binding.getViewModel().getDisplayedLayout() + "]," +
                " recyclerView.visibility[" + binding.list.getVisibility() + "], adapter.count[" + adapter.getItemCount() + "]");

        binding.executePendingBindings();
        binding.invalidateAll();

        Log.d(TAG, "initializeList: (after pendingBinding)" + "viewModel.displayLayout[" + binding.getViewModel().getDisplayedLayout() + "]," +
                " recyclerView.visibility[" + binding.list.getVisibility() + "], adapter.count[" + adapter.getItemCount() + "]");
    }

    protected RecyclerView.Adapter getFinalAdapter() {
        return getWrapAdapter(adapter);
    }

    @NonNull
    private WrapAdapter getWrapAdapter(RecyclerView.Adapter adapter) {
        WrapAdapter wrapAdapter = new WrapAdapter(adapter);
        wrapAdapter.setOnItemClickListener(binding.list, BaseListFragment.this);
        return wrapAdapter;
    }
}

我很确定这是数据绑定的原因,因为

  1. initializeList()中的2个日志显示适配器包含项目:
  2.   

    D / BaseListFragment:initializeList:viewModel.displayLayout [1],recyclerView.visibility [8],adapter.count [27]

         

    D / BaseListFragment:initializeList :(在pendingBinding之后)viewModel.displayLayout [1],recyclerView.visibility [0],adapter.count [27]

    (这里很奇怪的是,在执行挂起绑定并使所有操作无效后,可见性设置为0 - &gt; View.VISIBLERecyclerView仍然没有显示)

    1. 当我从android:visibility="@{viewModel.loaded ? View.VISIBLE : View.GONE}"删除行RecyclerView时,他们没有问题,RecyclerView始终可见。
    2. 知道这里发生了什么事吗?

      更新

      WrapAdapter是一个来自外部库的类:https://github.com/eyeem/RecyclerViewTools/blob/master/library/src/main/java/com/eyeem/recyclerviewtools/adapter/WrapAdapter.java

1 个答案:

答案 0 :(得分:-2)

将可见性从GONE更改为INVISIBLE可以帮我解决问题。 我刚刚将android:visibility="@{viewModel.loaded ? View.VISIBLE : View.GONE}"更改为android:visibility="@{viewModel.loaded ? View.VISIBLE : View.INVISIBLE}"

我不需要RecyclerViewGONE所以现在还可以,但我仍然想知道问题是什么。

如果有人了解GONE无效但INVISIBLE无效的原因,请发表评论。