【问题标题】:Recyclerview Changing Items During ScrollRecyclerview 在滚动期间更改项目
【发布时间】:2015-11-10 23:14:30
【问题描述】:

我有一个 RecyclerView。每行都有一个播放按钮、文本视图和进度条。当点击播放按钮时,必须从我的 sdcard 播放音频并且必须进度条 问题是当我向下滚动回收视图时,会更改下一行的进度条。意味着我可以一次在屏幕上放置 5 个项目。当我滚动到第 6 行时,第 6 行搜索栏突然变化。

public class ListAdapter extends RecyclerView.Adapter {

    private List<Historyitem> stethitems;
    public Context mContext;
    public Activity activity;
    public Handler mHandler;

    static MediaPlayer mPlayer;
    static Timer mTimer;

    public ListAdapter(Activity activity,Context mContext,List<Historyitem> stethitems) {
        this.stethitems = stethitems;
        this.mContext = mContext;
        this.activity = activity;
    }

    @Override
    public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup viewGroup, int viewType) {

        View rootView = LayoutInflater.
                from(mContext).inflate(R.layout.stethoscopeadapteritem, null, false);
        RecyclerView.LayoutParams lp = new RecyclerView.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
                ViewGroup.LayoutParams.WRAP_CONTENT);
        rootView.setLayoutParams(lp);
        mHandler = new Handler();
        return new MyViewHolder(rootView);
    }

    @Override
    public void onBindViewHolder(final RecyclerView.ViewHolder viewHolder, int position) {

        final Historyitem dataItem = stethitems.get(position);
        final MyViewHolder myViewHolder = (MyViewHolder) viewHolder;
        myViewHolder.progressplay.setProgress(0);
        myViewHolder.stethdatetime.setText(dataItem.getReported_Time());
        myViewHolder.stethhosname.setText(dataItem.getdiv());

        if(dataItem.getPatient_Attribute().replaceAll(" ","").equals("")){
            myViewHolder.stethdoctorname.setText(dataItem.getunit());
        } else {
            myViewHolder.stethdoctorname.setText(dataItem.getPatient_Attribute());
        }

        myViewHolder.stethstreamplay.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {

                   FileDownload(dataItem.getmsg(),
                            myViewHolder.progressplay);

            }
        });
    }

    @Override
    public int getItemCount() {

        return stethitems.size();
    }

    public class MyViewHolder extends RecyclerView.ViewHolder {

        final CustomTextRegular stethdatetime;
        final CustomTextView stethhosname;
        final CustomTextBold stethdoctorname;
        final ImageButton stethstreamplay;
        final NumberProgressBar progressplay;

        public MyViewHolder(View itemView) {
            super(itemView);

            stethdatetime = (CustomTextRegular)
                    itemView.findViewById(R.id.stethdatetime);
            stethhosname = (CustomTextView)
                    itemView.findViewById(R.id.stethhosname);
            stethdoctorname = (CustomTextBold)
                    itemView.findViewById(R.id.stethdoctorname);
            stethstreamplay = (ImageButton)
                    itemView.findViewById(R.id.stethstreamplay);
            progressplay= (NumberProgressBar)
                    itemView.findViewById(R.id.progressplay);


        }
    }
    public void  FileDownload(final String downloadpath,

                                     final NumberProgressBar progressplay) {

        new AsyncTask<NumberProgressBar, Integer, NumberProgressBar>() {
            NumberProgressBar progress;
            @Override
            protected void onPreExecute() {
                super.onPreExecute();
                try {
                    if(mPlayer!=null){
                        mPlayer.stop();
                    }
                }catch (Exception e){
                }
                try {
                    if(mTimer != null){
                        mTimer.purge();
                        mTimer.cancel();
                    }
                }catch (Exception e){
                }
            }

            @Override
            protected NumberProgressBar doInBackground(NumberProgressBar... params) {
                int count;
                progress = progressplay;
                try {

                    final List<NameValuePair> list = new ArrayList<NameValuePair>();
                    list.add(new BasicNameValuePair("pid",id));

                    URL url = new URL(Config.requestfiledownload + "?path=" +
                            downloadpath);
                    URLConnection connection = url.openConnection();
                    connection.connect();

                    int lenghtOfFile = connection.getContentLength();
                    // download the file
                    InputStream input = new BufferedInputStream(url.openStream());
                    OutputStream output = new FileOutputStream(Environment.getExternalStorageDirectory() +
                            "record.wav");
                    byte data[] = new byte[1024];
                    long total = 0;
                    while ((count = input.read(data)) != -1) {
                        total += count;
                        // publishing the progress....
                        publishProgress((int) (total * 100 / lenghtOfFile));
                        output.write(data, 0, count);
                    }
                    output.flush();
                    output.close();
                    input.close();
                } catch (Exception e) {

                }
                return progress;
            }
            @Override
            protected void onPostExecute(final NumberProgressBar numberProgressBar) {
                super.onPostExecute(numberProgressBar);

                        try {
                            StartMediaPlayer(numberProgressBar);
                        } catch (Exception e){
                            e.printStackTrace();
                        }

            }
        }.execute();
    }

    public void StartMediaPlayer(final NumberProgressBar progressbar){
        Uri playuri = Uri.parse("file:///sdcard/record.wav");
        mPlayer = new MediaPlayer();
        mPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
        mPlayer.reset();
        try {
            mPlayer.setDataSource(mContext, playuri);
        } catch (IllegalArgumentException e) {

        } catch (SecurityException e) {

        } catch (IllegalStateException e) {

        } catch (Exception e) {

        }
        try {
            mPlayer.prepare();
        } catch (Exception e) {

        }

        mPlayer.start();
        progressbar.setMax(mPlayer.getDuration());
        mPlayer.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
            @Override
            public void onCompletion(MediaPlayer mp) {
                if(mPlayer!=null) {
                    mPlayer.release();
                    progressbar.setProgress(0);
                }
                if(mTimer != null){
                    mTimer.purge();
                    mTimer.cancel();
                }
            }
        });
        mTimer = new Timer();
        mTimer.schedule(new TimerTask() {
            @Override
            public void run() {
                mHandler.post(new Runnable() {
                    @Override
                    public void run() {
                        progressbar.setProgress(mPlayer.getCurrentPosition());
                    }
                });
            }
        },0,500);
    }}

【问题讨论】:

  • 作为我的回答,正在帮助很多人,请您接受我的回答。谢谢

标签: android android-scrollview android-recyclerview


【解决方案1】:

请试试这个

  1. 如果您使用ListView - 覆盖以下方法。

     @Override
     public int getViewTypeCount() {    
         return getCount();
     }
    
     @Override
     public int getItemViewType(int position) {    
         return position;
     }
    
  2. 如果您使用 RecyclerView - 仅覆盖 getItemViewType() 方法。

     @Override
     public int getItemViewType(int position) {    
         return position;
     }
    

【讨论】:

  • 很好的解决方案.. 拯救了我的一天
  • 最佳答案兄弟。真的有帮助
  • 天哪,它工作得很好。 :) 这个答案必须被接受
  • 为什么getItemViewType 会返回position??很好奇这个答案。
  • 这种方法可能会影响性能,尤其是在拥有大量项目时:RecyclerView 的一大优势是它重用了 ViewHolder 实例和相应的视图层次结构。因此,当用户滚动并且新项目变得可见而不是创建新视图时,以前不再可见的 ViewHolders 将被回收并绑定到新可见的项目。但是通过将位置作为类型返回,RecyclerView 会将每个 ViewHolder 处理为不可重用于新项目,因为它具有不同的类型。所以 RecyclerView 的“回收功能”基本上是通过这样做禁用的
【解决方案2】:

在您的适配器构造函数中添加setHasStableIds(true); 并在适配器中覆盖这两个方法。如果有人在ViewPager 中使用RecyclerView,它也可以在NestedScrollView 中使用。

@Override
public long getItemId(int position) {
            return position;
}

@Override
public int getItemViewType(int position) {
       return position;
}

【讨论】:

  • 这对我和我的 RecyclerView 来说是最好的答案。仅覆盖“getItemViewType”会消除我的 recyclerView 的一些随机性(仅在向下滚动时,在位置上向上,但向后滚动时不起作用)。同时实现“getItemId”覆盖和“setHasStableIds(true)”可以完美地完成这项工作,无论是向下滚动还是向上滚动。
  • 但是当我让 getItemViewType 返回各种视图类型时,问题就出现了,而不仅仅是常规位置。有谁知道热来解决这个问题?
  • 谢谢,我整天都在为类似的问题苦苦挣扎,因为文本输入可能会随机更改值。你是救世主。
  • 我总是回来这里
【解决方案3】:

顾名思义,RecyclerView 中的视图在您向下滚动时会被回收。这意味着您需要在支持模型中保留每个项目的状态,在本例中为 Historyitem,并在您的 onBindViewHolder 中恢复它。

1) 创建位置、最大值以及您需要在模型中保存ProgressBar 状态的任何其他变量。

2) 根据您的支持模型中的数据设置您的ProgressBar 的状态;单击时,将项目的位置传递给您的 FileDownload/StartMediaPlayer 方法。

public void onBindViewHolder(final RecyclerView.ViewHolder viewHolder, int position) {
    final Historyitem dataItem = stethitems.get(position);
    final MyViewHolder myViewHolder = (MyViewHolder) viewHolder;
    myViewHolder.progressplay.setMax(dataItem.getMax());
    myViewHolder.progressplay.setProgress(dataItem.getPosition());
    ...
    myViewHolder.stethstreamplay.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View view) {
               FileDownload(dataItem.getmsg(), position);
        }
    });

3) 通过更新支持模型并通知它已更改来更新进度条。

stethitems.get(position).setPosition(mPlayer.getCurrentPosition());
notifyItemChanged(position);

【讨论】:

    【解决方案4】:

    我在尝试实现一个包含edittex和一个复选框作为行元素的recyclerview时遇到了同样的问题。我只是通过在适配器类中添加以下两行来解决滚动值更改问题。

    @Override
    public long getItemId(int position) {
        return position;
    }
    
    @Override
    public int getItemViewType(int position) {
        return position;
    }
    

    我希望这将是一个可能的解决方案。谢谢

    【讨论】:

      【解决方案5】:

      recyclerview.setItemViewCacheSize(YourList.size());

      【讨论】:

      • 通常这不是缓存列表的好方法,但是今天我有一个小的 recyclerView 所以这节省了我的一天。非常感谢
      【解决方案6】:

      如果你的 recyclerview ViewHolder 有更多的逻辑或者有不同的视图,那么你应该尝试:

       **order_recyclerView.setItemViewCacheSize(x);**
      

      其中 x 是列表的大小。以上对我有用,我希望它也对你有用。

      【讨论】:

        【解决方案7】:

        当我们动态更改RecyclerView 项目时(即,当更改特定RecyclerView 项目的背景颜色时),由于RecyclerView 重用它的性质,它可能会在滚动时以意想不到的方式改变项目的外观项目。

        但是,可以使用包裹在RecyclerView 周围的android.support.v4.widget.NestedScrollView 并让NestedScrollView 处理滚动来避免这种情况。

        <android.support.v4.widget.NestedScrollView
            android:layout_width="match_parent"
            android:layout_height="match_parent">
        
            <LinearLayout
                android:layout_width="match_parent"
                android:layout_height="match_parent">
        
                <android.support.v7.widget.RecyclerView
                    android:id="@+id/recycler_view"
                    android:layout_width="match_parent"
                    android:layout_height="match_parent"/>
        
           </LinearLayout>
        </android.support.v4.widget.NestedScrollView>
        

        然后在代码中,您可以禁用 RecyclerView 的嵌套滚动以通过仅让 NestedScrollView 处理滚动来平滑滚动。

        ViewCompat.setNestedScrollingEnabled(recyclerView, false);
        

        【讨论】:

          【解决方案8】:

          只需将 recylerView 放在 xml 中的 NestedScroll 视图中,然后添加属性 nestedScrollingEnabled = false。

          然后在你的适配器 onBindViewHolder 添加这一行

          final MyViewHolder viewHolder = (MyViewHolder)holder;

          将此 viewHolder 对象与您的视图一起使用以设置文本或执行任何类型的 Click 事件。

          例如 viewHolder.txtSubject.setText("Example");

          【讨论】:

          • 将 recylerView 放在 XML 中的 NestedScroll 视图中,并添加属性 nestedScrollingEnabled = false。谢谢这对我有帮助:)
          【解决方案9】:

          覆盖适配器中的 getItemViewType 方法。在 kotlin 中使用

          override fun getItemViewType(position: Int): Int {
              return position
          }
          

          【讨论】:

            【解决方案10】:

            我在处理大量数据时遇到了同样的问题,它与 5 一起工作,因为它渲染了在屏幕上可见的五个元素,但给概率提供了更多元素。事情是..

            有时 RecyclerView 和 listView 只是跳过填充数据。如果 RecyclerView 绑定功能在滚动时被跳过,但是当您尝试调试 recyclerView 适配器时,它会正常工作,因为它每次都会调用 onBind,您还可以看到官方谷歌开发人员的视图The World of listView。大约 20 分钟 -30 分钟,他们会解释说你永远不能假设 getView by position 每次都会被调用。

            所以,我会建议使用

            RecyclerView DataBinder created by satorufujiwara.

            RecyclerView MultipleViewTypes Binder created by yqritc.

            如果您发现它们很容易解决,这些是其他可用的 Binder。

            这是处理 MultipleView 类型或使用大量数据的方法。这些活页夹可以帮助你 只需仔细阅读将修复它的文档,和平!

            【讨论】:

              【解决方案11】:

              你为什么不这样尝试,

                  HashMap<String, Integer> progressHashMap = new HashMap<>();
              
                  //...
                  if(!progressHashMap.containsKey(downloadpath)){
                      progressHashMap.put(downloadpath, mPlayer.getCurrentPosition());
                  }
              
                  progressbar.setProgress(progressHashMap.get(downloadpath));
              

              【讨论】:

                【解决方案12】:

                试试这个

                @Override public void smoothScrollToPosition(RecyclerView    recyclerView, RecyclerView.State state,
                       int position) {    LinearSmoothScroller linearSmoothScroller =
                           new LinearSmoothScroller(recyclerView.getContext()) {
                               @Override
                               public PointF computeScrollVectorForPosition(int targetPosition) {
                                   return LinearLayoutManager.this
                                           .computeScrollVectorForPosition(targetPosition);
                               }
                           };    linearSmoothScroller.setTargetPosition(position);    startSmoothScroll(linearSmoothScroller); }
                

                see this also

                【讨论】:

                  【解决方案13】:

                  此行在每次绑定时将进度更改为 0

                  myViewHolder.progressplay.setProgress(0);
                  

                  将其状态保存在某处,然后将其加载到同一行。

                  【讨论】:

                    【解决方案14】:

                    我有类似的问题,并搜索了很多正确的答案。基本上,它更像是一种回收器视图的设计,它更新滚动上的视图,因为它刷新了视图。

                    所以你需要做的就是在绑定时告诉它不要刷新它。

                    这就是你的 onBindViewHolder 的样子

                    @Override
                    @SuppressWarnings("unchecked")
                    public void onBindViewHolder(final BaseViewHolder holder, final int position) {
                        holder.bind(mList.get(position));
                    
                        // This is the mighty fix of the issue i was having
                        // where recycler view was updating the items on scroll.
                        holder.setIsRecyclable(false);
                    }
                    

                    【讨论】:

                    • 它违背了使用“RECYCLER”视图的全部目的
                    【解决方案15】:

                    这是 recyclerView 的预期行为。由于视图被回收,您的项目可能会进入随机视图。为了克服这个问题,您必须自己指定将哪个项目放入哪种视图中。此信息可以保存在 SparseBooleanArray 中。你可以做的是像这样在你的适配器中创建一个 SparseBooleanArray

                    SparseBooleanArray selectedItems = new SparseBooleanArray();
                    

                    每当您的视图发生变化时,请执行以下操作:

                    selectedItems.put(viewItemIndex,true);
                    

                    现在在你的 onBindViewHolder 中做

                     if(selectedItems.get(position, false)){
                        //set progress bar of related to the view to desired position
                        }
                        else {
                            //do the default
                        }
                    

                    这是解决您问题的基础。您可以将此逻辑调整为 recyclerView 中任何类型的类似问题。

                    【讨论】:

                    • 如何设置view的viewItemIndex
                    • 这是您的适配器列表中的项目。您可以在代码中使用getAdapterPostionstethitems.get(position)
                    • 我仍然面临同样的问题。当我向下滚动 recyclerview 时,更改下一行的进度条。
                    • 没有进入else部分。
                    • 啊抱歉,将 if(selectedItems.get(position, true)) 更改为 if(selectedItems.get(position, false))。我将编辑原始答案。
                    猜你喜欢
                    • 1970-01-01
                    • 1970-01-01
                    • 1970-01-01
                    • 2022-01-01
                    • 1970-01-01
                    • 1970-01-01
                    • 1970-01-01
                    • 1970-01-01
                    • 2016-03-20
                    相关资源
                    最近更新 更多