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 项目更改上触发 MainActivity 映射修改
嵌套的 RecyclerView 滚动无法向下滚动 ViewPager2 的 BottomSheetBehavior
Android RecyclerView:如果不使用“setIsRecyclabe(false)”,则滚动期间内存使用量会增加