如何使用数据绑定在MVVM中设置适配器

时间:2018-03-31 13:38:54

标签: android mvvm android-recyclerview android-adapter android-mvvm

我是MVVM和DataBinding的新手。我已经设置了ViewHolder和Adapter。现在我不知道如何在活动中设置适配器。我应该使用另一个ViewModel来填充对象的arraylist吗?如果是,那该怎么办?

我实施的代码如下:

ScoresActivity:

 public class ScoresActivity extends AppCompatActivity implements Observer{

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        initBinding();
    }

    private void initBinding() {
        ActivityScoresBinding activityScoresBinding = DataBindingUtil.setContentView(this, R.layout.activity_scores);
        ScoreViewModel scoreViewModel=new ScoreViewModel();
        activityScoresBinding.setScoreModel(scoreViewModel);
        scoreViewModel.addObserver(this);
    }

    @Override
    public void update(Observable observable, Object data) {

    }
}

ScoreViewModel

    public class ScoreViewModel extends Observable{

    private List<User> userList;

    public ScoreViewModel(){
        userList=new ArrayList<>();
        fillData();
    }

    private void fillData() {
        //fills data in user list
         ...
    }
}

1 个答案:

答案 0 :(得分:2)

从Google示例中快速浏览

public class ProductListViewModel extends AndroidViewModel {

private final DataRepository mRepository;

// MediatorLiveData can observe other LiveData objects and react on their emissions.
private final MediatorLiveData<List<ProductEntity>> mObservableProducts;

public ProductListViewModel(Application application) {
    super(application);

    mObservableProducts = new MediatorLiveData<>();
    // set by default null, until we get data from the database.
    mObservableProducts.setValue(null);

    mRepository = ((BasicApp) application).getRepository();
    LiveData<List<ProductEntity>> products = mRepository.getProducts();

    // observe the changes of the products from the database and forward them
    mObservableProducts.addSource(products, mObservableProducts::setValue);
}

/**
 * Expose the LiveData Products query so the UI can observe it.
 */
public LiveData<List<ProductEntity>> getProducts() {
    return mObservableProducts;
}

public LiveData<List<ProductEntity>> searchProducts(String query) {
    return mRepository.searchProducts(query);
}

}

片段

public class ProductListFragment extends Fragment {

public static final String TAG = "ProductListViewModel";

private ProductAdapter mProductAdapter;

private ListFragmentBinding mBinding;

@Nullable
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container,
        @Nullable Bundle savedInstanceState) {
    mBinding = DataBindingUtil.inflate(inflater, R.layout.list_fragment, container, false);

    mProductAdapter = new ProductAdapter(mProductClickCallback);
    mBinding.productsList.setAdapter(mProductAdapter);

    return mBinding.getRoot();
}

@Override
public void onActivityCreated(@Nullable Bundle savedInstanceState) {
    super.onActivityCreated(savedInstanceState);
    final ProductListViewModel viewModel =
            ViewModelProviders.of(this).get(ProductListViewModel.class);

    mBinding.productsSearchBtn.setOnClickListener(new OnClickListener() {
        @Override
        public void onClick(View v) {
            Editable query = mBinding.productsSearchBox.getText();
            if (query == null || query.toString().isEmpty()) {
                subscribeUi(viewModel.getProducts());
            } else {
                subscribeUi(viewModel.searchProducts("*" + query + "*"));
            }
        }
    });

    subscribeUi(viewModel.getProducts());
}

private void subscribeUi(LiveData<List<ProductEntity>> liveData) {
    // Update the list when the data changes
    liveData.observe(this, new Observer<List<ProductEntity>>() {
        @Override
        public void onChanged(@Nullable List<ProductEntity> myProducts) {
            if (myProducts != null) {
                mBinding.setIsLoading(false);
                mProductAdapter.setProductList(myProducts);
            } else {
                mBinding.setIsLoading(true);
            }
            // espresso does not know how to wait for data binding's loop so we execute changes
            // sync.
            mBinding.executePendingBindings();
        }
    });
}

private final ProductClickCallback mProductClickCallback = new ProductClickCallback() {
    @Override
    public void onClick(Product product) {

        if (getLifecycle().getCurrentState().isAtLeast(Lifecycle.State.STARTED)) {
            ((MainActivity) getActivity()).show(product);
        }
    }
};
}

布局

<layout xmlns:tools="http://schemas.android.com/tools"
xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto">

<data>
    <variable
        name="isLoading"
        type="boolean" />
</data>

<LinearLayout
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@color/cardview_light_background"
    android:orientation="vertical">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginStart="@dimen/item_horizontal_margin"
        android:layout_marginEnd="@dimen/item_horizontal_margin"
        android:orientation="horizontal">

        <androidx.appcompat.widget.AppCompatEditText
            android:id="@+id/products_search_box"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:hint="@string/search_products_hint"/>

        <ImageButton
            android:id="@+id/products_search_btn"
            android:layout_width="48dp"
            android:layout_height="48dp"
            android:contentDescription="@string/cd_search_products"
            app:srcCompat="@drawable/ic_search_black_24dp"/>

    </LinearLayout>

    <TextView
        android:id="@+id/loading_tv"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:gravity="center_vertical|center_horizontal"
        android:text="@string/loading_products"
        android:textAlignment="center"
        app:visibleGone="@{isLoading}"/>

    <androidx.recyclerview.widget.RecyclerView
        android:id="@+id/products_list"
        android:contentDescription="@string/cd_products_list"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        app:layoutManager="LinearLayoutManager"
        app:visibleGone="@{!isLoading}"/>

</LinearLayout>
</layout>

适配器

public class ProductAdapter extends RecyclerView.Adapter<ProductAdapter.ProductViewHolder> {

List<? extends Product> mProductList;

@Nullable
private final ProductClickCallback mProductClickCallback;

public ProductAdapter(@Nullable ProductClickCallback clickCallback) {
    mProductClickCallback = clickCallback;
    setHasStableIds(true);
}

public void setProductList(final List<? extends Product> productList) {
    if (mProductList == null) {
        mProductList = productList;
        notifyItemRangeInserted(0, productList.size());
    } else {
        DiffUtil.DiffResult result = DiffUtil.calculateDiff(new DiffUtil.Callback() {
            @Override
            public int getOldListSize() {
                return mProductList.size();
            }

            @Override
            public int getNewListSize() {
                return productList.size();
            }

            @Override
            public boolean areItemsTheSame(int oldItemPosition, int newItemPosition) {
                return mProductList.get(oldItemPosition).getId() ==
                        productList.get(newItemPosition).getId();
            }

            @Override
            public boolean areContentsTheSame(int oldItemPosition, int newItemPosition) {
                Product newProduct = productList.get(newItemPosition);
                Product oldProduct = mProductList.get(oldItemPosition);
                return newProduct.getId() == oldProduct.getId()
                        && Objects.equals(newProduct.getDescription(), oldProduct.getDescription())
                        && Objects.equals(newProduct.getName(), oldProduct.getName())
                        && newProduct.getPrice() == oldProduct.getPrice();
            }
        });
        mProductList = productList;
        result.dispatchUpdatesTo(this);
    }
}

@Override
public ProductViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
    ProductItemBinding binding = DataBindingUtil
            .inflate(LayoutInflater.from(parent.getContext()), R.layout.product_item,
                    parent, false);
    binding.setCallback(mProductClickCallback);
    return new ProductViewHolder(binding);
}

@Override
public void onBindViewHolder(ProductViewHolder holder, int position) {
    holder.binding.setProduct(mProductList.get(position));
    holder.binding.executePendingBindings();
}

@Override
public int getItemCount() {
    return mProductList == null ? 0 : mProductList.size();
}

@Override
public long getItemId(int position) {
    return mProductList.get(position).getId();
}

static class ProductViewHolder extends RecyclerView.ViewHolder {

    final ProductItemBinding binding;

    public ProductViewHolder(ProductItemBinding binding) {
        super(binding.getRoot());
        this.binding = binding;
    }
}
}

适配器项布局

<layout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto">
<data>
    <variable name="product"
              type="com.example.android.persistence.model.Product"/>
    <variable name="callback"
              type="com.example.android.persistence.ui.ProductClickCallback"/>
</data>

<androidx.cardview.widget.CardView
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:minHeight="@dimen/product_item_min_height"
    android:onClick="@{() ->  callback.onClick(product)}"
    android:orientation="horizontal"
    android:layout_marginStart="@dimen/item_horizontal_margin"
    android:layout_marginEnd="@dimen/item_horizontal_margin"
    app:cardUseCompatPadding="true">

    <RelativeLayout
        android:layout_marginStart="@dimen/item_horizontal_margin"
        android:layout_marginEnd="@dimen/item_horizontal_margin"
        android:layout_width="match_parent"
        android:layout_height="wrap_content">

        <TextView
            android:id="@+id/name"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:contentDescription="@string/cd_product_name"
            android:text="@{product.name}"/>

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_alignParentEnd="true"
            android:layout_marginEnd="5dp"
            android:text="@{@string/product_price(product.price)}"/>

        <TextView
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_below="@id/name"
            android:text="@{product.description}"/>
    </RelativeLayout>

</androidx.cardview.widget.CardView>
</layout>