草庐IT

android - RxJava2 去抖功能在 RecyclerView 中无法正常工作 - Android

coder 2023-11-26 原文

我正在尝试创建一个自定义 ImageButton,它将累积点击次数并在用户停止点击该按钮 1 秒时触发一个事件。

我使用了 debounce 函数来完成这个。

自定义图像按钮:

public class MBImageButton extends ImageButton {

    private AtomicInteger mCounter;
    private Disposable mDisposable;
    private Observable<Object> observable;
    private OnAccumulatedRequestsRead mOnAccumulatedRequestsRead;
    private OnEverClickListener mOnEverClickListener;
    private int emitEveryMilli = 1000; // every 1 second by default
    private boolean shouldDisposeOnDetachFromWindow = true;

    public MBImageButton(Context context) {
        super(context);
        init();
    }

    public MBImageButton(Context context, AttributeSet attrs) {
        super(context, attrs);
        init();
    }

    public MBImageButton(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init();
    }

    public void setAccumulatedClickListeners(OnEverClickListener onEverClickListener,
                                             OnAccumulatedRequestsRead onAccumulatedRequestsRead) {
        setOnAccumulatedRequestsRead(onAccumulatedRequestsRead);
        setOnEverClickListener(onEverClickListener);
        initClickObservable();
        subscribe();
    }

    private void initClickObservable() {
        observable = Observable.create(emitter -> {
            emitter.setCancellable(() -> setOnClickListener(null));
            try {
                setOnClickListener(view -> {
                    try {
                        final int currentCount = mCounter.incrementAndGet();
                        Timber.d("Clicked: " + currentCount);
                        if (mOnEverClickListener != null) {
                            mOnEverClickListener.onEveryClickListener(currentCount);
                        }
                        emitter.onNext(new Object());
                    } catch (Exception e) {
                        emitter.onError(e);
                    }
                });
            } catch (Exception e) {
                emitter.onError(e);
            }
        }).doOnSubscribe(disposable -> mDisposable = disposable)
                               .debounce(emitEveryMilli, TimeUnit.MILLISECONDS);
    }

    private void subscribe() {
        observable.subscribeOn(Schedulers.newThread()).observeOn(AndroidSchedulers.mainThread())
                .subscribe(o -> {
                    try {
                        final int count = mCounter.get();
                        if(count==0) return;
                        mCounter.set(0);
                        Timber.d("Accumulated Clicks: " + count);
                        if (mOnAccumulatedRequestsRead != null) {
                            mOnAccumulatedRequestsRead.onAccumulatedRequestsReady(count);
                        }
                    } catch (Exception e) {
                        Timber.e(e);
                    }
                }, Timber::e);
    }

    private void init() {
        mCounter = new AtomicInteger(0);
    }

    public void disposeAccumulatedClickListeners() {
        if (mDisposable != null) {
            mDisposable.dispose();
        }
    }

    public void shouldDisposeOnDetachFromWindow(boolean shouldDisposeOnDetachFromWindow) {
        this.shouldDisposeOnDetachFromWindow = shouldDisposeOnDetachFromWindow;
    }

    public void setEmitEveryMilliseconds(int emitEveryMilli) {
        this.emitEveryMilli = emitEveryMilli;
        initClickObservable();
        subscribe();
    }

    private void setOnEverClickListener(OnEverClickListener onEverClickListener) {
        mOnEverClickListener = onEverClickListener;
    }

    private void setOnAccumulatedRequestsRead(OnAccumulatedRequestsRead onAccumulatedRequestsRead) {
        mOnAccumulatedRequestsRead = onAccumulatedRequestsRead;
    }

    @Override
    protected void onDetachedFromWindow() {
        super.onDetachedFromWindow();
        if (shouldDisposeOnDetachFromWindow) {
            if (mDisposable != null) {
                mDisposable.dispose();
            }
        }
    }

    public interface OnAccumulatedRequestsRead {
        void onAccumulatedRequestsReady(int count);
    }

    public interface OnEverClickListener {
        void onEveryClickListener(int currentCount);
    }
}

为去抖动设置此 imageButton 的函数是:

setAccumulatedClickListeners(OnEverClickListener, OnAccumulatedRequestsRead)

当我从 RecyclerView 中设置这个 View 时,一切正常,

当我连续点击 9 次时,结果如下所示:

D/MBImageButton: Clicked: 1
D/MBImageButton: Clicked: 2
D/MBImageButton: Clicked: 3
D/MBImageButton: Clicked: 4
D/MBImageButton: Clicked: 5
D/MBImageButton: Clicked: 6
D/MBImageButton: Clicked: 7
D/MBImageButton: Clicked: 8
D/MBImageButton: Clicked: 9
D/MBImageButton: Accumulated Clicks: 9

当我将此自定义 ImageButton 添加到 RecyclerView ViewHolder 时,相同点击次数的结果不正确:

D/MBImageButton: Clicked: 1
D/MBImageButton: Clicked: 2
D/MBImageButton: Clicked: 1
D/MBImageButton: Clicked: 3
D/MBImageButton: Clicked: 2
D/MBImageButton: Clicked: 4
D/MBImageButton: Accumulated Clicks: 4
D/MBImageButton: Clicked: 1
D/MBImageButton: Accumulated Clicks: 2
D/MBImageButton: Clicked: 2
D/MBImageButton: Accumulated Clicks: 2
D/MBImageButton: Accumulated Clicks: 0
D/MBImageButton: Clicked: 1
D/MBImageButton: Accumulated Clicks: 1
D/MBImageButton: Accumulated Clicks: 0
D/MBImageButton: Accumulated Clicks: 0

这是再次连续点击 9 次的另一个结果:

D/MBImageButton: Clicked: 1
D/MBImageButton: Clicked: 2
D/MBImageButton: Clicked: 1
D/MBImageButton: Clicked: 3
D/MBImageButton: Clicked: 2
D/MBImageButton: Clicked: 4
D/MBImageButton: Clicked: 3
D/MBImageButton: Accumulated Clicks: 4
D/MBImageButton: Clicked: 1
D/MBImageButton: Accumulated Clicks: 3
D/MBImageButton: Accumulated Clicks: 1
D/MBImageButton: Accumulated Clicks: 0
D/MBImageButton: Clicked: 1
D/MBImageButton: Accumulated Clicks: 1
D/MBImageButton: Accumulated Clicks: 0
D/MBImageButton: Accumulated Clicks: 0
D/MBImageButton: Accumulated Clicks: 0

两次我都以相同的间隔时间点击,但是当这个按钮添加到 RecyclerView 时它给出了奇怪的结果。

可能是什么问题?

更新:

我的适配器简化了实现:

public class ProductRVAdapter extends BaseProductRVAdapter<ProductRVAdapter.ProductVH> {

    private ProductAdapterListener mProductAdapterListener;

    public ProductRVAdapter(List<Product> productList, ProductAdapterListener productAdapterListener) {
        super(productList);
        mProductAdapterListener = productAdapterListener;
    }

    @Override
    public ProductVH onCreateViewHolder(ViewGroup parent, int viewType) {
        final View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_product, parent, false);
        return new ProductVH(view);
    }

    @Override
    public void onBindViewHolder(ProductVH holder, int position) {
        holder.bind(productList.get(position), position);

        holder.ib_decrease.setAccumulatedClickListeners(currentCount -> onProductAdapterDecreaseClicked.onClick(holder.ib_decrease),
                count -> {
                    if (mProductAdapterListener != null) {
                        mProductAdapterListener.onProductAdapterProductChangeToWSListener(productList.get(position), position, -count);
                    }
                });

        holder.ib_add.setAccumulatedClickListeners(currentCount -> onProductAdapterAddToCartClicked.onClick(holder.ib_add),
                count -> {
                    if (mProductAdapterListener != null) {
                        mProductAdapterListener.onProductAdapterProductChangeToWSListener(productList.get(position), position, count);
                    }
                });
    }

    public interface ProductAdapterListener {

        void onProductAdapterAddToCartClicked(Product product, int position);

        void onProductAdapterProductChangeToWSListener(Product product, int position, int amountChanged);

        void onProductAdapterDecreaseClicked(Product product, int position);
    }

    public static class ProductVH extends RecyclerView.ViewHolder {
        @BindView(R.id.iv_productImage)
        ImageView iv_producImage;
        @BindView(R.id.tv_productName)
        TextView tv_productName;
        @BindView(R.id.tv_prodAmount)
        MBTextView tv_prodAmount;

        @BindView(R.id.ib_decrease)
        MBImageButton ib_decrease;
        @BindView(R.id.ib_add)
        MBImageButton ib_add;

        ProductVH(View itemView) {
            super(itemView);
            ButterKnife.bind(this, itemView);
        }


        public void bind(Product product, int position) {
            tv_productName.setText(product.getName());
            tv_prodAmount.setText(product.getAmount());

            ImageLoader.loadImage(iv_producImage.getContext(), iv_producImage, product.getImg(), R.drawable.ic_category_item);

            ib_decrease.setTag(position);
            ib_add.setTag(position);
        }
    }
}

最佳答案

我找到了解决方案

主要问题是当我在 RecyclerView Adapter 中滚动时,vies 与窗口分离并且函数 onDetachedFromWindow 被调用,在该函数中我 unsubscribe (处置) 来自可观察对象。

尝试过的解决方案:

<强>1。我添加了 attachFunction

@Override
protected void onAttachedToWindow() {
    super.onAttachedToWindow();
    if (shouldDisposeOnDetachFromWindow) {
        if (mDisposable != null) {
            if (mDisposable.isDisposed()) {
                initClickObservable();
                subscribe();
            }
        }
    }
}

<强>2。删除了 notifyItemChanged(position) 调用

通常我会在使用 notifyItemChange() 进行一些修改后更新 View 。现在我直接通过 setText 或类似函数更新 View 。

它现在可以正常工作了。

关于android - RxJava2 去抖功能在 RecyclerView 中无法正常工作 - Android,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/42479456/

有关android - RxJava2 去抖功能在 RecyclerView 中无法正常工作 - Android的更多相关文章

  1. ruby-on-rails - 由于 "wkhtmltopdf",PDFKIT 显然无法正常工作 - 2

    我在从html页面生成PDF时遇到问题。我正在使用PDFkit。在安装它的过程中,我注意到我需要wkhtmltopdf。所以我也安装了它。我做了PDFkit的文档所说的一切......现在我在尝试加载PDF时遇到了这个错误。这里是错误:commandfailed:"/usr/local/bin/wkhtmltopdf""--margin-right""0.75in""--page-size""Letter""--margin-top""0.75in""--margin-bottom""0.75in""--encoding""UTF-8""--margin-left""0.75in""-

  2. ruby-on-rails - 无法使用 Rails 3.2 创建插件? - 2

    我对最新版本的Rails有疑问。我创建了一个新应用程序(railsnewMyProject),但我没有脚本/生成,只有脚本/rails,当我输入ruby./script/railsgeneratepluginmy_plugin"Couldnotfindgeneratorplugin.".你知道如何生成插件模板吗?没有这个命令可以创建插件吗?PS:我正在使用Rails3.2.1和ruby​​1.8.7[universal-darwin11.0] 最佳答案 随着Rails3.2.0的发布,插件生成器已经被移除。查看变更日志here.现在

  3. ruby - 无法运行 Rails 2.x 应用程序 - 2

    我尝试运行2.x应用程序。我使用rvm并为此应用程序设置其他版本的ruby​​:$rvmuseree-1.8.7-head我尝试运行服务器,然后出现很多错误:$script/serverNOTE:Gem.source_indexisdeprecated,useSpecification.Itwillberemovedonorafter2011-11-01.Gem.source_indexcalledfrom/Users/serg/rails_projects_terminal/work_proj/spohelp/config/../vendor/rails/railties/lib/r

  4. ruby-on-rails - 无法在centos上安装therubyracer(V8和GCC出错) - 2

    我正在尝试在我的centos服务器上安装therubyracer,但遇到了麻烦。$geminstalltherubyracerBuildingnativeextensions.Thiscouldtakeawhile...ERROR:Errorinstallingtherubyracer:ERROR:Failedtobuildgemnativeextension./usr/local/rvm/rubies/ruby-1.9.3-p125/bin/rubyextconf.rbcheckingformain()in-lpthread...yescheckingforv8.h...no***e

  5. ruby - 无法让 RSpec 工作—— 'require' : cannot load such file - 2

    我花了三天的时间用头撞墙,试图弄清楚为什么简单的“rake”不能通过我的规范文件。如果您遇到这种情况:任何文件夹路径中都不要有空格!。严重地。事实上,从现在开始,您命名的任何内容都没有空格。这是我的控制台输出:(在/Users/*****/Desktop/LearningRuby/learn_ruby)$rake/Users/*******/Desktop/LearningRuby/learn_ruby/00_hello/hello_spec.rb:116:in`require':cannotloadsuchfile--hello(LoadError) 最佳

  6. ruby - 无法覆盖 irb 中的 to_s - 2

    我在pry中定义了一个函数:to_s,但我无法调用它。这个方法去哪里了,怎么调用?pry(main)>defto_spry(main)*'hello'pry(main)*endpry(main)>to_s=>"main"我的ruby版本是2.1.2看了一些答案和搜索后,我认为我得到了正确的答案:这个方法用在什么地方?在irb或pry中定义方法时,会转到Object.instance_methods[1]pry(main)>defto_s[1]pry(main)*'hello'[1]pry(main)*end=>:to_s[2]pry(main)>defhello[2]pry(main)

  7. ruby - 无法在 60 秒内获得稳定的 Firefox 连接 (127.0.0.1 :7055) - 2

    我使用的是Firefox版本36.0.1和Selenium-Webdrivergem版本2.45.0。我能够创建Firefox实例,但无法使用脚本继续进行进一步的操作无法在60秒内获得稳定的Firefox连接(127.0.0.1:7055)错误。有人能帮帮我吗? 最佳答案 我遇到了同样的问题。降级到firefoxv33后一切正常。您可以找到旧版本here 关于ruby-无法在60秒内获得稳定的Firefox连接(127.0.0.1:7055),我们在StackOverflow上找到一个类

  8. ruby - 安装 Ruby 时遇到问题(无法下载资源 "readline--patch") - 2

    当我尝试安装Ruby时遇到此错误。我试过查看this和this但无济于事➜~brewinstallrubyWarning:YouareusingOSX10.12.Wedonotprovidesupportforthispre-releaseversion.Youmayencounterbuildfailuresorotherbreakages.Pleasecreatepull-requestsinsteadoffilingissues.==>Installingdependenciesforruby:readline,libyaml,makedepend==>Installingrub

  9. ruby-on-rails - 无法让 rspec、spork 和调试器正常运行 - 2

    GivenIamadumbprogrammerandIamusingrspecandIamusingsporkandIwanttodebug...mmm...let'ssaaay,aspecforPhone.那么,我应该把“require'ruby-debug'”行放在哪里,以便在phone_spec.rb的特定点停止处理?(我所要求的只是一个大而粗的箭头,即使是一个有挑战性的程序员也能看到:-3)我已经尝试了很多位置,除非我没有正确测试它们,否则会发生一些奇怪的事情:在spec_helper.rb中的以下位置:require'rubygems'require'spork'

  10. ruby-on-rails - Cucumber 是否只是 rspec 的包装器以帮助将测试组织成功能? - 2

    只是想确保我理解了事情。据我目前收集到的信息,Cucumber只是一个“包装器”,或者是一种通过将事物分类为功能和步骤来组织测试的好方法,其中实际的单元测试处于步骤阶段。它允许您根据事物的工作方式组织您的测试。对吗? 最佳答案 有点。它是一种组织测试的方式,但不仅如此。它的行为就像最初的Rails集成测试一样,但更易于使用。这里最大的好处是您的session在整个Scenario中保持透明。关于Cucumber的另一件事是您(应该)从使用您的代码的浏览器或客户端的角度进行测试。如果您愿意,您可以使用步骤来构建对象和设置状态,但通常您

随机推荐