【问题标题】:findviewwithtag gives null when recyclerview gets scrolled当 recyclerview 滚动时,findviewwithtag 给出 null
【发布时间】:2020-06-28 06:12:37
【问题描述】:

我有一个使用 recyclerView 实现的自定义库,我想从其他片段中取消选择所选项目。

为此,我为每个视图设置了一个标签 -

@Override
    public void onBindViewHolder(GalleryAdapter.ViewHolder viewHolder, int i) {
        String gitem = galleryList.get(i);
        viewHolder.itemView.setTag(gitem); // Setting the tag
        ....
    }

要取消选择,我将按标签查找视图 -

recyclerView_gallery.findViewWithTag(String.valueOf(showImageView.getTag())).performClick();

当画廊 recyclerview 滚动在该图像上时它正在工作,但是当我向下滚动并转到该片段时 - findViewWithTag 返回 null,但是当我滚动到该图像并再次执行相同操作时,我发现没有错误。

我已经试过了-

recyclerView_gallery.getLayoutManager().scrollToPosition(position);
recyclerView_gallery.findViewWithTag(String.valueOf(showImageView.getTag())).performClick();

我也试过了-

recyclerView_gallery.findViewHolderForAdapterPosition(position);

但结果是一样的,如果 recyclerView 被滚动,它就不起作用了。

【问题讨论】:

    标签: android android-recyclerview android-adapter


    【解决方案1】:

    RecyclerViews 回收当前对同一内存不可见的所有视图。

    这意味着如果你滚动得太远,视图就不再有标签,所以findViewWithTag 将返回 null。

    我要做的是在某处设置一个列表,其中包含所有应该选择的项目,然后每次在onBindViewHolder 中检查并执行相应的操作。每次将项目滚动进/出屏幕时都会调用此方法。

    代码可能如下所示:

    public class RecyclerViewAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
    
        public List<String> selectedTags = new ArrayList<>();
    
        @Override
        public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) {
            Log.d("TAG", "onBindViewHolder was called for position: " + position);
    
            TextView textView = ((ViewHolder) holder).textView;
            textView.setTag(tags.get(position));
            textView.setOnClickListener(...)
    
            if (selectedTags.contains(textView.getTag())) {
                Log.d("TAG", position + " do something with this item.");
                textView.performClick();
            }
        }
    ...
    }
    
    public class MainActivity extends AppCompatActivity {
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            ...
            List<String> someTags = new ArrayList<>();
            someTags.add("someTags");
    
            ((RecyclerViewAdapter) recyclerView.getAdapter()).tags = someTags;
            ...
        }
    }
    

    【讨论】:

      【解决方案2】:

      Recycler 视图仅创建比可见视图计数多 4 或 5 个视图,并重新绑定/重用用户不可见的视图,因此称为 RecyclerView。调用 OnCreateView 方法来创建所需的视图,并且每次视图绑定数据时都会调用 onBindViewHolder。

      public void onBindViewHolder(GalleryAdapter.ViewHolder viewHolder, int i) {
          String gitem = galleryList.get(i);
          viewHolder.itemView.setTag(gitem); // Setting the tag
          ....
      }
      

      当视图与数据“A”绑定时,它的标签是“A”,当它被重用于项目“B”时,它的标签变成“B”,所以当你使用findViewByTag(“A”)时,它返回 null 因为标签已经变成了“B”。您可以通过将回收器视图包装在 NestedRecyclerView 中来禁用此功能,因为 NestedRecyclerView 创建了所需的所有视图,并且不会有视图重用。 (这很容易,但我不建议这样做)。

      另一种方法是将所选项目/itemId 保存在这样的位置。假设你的 galleryList 是一个字符串列表,

      private Set<String> selectedItemList = new HashSet<>();
      private List<String> galleryList = new ArrayList<>();
      
      public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
          String gItem = galleryList.get(position);
          holder.populateUi(gItem); //populate normal ui
          if (selectedItemList.contains(gItem)) {
              holder.markSelected(); //mark the ui as selected
          } else {
              holder.markUnSelected(); //mark the ui as unSelected
          }
      }
      

      而且我认为您可以通过某种方式获得被点击项目的位置,如果是这样,您可以按如下方式更新 ui。

      public void notifySelected(int... positions) {
          for (int position:positions) {
              String gItem = galleryList.get(position);
              selectedItemList.add(gItem);
              notifyItemChanged(position);
          }
      }
      
      public void notifyUnSelected(int... positions) {
          for (int position:positions) {
              String gItem = galleryList.get(position);
              selectedItemList.remove(gItem);
              notifyItemChanged(position);
          }
      }
      

      如果你不能得到位置,但只有项目,你可以这样改变

      int index= galleryList.indexOf(item);
      if(index>-1){
          selectedItemList.add(galleryList.get(index));
      }
      

      更好的方法是使用itemChanged Method with payload,因为它更流畅。 很抱歉代码风格,因为我很久没有用 Java 编码了。

      【讨论】:

        【解决方案3】:

        正如@WHOA 提到的

        当视图与数据“A”绑定时,它的标签是“A”,当它被重用于项目“B”时,它的标签变成“B”,所以当你使用findViewByTag(“A”)时,它返回 null 因为标签已经变成了“B”。

        这可能是您无法按标签找到视图的一个原因。但我不建议您使用 id 或选定标签保存任何其他列表。取而代之的是,您可以向数据模型添加一个布尔字段,该字段将告诉您的适配器是否被选中,或者您可以为您的模型创建一个具有相同字段的包装器(Idk 什么对您的情况更好)。然后您将确定是否选择了项目,并且您将能够将您的数据作为选择/未选择存储在数据库或其他存储中。

        一些代码示例要清楚: 适配器

        class RvAdapter(private val items: List<ListItem>) : RecyclerView.Adapter<MyViewHolder>() {
        
        fun selectItems(vararg ids: Int) {
            items.filter { ids.contains(it.id) }
                    .forEach { it.isSelected = true }
            notifyDataSetChanged()
        }
        
        fun unSelectItems(vararg ids: Int) {
            items.filter { ids.contains(it.id) }
                    .forEach { it.isSelected = true }
            notifyDataSetChanged()
        }
        
        override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyViewHolder {
            return MyViewHolder(LayoutInflater.from(parent.context).inflate(R.layout.item, parent, false))
        }
        
        override fun getItemCount(): Int = items.size
        
        override fun onBindViewHolder(holder: MyViewHolder, position: Int) {
            val currentItem = items[position]
            holder.bind(currentItem)
        }
        }
        

        ViewHolder:

        class MyViewHolder(view: View) : RecyclerView.ViewHolder(view) {
        fun bind(item: ListItem) {
            (itemView as TextView).text = item.id.toString()
            itemView.isSelected = item.isSelected // you decide how to mark your view as selected/unselected
        }
        }
        

        和 ListItem(根据需要更改 id: Int):

        data class ListItem(val id: Int, var isSelected: Boolean)
        

        IMO,这样,您和其他人将来阅读此代码或编辑时会更加清晰。 希望对您有所帮助!

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2018-05-08
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多