代码之家  ›  专栏  ›  技术社区  ›  Dr.jacky Mateusz Kaflowski

Android:RecyclerView的项目不会通过数据绑定和领域刷新

  •  2
  • Dr.jacky Mateusz Kaflowski  · 技术社区  · 7 年前

    脚本:
    我有一个 HomePageProductAdapter 它在其他适配器中使用( HomePageProductSliderAdapter & HomePageHotDealsSliderAdapter ).
    主页上有两个RecyclerView,分别用于常规产品和热门产品;这两个列表中都有一些相同的产品。例如,在第一个列表中,有 product1 , product2 , product3 product4 ; 在第二个列表中 产品1 product5 .

    问题:
    当前显示在屏幕上的项目不会在其他列表中刷新。例如,当我增加 产品1 在第一个列表中,它不会刷新第二个列表。

    细节:

    产品 (用于 主页产品适配器 HomeProductSliderModel ):

    public class Product extends RealmObject implements Observable/*BaseObservable*/ {
    @PrimaryKey
    public String productId;
    private String quantity = "0";
    @Ignore
    private transient PropertyChangeRegistry mCallbacks;
    
    //Constructor, getters and setters
    
    @Bindable
    public String getQuantity() {
        return quantity;
    }
    
    public void setQuantity(String quantity) {
        if (quantity != null && !quantity.equals("")) {
            this.quantity = quantity;
            notifyPropertyChanged(BR.quantity);
        }
    }
    
    public synchronized void notifyChange() {
        if (mCallbacks != null) {
            mCallbacks.notifyCallbacks(this, 0, null);
        }
    }
    
    public void notifyPropertyChanged(int fieldId) {
        if (mCallbacks != null) {
            mCallbacks.notifyCallbacks(this, fieldId, null);
        }
    }
    
    @Override
    public void addOnPropertyChangedCallback(OnPropertyChangedCallback onPropertyChangedCallback) {
        if (mCallbacks == null) {
            mCallbacks = new PropertyChangeRegistry();
        }
        mCallbacks.add(onPropertyChangedCallback);
    }
    
    @Override
    public void removeOnPropertyChangedCallback(OnPropertyChangedCallback onPropertyChangedCallback) {
        if (mCallbacks != null) {
            mCallbacks.remove(onPropertyChangedCallback);
        }
    }
    }
    

    主页产品适配器 (用于 主页产品SliderAdapter 主页HotdealsSlideRadapter ):

    public class HomePageProductAdapter extends RecyclerView.Adapter<HomePageProductAdapter.Viewholder> {
    private List<Product> productsList = new ArrayList<>();
    
    public HomePageProductAdapter(List<Product> product) {
        productsList = product;
        Realm realm = null;
        realm = mRealmManager.getLocalInstance();
        for(final Product prd: productsList)
            realm.executeTransaction(new Realm.Transaction() {
                @Override
                public void execute(@NonNull Realm realm) {
                    Product productRealm = realm.where(Product.class).equalTo(ProductFields.PRODUCT_ID, prd.getProductId()).findFirst();
                    if (productRealm == null) {
                        realm.insert(prd);
                    }
                }
            });
    }
    
    @Override
    public Viewholder onCreateViewHolder(ViewGroup parent, int viewType) {
        HomeProductItemBinding item = DataBindingUtil.inflate(LayoutInflater.from(parent.getContext()), R.layout.home_product_item, parent, false);
        return new Viewholder(item);
    }
    
    @Override
    public void onBindViewHolder(final Viewholder holder, final int position) {
        final Product productIns = productsList.get(holder.getAdapterPosition());
        Realm realm = null;
        realm = mRealmManager.getLocalInstance();
        realm.executeTransaction(new Realm.Transaction() {
            @Override
            public void execute(@NonNull Realm realm) {
                Product productRealm = realm.where(Product.class).equalTo(ProductFields.PRODUCT_ID, productIns.getProductId()).findFirst();
    
                if (productRealm == null) {
                    productRealm = realm.createObject(Product.class, productIns.getProductId());
                }
    
                if (productRealm != null) {
                    if (productRealm.getQuantity() == null || productRealm.getQuantity().equalsIgnoreCase("0")) {
                        holder.HomeProductBindGrid.strCount.setText("0"); //And I'm wondering why should I use set value manually, when there is holder.HomeProductBindGrid.setProd
                        productRealm.setQuantity("0");
                    }
                    else {
                        holder.HomeProductBindGrid.strCount.setText(String.valueOf(productRealm.getQuantity()));
                    }
                }
    
                holder.HomeProductBindGrid.setProd(productRealm); // I'm using this line and android:text="@{prod.quantity}" in the XML, why should I set the values manually?!
                holder.HomeProductBindGrid.executePendingBindings();
            }
        });
    
        holder.HomeProductBindGrid.inc_CountButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                final Product productIns = productsList.get(holder.getAdapterPosition()); //Which one should I use?! -> //holder.HomeProductBindGrid.getProd(); //productsList.get(position); //productsList.get(holder.getAdapterPosition());
                Realm realm = null;
                realm = mRealmManager.getLocalInstance();
                realm.executeTransaction(new Realm.Transaction() {
                    @Override
                    public void execute(@NonNull Realm realm) {
                        Product productRealm = realm.where(Product.class).equalTo(ProductFields.PRODUCT_ID, productIns.getProductId()).findFirst();
    
                        if (productRealm == null) {
                            productRealm = realm.createObject(Product.class, productIns.getProductId());
                        }
    
                        if (productRealm != null) {
                            if (productRealm.getQuantity() == null || productRealm.getQuantity().equalsIgnoreCase("0")) {
                                productRealm.setQuantity("1");
                                holder.HomeProductBindGrid.strCount.setText(String.valueOf(productRealm.getQuantity()));
                            }
                            else {
                                productRealm.setQuantity(String.valueOf(Integer.valueOf(productRealm.getQuantity()) + 1));
                                holder.HomeProductBindGrid.strCount.setText(String.valueOf(productRealm.getQuantity()));
                            }
    
                            holder.HomeProductBindGrid.setProd(productRealm);
                            holder.HomeProductBindGrid.executePendingBindings();
                        }
                    }
                });
            }
        });
    }
    
    class Viewholder extends RecyclerView.ViewHolder {
        HomeProductItemBinding HomeProductBindGrid;
        ItemListBinding ItemListBindList;
    
        Viewholder(HomeProductItemBinding binding) {
            super(binding.getRoot());
            this.HomeProductBindGrid = binding;
        }
    }
    }
    

    HomeProductSliderModel (用于 主页产品SliderAdapter 主页HotdealsSlideRadapter ):

    public class HomeProductSliderModel extends BaseObservable{
    //I extended this model from BaseObservable, in the hope that the other lists refresh, but didn't make any different.
    private List<Product> productList;
    
    public HomeProductSliderModel(List<Product> productList) {
        this.productList = productList;
    }
    
    @NonNull
    @Bindable
    public List<Product> getProductList() {
        return productList;
    }
    
    public void setProductList(List<Product> productList) {
    //I don't know where should I use it (Like setQuantity in HomePageProductAdapter?!
        this.productList = productList;
        //notifyChange();
        notifyPropertyChanged(BR.productList);
    }
    
    @Override
    public void addOnPropertyChangedCallback(OnPropertyChangedCallback callback) {
        super.addOnPropertyChangedCallback(callback);
    }
    
    @Override
    public void removeOnPropertyChangedCallback(OnPropertyChangedCallback callback) {
        super.removeOnPropertyChangedCallback(callback);
    }
    
    @Override
    public void notifyChange() {
        super.notifyChange();
    }
    
    @Override
    public void notifyPropertyChanged(int fieldId) {
        super.notifyPropertyChanged(fieldId);
    }
    }
    

    主页产品SliderAdapter (用于 MainActivity ):

    public class HomePageProductSliderAdapter extends RecyclerView.Adapter<HomePageProductSliderAdapter.Holder> {
    private List<HomeProductSliderModel> mHomeProductSliderModels;
    
    public HomePageProductSliderAdapter(List<HomeProductSliderModel> homeProductSliderModels, int screenWidth) {
        mHomeProductSliderModels = homeProductSliderModels;
    }
    
    @Override
    public Holder onCreateViewHolder(ViewGroup parent, int viewType) {
        ProductSliderBinding bind = DataBindingUtil.inflate(LayoutInflater.from(parent.getContext()),
                R.layout.product_slider, parent, false);
    
        return new Holder(bind);
    }
    
    @Override
    public void onBindViewHolder(Holder holder, int position) {
        HomeProductSliderModel homeProductSliderModel = mHomeProductSliderModels.get(holder.getAdapterPosition());
        holder.bindHomeProduct(homeProductSliderModel);
    }
    
    @Override
    public int getItemCount() {
        return mHomeProductSliderModels.size();
    }
    
    class Holder extends RecyclerView.ViewHolder {
        ProductSliderBinding productSliderBind;
    
        Holder(ProductSliderBinding binding) {
            super(binding.getRoot());
            productSliderBind = binding;
        }
    
        public void bindHomeProduct(@NonNull HomeProductSliderModel homeProductSlider) {
            productSliderBind.setHomeProductSlider(homeProductSlider);
            productSliderBind.executePendingBindings();
            productSliderBind.productRecycler.setAdapter(new HomePageProductAdapter(/*mHomeProductSliderModels.get(position)*/homeProductSlider.getProductList()));
            productSliderBind.executePendingBindings();
        }
    }
    }
    

    主页HotdealsSlideRadapter (用于 主要活动 ):

    public class HomePageHotDealsSliderAdapter extends RecyclerView.Adapter<HomePageHotDealsSliderAdapter.Holder> {
    private List<HomeProductSliderModel> mHomeProductSliderModels;
    
    public HomePageHotDealsSliderAdapter(List<HomeProductSliderModel> homeProductSliderModels) {
        mHomeProductSliderModels = homeProductSliderModels;
    }
    
    @Override
    public Holder onCreateViewHolder(ViewGroup parent, int viewType) {
        HomeHotDealsItemBinding bind = DataBindingUtil.inflate(LayoutInflater.from(parent.getContext()),
                R.layout.home_hot_deals_item, parent, false);
    
        return new Holder(bind);
    }
    
    @Override
    public void onBindViewHolder(Holder holder, int position) {
        HomeProductSliderModel homeProductSliderModel = mHomeProductSliderModels.get(holder.getAdapterPosition());
        holder.bindHomeProduct(homeProductSliderModel);
    }
    
    @Override
    public int getItemCount() {
        return mHomeProductSliderModels.size();
    }
    
    class Holder extends RecyclerView.ViewHolder {
        HomeHotDealsItemBinding homeHotDealsItemBind;
    
        Holder(HomeHotDealsItemBinding binding) {
            super(binding.getRoot());
            homeHotDealsItemBind = binding;
        }
    
        public void bindHomeProduct(@NonNull HomeProductSliderModel homeProductSlider) {
            homeHotDealsItemBind.setHomeHotProductSlider(homeProductSlider);
            homeHotDealsItemBind.executePendingBindings();
            homeHotDealsItemBind.recyclerView.setAdapter(new HomePageProductAdapter(mContext, /*mHomeProductSliderModels.get(position)*/homeProductSlider.getProductList(), screenWidth, true));
            homeHotDealsItemBind.executePendingBindings();
        }
    }
    }
    

    home\u product\u项目。xml (用于 主页产品适配器 ):

    <layout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:apps="http://schemas.android.com/apk/res-auto"
    xmlns:card_view="http://schemas.android.com/tools">
    <data>
        <import type="android.view.View" />
        <variable
            name="prod"
            type="com.see.core_app.Model.Product" />
    </data>
    ...
    <TextView
        android:id="@+id/str_count"
        android:text="@{prod.quantity}"/>
    
    <Button
        android:id="@+id/inc_CountButton"/>
    ...
    </layout>
    

    product\u滑块。xml (用于 主页产品SliderAdapter ):

    <?xml version="1.0" encoding="utf-8"?>
    <layout xmlns:android="http://schemas.android.com/apk/res/android">
    <data>
        <import type="android.view.View" />
        <variable
            name="homeProductSlider"
            type="com.see.core_app.Model.HomeProductSliderModel" />
    </data>
    ...
    
        <android.support.v7.widget.RecyclerView
            android:id="@+id/product_recycler"/>
    ...
    </layout>
    

    home\u hot\u deals\u项目。xml (用于 主页HotdealsSlideRadapter ):

    <?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>
        <import type="android.view.View" />
        <variable
            name="homeHotProductSlider"
            type="com.see.core_app.Model.HomeProductSliderModel" />
    </data>
    ...
        <android.support.v7.widget.RecyclerView
            android:id="@+id/recycler_view"/>
    ...
    </layout>
    

    使现代化 关于@EpicPandaForce的评论:

    我将所有本地产品变量更改为 全局变量如下:

    private Product productRealm;
    

    和已更改 where 查询时间 onBindViewHolder , inc_CountButton dec_CountButton 范围如下:

    productRealm = realm.where(Product.class).equalTo(ProductFields.PRODUCT_ID, productIns.getProductId()).findFirst();
    

    并写道 RealmChangeListener 在的底部 onBindViewHolder 范围:

    @Override
    public void onBindViewHolder(final Viewholder holder, final int position) {
    productRealm = realm.where ...
        realm.executeTransaction(new Realm.Transaction() {
    ...
    });
            productRealm.addChangeListener(new RealmChangeListener<RealmModel>() {
            @Override
            public void onChange(RealmModel realmModel) {
                Product localProductRealm = (Product) realmModel;
                if (localProductRealm.getQuantity() == null || localProductRealm.getQuantity().equalsIgnoreCase("0")) {
                    holder.HomeProductBindGrid.strCount.setText("0");
                } else {
                    holder.HomeProductBindGrid.strCount.setText(String.valueOf(productRealm.getQuantity()));
                }
    
                ((Product) realmModel).notifyChange();
            }
        });
    }
    

    但有时 notifyChange 不要打电话!

    1 回复  |  直到 7 年前
        1
  •  0
  •   Dr.jacky Mateusz Kaflowski    7 年前

    如果不使用 RealmRecyclerViewAdapter .


    解决方案:

    主页产品适配器 :

    public HomePageProductAdapter(@Nullable OrderedRealmCollection<Product> data, boolean autoUpdate, boolean updateOnModification) {
        super(data, autoUpdate, updateOnModification);
        setHasStableIds(true);
    }
    
    • 添加 implementation 'io.realm:android-adapters:2.1.1' 应用程序的 build.gradle
    • 全部删除 realm.where 查询 但是 onBindViewHolder
    • 全部删除 holder.HomeProductBindGrid.setProd(productRealm); 绑定 但是 onBindViewHolder
    • 改变 HomeProductSliderModel 构造函数到
      public HomeProductSliderModel(String title, OrderedRealmCollection<Product> productList) {

    附笔 :
    唯一剩下的问题是,该项目在每次通知更改时会闪烁两次。