Recyclerview 在滚动期间更改项目

Posted

技术标签:

【中文标题】Recyclerview 在滚动期间更改项目【英文标题】:Recyclerview Changing Items During Scroll 【发布时间】: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);
    

【问题讨论】:

作为我的回答,正在帮助很多人,请您接受我的回答。谢谢 【参考方案1】:

请试试这个

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

     @Override
     public int getViewTypeCount()     
         return getCount();
     
    
     @Override
     public int getItemViewType(int position)     
         return position;
     
    

    如果您使用 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_
    android:layout_>

    <LinearLayout
        android:layout_
        android:layout_>

        <android.support.v7.widget.RecyclerView
            android:id="@+id/recycler_view"
            android:layout_
            android:layout_/>

   </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 这是您的适配器列表中的项目。您可以在代码中使用getAdapterPostion 或stethitems.get(position) 我仍然面临同样的问题。当我向下滚动 recyclerview 时,更改下一行的进度条。 没有进入else部分。 啊抱歉,将 if(selectedItems.get(position, true)) 更改为 if(selectedItems.get(position, false))。我将编辑原始答案。

以上是关于Recyclerview 在滚动期间更改项目的主要内容,如果未能解决你的问题,请参考以下文章

焦点更改时强制 RecyclerView 滚动(Android TV)

更改 recyclerView 滚动位置参考

滚动时如何为 recyclerview 项目设置动画?

如何在 RecyclerView 项目更改上触发 MainActivity 映射修改

嵌套的 RecyclerView 滚动无法向下滚动 ViewPager2 的 BottomSheetBehavior

Android RecyclerView:如果不使用“setIsRecyclabe(false)”,则滚动期间内存使用量会增加