【问题标题】:How to make RecyclerView stops recycling defined positions?如何让 RecyclerView 停止回收定义的位置?
【发布时间】:2017-07-01 17:46:05
【问题描述】:

我的问题是:我在 RecyclerView 内的一个视图上发生了视频流。

当用户滚动时,视图会被回收,其他相机会在该回收的视图上开始自己的流式传输。这对用户界面不利,因为流式传输过程需要几秒钟才能启动。

我如何对 RecyclerView 说:“嘿,Recycler,请不要回收那个确切的位置 x 并给那个位置 总是 你给它的同一个视图第一次,而不是随机一次”?

请有人帮帮我=(

【问题讨论】:

  • 如果不想回收,或许可以使用ScrollView
  • 问题是它是一个设备列表。很多设备都没有这个问题,但其中一些设备是相机。我想通过用户按下这些相机卡开始流式传输,但是滚动时视图会被回收。那是我不想要的东西。

标签: android android-layout android-recyclerview android-viewholder recycle


【解决方案1】:

如果你使用查询,你可以使用

query.limit(//no of items you want to show in your RecyclerView)  

试一试。

或请发布您的查询代码

【讨论】:

  • 查询?什么意思?
  • 您如何将您的数据放在回收站视图中?请发布您的代码
  • 它是一个简单的数组列表,每一项都有一个布尔值,指示它是视频流还是简单的照片。
  • layoutManager= new LinearLayoutManager(getApplicationContext(),//没有要显示的项目);然后在你的适配器中返回 // 没有你想显示的项目 .size().
【解决方案2】:

在您希望被回收的ViewHolder 上执行viewHolder.setIsRecyclable(false)

来自docs of ViewHolder#setIsRecyclable(boolean)

通知回收者该项目是否可以回收。在 setIsRecyclable() 稍后设置为 true 之前,不可回收的视图不会被其他项目重用。

这将导致只创建一个ViewHolder

public class MyAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
    ...
    @Override
    public void onViewAttachedToWindow(final RecyclerView.ViewHolder holder) {
        if (holder instanceof VideoViewHolder) {
            holder.setIsRecyclable(false);
        }
        super.onViewAttachedToWindow(holder);
    }

    @Override
    public void onViewDetachedFromWindow(final RecyclerView.ViewHolder holder) {
        if (holder instanceof VideoViewHolder){
            holder.setIsRecyclable(true);
        }
        super.onViewDetachedFromWindow(holder);
    }
    ...
}

【讨论】:

  • 我试过了。但问题是:该视图不会被任何项目回收,甚至不会被原始项目回收......因此,当回收器再次到达相机时,它将创建另一个视图。
  • @DanielOliveira,你在哪里表演viewholder.setIsRecyclable(false)
  • onBindViewHolder 或 onCreateViewHolder。这个方法完全按照它应该做的:它不会回收视图,但会创建另一个视图,但仍然不能解决问题
  • 谢谢您的努力,我尝试了您的代码,但在显示视频卡后它仍然提供不同的持有人。我正在通过 viewholder's.hascode() 方法检查它们是否不同 =(
  • 如果我的列表中有 3 个摄像头,我将在池中拥有 3 个 CameraHolders,它们将随机分配给每个摄像头。一个简单的测试方法是用 3 台相机和至少 10 台其他设备填充您的列表。将摄像头从屏幕上移开,然后再往回滚动。
【解决方案3】:

您的问题来自于视图本身。 Viewholders 保持对视图的引用,而适配器则没有。适配器仅保留数据收集。因此,向 viewholder 添加一个字段,以保留对用于在 viewholder 中填充视图的数据元素的引用。换句话说:

public class SomeViewHolder extends RecyclerView.ViewHolder{

    private View view;
    private Data data;

    public SomeViewHolder(View itemView) {
        super(itemView);
        view = itemView;
    }

    public void bindData(Data data){
        view.setData(data);
        this.data = data;
    }

    public void setData(Data data){
       this.data = data;
    }


    public Data getData(){
        return data;
    }

    public View getView(){
        return view;
    }
}

现在,viewholder 知道适配器的哪个元素正在使用。因此,在adapter中重写绑定方法时,可以检查holder是否已经绑定了一些数据,如果数据中包含视频,可以避免绑定,强行设置一个已经加载的view。

@Override
public void onBindViewHolder(SomeViewHolder holder, int position) {

    //videoViewData is a data field you have to put into the adapter.
    //videoView is a view field you have to put into the adapter.

    if(adapterData.get(position).equals(videoViewData)){
        holder.setView(videoView);
        holder.setData(adapterData.get(position));
    }else{
        holder.bindData(adapterData.get(position));
        if(adapterData.get(position).isVideo()){
            videoViewData = adapterData.get(position);
            videoView = holder.getView(); 
        }
    }

}

最后,您必须重写适配器中的 onViewRecycled 方法,因此,当包含视频的视图被回收时,您可以获取该视图并将其放在其他位置。

public void onViewRecycled(SomeViewHolder holder){
    if(holder.getData().isVideo()){
        videoViewData = holder.getData().
        videoView = holder.getView();
        videoView.pauseVideo();
    }
}

请记住,如果您不管理存储的视图,这可能会导致一些严重的泄漏。此外,您必须定义方法来判断您的数据何时是视频,以及正确定义的 equals 方法。

【讨论】:

  • 方法很有趣,但还是有问题。如何从一个持有者“移除” videoView 并将其添加到另一个持有者?
【解决方案4】:

尝试将其用于该特定位置:

 holder.setIsRecyclable(false);

希望这会有所帮助。

【讨论】:

    【解决方案5】:

    在适配器的getItemViewType(int position) 方法中,为每个视频分配唯一的值,因此它始终会根据您的需要为相同的视频返回相同的 ViewHolder。

    • 返回唯一的正数作为每个视频类型的类型(这里我使用适配器位置作为唯一键)
    • 为任何非视频项目返回负数。 (这里没什么特别的,只是为了避免与视频项目冲突,我们对非视频项目使用负数)

    我希望你能明白。干杯:)

        @Override
        public int getItemViewType(int position) {
            // Just as an example, return 0 or 2 depending on position
            // Note that unlike in ListView adapters, types don't have to be   contiguous
            if(dataList.get(position).isVideo()){
                return position;
    
            }else{
                return -1;//indicates general type, if you have more types other than video, you can use -1,-2,-3 and so on.
            }
        }
    
     @Override
        public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
             switch (viewType) {
                 case -1:  View view1 = LayoutInflater.from(parent.getContext())
                        .inflate(R.layout.general_item, parent, false);
                         return new GeneralViewHolder(view1);
                 default:View view2 = LayoutInflater.from(parent.getContext())
                        .inflate(R.layout.video_item, parent, false);
                         return new VideoViewHolder(view2);
    
             }
        }
    

    【讨论】:

    • 这是一个巨大的解决方法,它向我们展示了 RecyclerView 需要在其 api 中提供一些正式的东西才能让我们达到这种行为。但这实际上有效,我给你我的赏金积分。谢谢达里什。
    • 漂亮的解决方案。
    • @Darish 对我的观点有任何想法,我只是不希望第一个项目被回收,我什至完全不回收 recyclerview。这是我的代码:stackoverflow.com/questions/42209168/…
    【解决方案6】:

    RecyclerView 多次使用一个视图,当它包含一次未显示在屏幕上的列表时(意味着列表包含大量未同时显示在屏幕上的项目,您需要上下滚动)。当用户滚动列表时,屏幕外的项目被重复使用以显示剩余的列表项目,这称为回收。

    要停止回收项目,请在您的 onBindViewHolder 方法中调用此方法:

    viewHolder.setIsRecyclable(false);
    

    此语句停止回收视图。

    要开始回收项目,请在您的 onBindViewHolder 方法中调用此方法:

    viewHolder.setIsRecyclable(true);
    

    我希望这能解决您的问题。 谢谢

    【讨论】:

    • 其实这并没有解决。它不会回收,而是创建一个新的。我的愿望是永远不要回收,永远不要创造一个新的,但总是使用相同的。
    • @Daniel 它有效并解决了我的问题。感谢您的意见。
    【解决方案7】:

    在 recyclerview 中处理不回收物品的最佳方法此答案将解决您的问题。

    Not to recycle item

    【讨论】:

      猜你喜欢
      • 2019-06-10
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-01-12
      • 2017-05-11
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多