Обновление ProgressBar в RecyclerView

У меня есть RecyclerView. В нем элементы имеют стандартный макет - один TextView и один ProgressBar. Элементы добавляются в recyclerview во время выполнения. Всякий раз, когда добавляется Item, запускается AsyncTask, который обновляет ProgressBar. AsynTask содержит ссылку на объект ProgressBar из адаптера RecyclerView. Проблема возникает, когда в ресайклере слишком много элементов.

Я знаю, что RecyclerView перерабатывает любые старые представления и, таким образом, хочет, чтобы это было по крайней мере для прогрессивных баров.

Каким будет идеальный способ реализовать это?

Ниже приведена выдержка из адаптера

public class RecViewAdapter extends
        RecyclerView.Adapter<RecViewAdapter.ViewHolder> {
    Context mContext;
    List<String> mRunns;
    static ExecutorService mExec;
    static HashSet<Integer> mProcessed = new HashSet<>();

    public RecViewAdapter(Context context, List<String> runns) {
        mContext = context;
        mRunns = runns;
        mExec = Executors.newFixedThreadPool(1);
    }

    @Override
    public ViewHolder onCreateViewHolder(ViewGroup viewGroup, int viewType) {
        View v = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.runnabel_item, viewGroup,
                false);
        ViewHolder vh = new ViewHolder(v);
        return vh;
    }

    @Override
    public void onBindViewHolder(ViewHolder holder, int position) {
        holder.runnName.setText(mRunns.get(position));
        if (!mProcessed.contains(position)) {
            new ProgressTask(holder.pBar, position).executeOnExecutor(mExec, null);
            mProcessed.add(position);
        }
    }

    @Override
    public int getItemCount() {
        return mRunns.size();
    }

    public static class ViewHolder extends RecyclerView.ViewHolder {
        TextView runnName;
        ProgressBar pBar;

        public ViewHolder(View view) {
            super(view);
            runnName = (TextView) view.findViewById(R.id.textView);
            pBar = (ProgressBar) view.findViewById(R.id.progressBar);
            pBar.setIndeterminate(false);
            pBar.setMax(100);
            pBar.setProgress(0);

        }
    }
}

Кроме того, я добавляю элементы в RecyclerView, используя notifydatasetchanged.

Ответ 1

Немного поздно, но я нашел способ заставить его работать.

Мой recyclerview содержит большое число зрителей, и только один из зрителей имеет индикатор выполнения. У меня есть база данных sqlite, в которой я поддерживаю идентификаторы, которые я использую для синхронизации между моей службой и активностью (для определения того, какие виды в recyclerview необходимо обновить).

В зависимости от вашей реализации вам нужно будет найти способ определить, какое событие трансляции соответствует тому элементу адаптера. Я дал упрощенную версию того, что я сделал ниже.

Модель для панели выполнения:

class ProgressModel{
    String progressId;
    int progress = 0;
}

public int getProgress(){
    return progress;
}

ViewHolder:

public class ProgressViewHolder extends RecyclerView.ViewHolder {
    private ProgressBar mProgressBar;

    public ProgressViewHolder(View itemView) {
        mProgressBar = (ProgressBar) itemView.findViewById(R.id.mProgressBar);
    }

    public ProgressBar getProgressBar() {
        return mProgressBar;
    }   
}

В адаптере recycliewiew,

@Override
public void onBindViewHolder(ProgressViewHolder holder, int position) {
    ProgressModel item = mData.get(position);
    int progress = item.getProgress();
        if (progress > 0) {
            ProgressBar downloadProgress = holder.getProgressBar();
            if (downloadProgress.isIndeterminate()) {
                downloadProgress.setIndeterminate(false);
            }
            downloadProgress.setProgress(progress);
        }
}

public void refresh(position, ProgressModel item){
    mData.set(position, item);
    notifyItemChanged(position);
}

В Управлении, которое реализует заполнение представления, создайте статический экземпляр самого себя и передайте его в BroadcastReceiver. Мне потребовалось некоторое время, чтобы понять, что требуется статический экземпляр, иначе представление не будет изменено, хотя я вызываю notifyItemchanged().

public class MainActivity extends Activity{
    private static MainActivity instance;
    private MyReceiver mReceiver;
    private MyAdapter mAdapter;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        instance = this;
        mReceiver = new MyReceiver(instance);
        //TODO: Initialize adapter with data and set to recyclerview
    }

    public void update(Intent intent){
        ProgressModel model = new ProgressModel ();
        //TODO: set progress detail from intent to model and get position from progressId
        instance.mAdapter.refresh(position,model);
    }

    private static class MyReceiver extends BroadcastReceiver {
        MainActivity activity;

        public DownloadReceiver(MainActivity activity) {
            this.activity = activity;
        }

        @Override
        public void onReceive(Context context, Intent intent) {
            //pass intent with progress details to activity
            activity.update(intent);
        }
    }

}

Надеюсь, что это поможет.

Ответ 2

Вы можете просто использовать метод "findViewHolderForAdapterPosition" для представления рециркулятора, и вы получите объект viewHolder из этого типа, затем вставьте этот viewHolder в свой видоискатель адаптера, чтобы вы могли напрямую получить доступ к своим представлениям виджетов, (в этом случае мы обращаемся к progressBar)

Ниже приведен пример кода для Kotlin

 /**
     * update progress bar in recycler view
     * get viewHolder from position and progress bar from that viewHolder
     *  we are rapidly updating progressbar so we did't use notify method as it always update whole row instead of only progress bar
     *  @param position : position of list cell
     *  @param progress : new progress value
     */
    private fun updateDownloadProgressBar(position :Int, progress:Int)
    {
        val viewHolder = recyclerViewDownload.findViewHolderForAdapterPosition(position)
        (viewHolder as ViewHolderDownload).progressBarDownload.progress=progress
    }