Recyclerview在滚动期间更改项目


77

我有一个RecyclerView。每行都有一个播放按钮,textview和Progressbar。当单击“播放”按钮时,必须播放sdcard中的音频并必须进度条。问题是,当我向下滚动recyclerview时,更改下一行的进度条。这意味着我一次可以在屏幕上容纳5个项目。当我滚动到第六行时,第六行搜索栏突然改变。

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

作为我的回答,正在帮助许多人,请您接受我的回答。谢谢
Surendar D

Answers:


230

请尝试这个

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

5
伟大的解决方案..救了我的一天
丹麦夏尔马

1
最佳答案bro.really帮助
Nitheesh KP

2
天哪,它运作完美。:)这个答案必须被接受
Ajay Shrestha

4
为什么getItemViewType要返回position?? 对这个答案感到好奇。
Jimit Patel

10
这种方法可能会影响性能,尤其是在具有大量项目的情况下:RecyclerView的一大优势是它可以重用ViewHolder实例和相应的视图层次结构。因此,当用户滚动显示新项目时,而不是创建新视图时,不再可见的先前ViewHolders将被回收并绑定到新可见项目。但是通过返回position作为类型,RecyclerView将把每个ViewHolder处理为不可用于新项目,因为它具有不同的类型。因此,通过执行此操作,基本上可以禁用RecyclerView的“回收功能”
pakat

59

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

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

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

1
这是对我和我的RecyclerView的最佳答案。仅覆盖“ getItemViewType”会为我的recyclerView消除一些随机性(仅当向下滚动,向上移动位置但向后不起作用时)。同时实现“ getItemId”覆盖和“ setHasStableIds(true)”都可以完美地完成工作,既向下滚动又向上滚动。
亚历山德罗·乌迪康

7
但是,当我让getItemViewType返回各种viewType,而不仅仅是规则位置时,就会出现问题。有谁知道热解决这个问题?
RadoVidjen

谢谢,我整天都在努力解决类似的问题,因为文本输入可能会随机更改值。你是救世主。
Geoff

我总是在这里复出
Acauã皮塔

28

顾名思义,RecyclerView当您向下滚动时,a中的视图将被回收。这意味着您需要将每个项目的状态保留在支持模型中(在本例中为)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);

11

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

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

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

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


3

放入recylerView中的NestedScroll ViewXML然后添加属性nestedScrollingEnabled = false

并在您的适配器上onBindViewHolder添加此行

final MyViewHolder viewHolder = (MyViewHolder)holder;

将此viewHolder对象与视图一起使用setText或执行任何类型的单击事件。

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


将recylerView放入XML的NestedScroll视图中,并添加属性nestedScrollingEnabled = false。谢谢,这对我有帮助:)
Sachin Solanki

2

recyclerview.setItemViewCacheSize(YourList.size());


通常,这不是缓存列表的好方法,但是,今天我有了一个很小的recyclerView,所以这节省了我的时间。非常感谢
OkanSerdaroğlu

2

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

但是,为避免这种情况,可以使用android.support.v4.widget.NestedScrollView包裹在上RecyclerView并让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);

0

在处理大量数据时,我遇到了同样的问题,它可以与5一起使用,因为它呈现了屏幕上可见的五个元素,但是却为prob提供了更多元素。事情是 ..

有时RecyclerView和listView只是跳过填充数据。如果滚动时跳过了RecyclerView绑定功能,但是当您尝试调试recyclerView适配器时,它将正常工作,因为它将每次调用onBind,您还可以看到Google官方开发人员的view the listView世界。在20分钟-30分钟左右,他们将解释您永远不能假设每次都会调用getView按位置。

所以,我建议使用

satorufujiwara创建的RecyclerView DataBinder。

要么

yqritc创建的RecyclerView MultipleViewTypes活页夹。

如果您发现这些易于解决的方法,则可以使用其他可用的Binders。

这是处理MultipleView类型或如果您使用大量数据的方法。这些活页夹可以帮助您仔细阅读文档,将其修复,平安!!


0

你为什么不这样尝试

    HashMap<String, Integer> progressHashMap = new HashMap<>();

    //...
    if(!progressHashMap.containsKey(downloadpath)){
        progressHashMap.put(downloadpath, mPlayer.getCurrentPosition());
    }

    progressbar.setProgress(progressHashMap.get(downloadpath));

0

试试这个

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

也看到这个




0

如果您的recyclerview ViewHolder具有更多逻辑或其他视图,则应尝试:

 **order_recyclerView.setItemViewCacheSize(x);**

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


-2

这是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中的任何类似问题调整此逻辑。


如何设置viewItemIndex视图
Arya

那是您的适配器列表中的项目。您可以使用getAdapterPostionstethitems.get(position)在您的代码中获得。
激战

我仍然面临着同样的问题。当我向下滚动recyclerview时,请在下一行中更改进度栏。
艾莉亚2015年

它没有进入其他部分。
艾莉亚2015年

抱歉,更改if(selectedItems.get(position, true))if(selectedItems.get(position, false))。我将编辑原始答案。
2015年

-2

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

因此,您需要做的就是在绑定时告诉它不要刷新它。

这是您的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);
}

1
它违反了使用“回收器”视图的全部目的
Alex
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.