2017-02-27 12 views
7

Próbuję utworzyć niestandardowy przycisk ImageButton, który będzie kumulował kliknięcia i wywoływał zdarzenie, gdy użytkownik przestanie klikać przycisk przez 1 sekundę.Funkcja odbicia RxJava2 nie działa poprawnie w RecyclerView - Android

Użyłem funkcji odbicia dla osiągnięcia tego.

Zwyczaj ImageButton:

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); 
    } 
} 

Funkcja że konfiguracja ta ImageButton dla debouncing jest:

setAccumulatedClickListeners(OnEverClickListener, OnAccumulatedRequestsRead) 

Kiedy konfiguracja to widok z RecyclerView, wszystko działa poprawnie,

Wynik wygląda tak, gdy klikam 9 razy sekwencyjnie:

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 

Kiedy dodać ten niestandardowy ImageButton do RecyclerView ViewHolder wynik nie jest prawidłowy dla tej samej liczby kliknięć:

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 

Jest to kolejny wynik na ponownie 9 kolejnych kliknięć:

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 

W obu razy kliknąłem z tym samym odstępem czasu, ale kiedy ten przycisk dodany do RecyclerView daje dziwny wynik.

Jaki może być problem?

Aktualizacja:

Moja Adapter uproszczona realizacja:

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); 
     } 
    } 
} 
+0

Czy możesz pokazać implementację adaptera? – azizbekian

+0

@azizbekian Dodałem implementację adaptera – MBH

+0

Nie widzę implementacji 'getItemCount()', która prawdopodobnie znajduje się w 'BaseProductRVAdapter'. Ile masz przedmiotów? – azizbekian

Odpowiedz

4

mam rozwiązanie

Głównym problemem jest to, kiedy przewijać w RecyclerView Adapter, że VIES się wolnostojący z okna i wywoływana jest funkcja onDetachedFromWindow, w tej funkcji i unsubscribe (dispose) z obserwowalnego.

Roztwory próbuje:

1. Po dodaniu attachFunction

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

2. Usunięto notifyItemChanged (pozycja) wywołuje

Zazwyczaj zaktualizować widok po pewnej modyfikacji używając notifyItemChange(). Teraz aktualizuję widok bezpośrednio przez setText lub podobne funkcje.

Działa teraz prawidłowo.

Powiązane problemy