2017-06-13 43 views
0

我有一个ViewModel,旨在控制ActivityFragment的状态。该视图模型包含4个ObservableBoolean被我的布局里面用来告诉视图必须可见或不可见:Android ObservableBoolean每次都不触发View.setVisibility

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被初始化。所以起初Fragment处于“加载”状态,当它接收到它的适配器时,它变成“内容”状态。下面是它的样子:

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. 的2日志initializeList()显示t帽适配器具有项目:

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 - >View.VISIBLERecyclerView仍然不适用翼起来)

  • 当我从RecyclerView删除行android:visibility="@{viewModel.loaded ? View.VISIBLE : View.GONE}"它们是没有问题和RecyclerView始终可见。
  • 想知道这里发生了什么吗?

    UPDATE

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

    +0

    我试图保持参照视图模型,而不是结合,但它似乎并没有改变任何东西。我把'initBinding'的第一行改成了'binding.setViewModel(viewModel = new ContentLoaderViewModel());'然后通过'viewModel'改变了'bindingView.getViewModel()'的位置,而'viewModel'现在是我'fragment'的一个私有变量。这是你建议的吗? 另外,你可以在我的更新中看到WrapAdapter来自哪里。你能告诉我为什么最好保持引用“ViewModel”而不是DataBinding对象(好奇心)的原因是什么? – MHogge

    回答

    -1

    更改的知名度。 我刚刚将android:visibility="@{viewModel.loaded ? View.VISIBLE : View.GONE}"更改为android:visibility="@{viewModel.loaded ? View.VISIBLE : View.INVISIBLE}"

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

    如果有人明白为什么GONE没有工作,但INVISIBLE是什么原因,请让评论。

    相关问题