Mr.Eddart
Mr.Eddart

Reputation: 10273

ListView: change siblings' data dynamically

I have a ListView with items containing each one a SeekBar.

What I want to achieve is that when one of the SeekBars is slowly slided, the rest of the SeekBars gracefully slide at the same time (this is, I want the events of one items to make effect on the rest of the list items).

I tried to store all the views in an array of views inside the adapter, but I was getting unexpected and unpredictable behavior (I guess it is related to the way ListView manages the child items internally).

Any hint on how this should be properly done?

Thank you very much!

Upvotes: 1

Views: 129

Answers (2)

artkoenig
artkoenig

Reputation: 7257

You can do this with fancy stuff like RxJava or event bus, or without external dependencies:

class MyObjAdapter extends RecyclerView.Adapter<MyObjAdapter.MyObjViewHolder> {

    class MyObjViewHolder extends RecyclerView.ViewHolder {
        SeekBar bar;

        MyObjViewHolder(View itemView, SeekBar.OnSeekBarChangeListener listener) {
            super(itemView);
            bar = itemView.findViewById(R.id.bar);
            bar.setOnSeekBarChangeListener(listener);
        }

    }

    private LinearLayoutManager layoutManager;
    private int value = 0;
    private SeekBar.OnSeekBarChangeListener listener = new SeekBar.OnSeekBarChangeListener() {

        @Override
        public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
            if (fromUser) {
                value = progress;
                int firstVisiblePosition = layoutManager.findFirstVisibleItemPosition();
                notifyItemRangeChanged(firstVisiblePosition,
                        layoutManager.findLastVisibleItemPosition() - firstVisiblePosition + 1);
            }
        }

        @Override
        public void onStartTrackingTouch(SeekBar seekBar) {
        }

        @Override
        public void onStopTrackingTouch(SeekBar seekBar) {
        }
    };


    public MyObjAdapter(LinearLayoutManager layoutManager) {
        this.layoutManager = layoutManager;
    }

    @Override
    public MyObjViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        View view = LayoutInflater.from(parent.getContext())
                .inflate(R.layout.item, parent, false);

        return new MyObjViewHolder(view, listener);
    }

    @Override
    public void onBindViewHolder(MyObjViewHolder holder, int position) {
        holder.bar.setProgress(value);
    }

    @Override
    public int getItemCount() {
        return 100;
    }
}

Upvotes: 1

azizbekian
azizbekian

Reputation: 62199

I would solve the problem using RxJava.

Basically, you have a stream of data (Seekbar's current position) and have arbitrary amount of subscribers (each Seekbar of each row).

You need some kind of Observable, that possesses following features:

  1. you can set a value to that Observable
  2. upon subscription you are able to receive the value, that was previously dispatched

Turns out BehaviorSubject is the one that you need.

So, having declared a BehaviorSubject this was:

BehaviorSubject<Integer> subject = BehaviorSubject.createDefault(0); // default value is 0

Now anytime any Seekbar's value is changed just perform subject.onNext(currentPosition).

As soon as the view is being shown on the screen - subscribe to the stream:

Disposable d = subject.subscribe(new Consumer<Integer>() {
    @Override
    public void accept(Integer integer) throws Exception {
        seekbar.setProgress(integer);
    }
)

When the view is being recycled - dispose from the Disposable:

d.dispose();

Upvotes: 1

Related Questions