关于RecyclerView,developer.android.com沉默了什么?

Android应用程序或Android应用程序片段的生命周期问题对于实践中的android(android开发人员)极为重要。 怎么了 因为与生命周期状态相关的所有方法( onCreate()onStart()等)的回调的执行顺序都是硬编码的,所以错误的应用程序将导致应用程序无法操作。 生命周期与它有什么关系? -细心的habretchit会问。 毕竟,标题似乎与他无关吗? 我的回答是:活动的生命周期和RecyclerView的工作之间有一些共同点-使用此小部件时执行回调方法的难度很大 ,因此需要正确地应用它

如果不这样做,列表可能会表现得非常神秘。

RecyclerView的最小适配器


举个例子 有这样的列表适配器,其标准最小填充为:

清单1


public class RvCustomAdapter extends RecyclerView.Adapter<RvCustomAdapter.CustomViewHolder> { private final Frag1 frag; private final LayoutInflater lInflater; private ArrayList<JSONDataSet> dataSet; ... ... ... public RvCustomAdapter(final Frag1 fragment) { this.frag = fragment; this.lInflater = (LayoutInflater) fragment.getContext() .getSystemService(Context.LAYOUT_INFLATER_SERVICE); this.dataSet = new ArrayList<>(); } ... ... ... @Override public CustomViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { //    View view = lInflater.inflate(R.layout.recycler_view_data_item, parent, false); /** *      *  (size, margins, paddings  .) */ RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) view.getLayoutParams(); params.height = RecyclerView.LayoutParams.WRAP_CONTENT; view.setLayoutParams(params); return new CustomViewHolder(view); } //    view ( layout manager-) @Override public void onBindViewHolder(@NonNull CustomViewHolder holder, int position) { holder.showData(position); } @Override public int getItemCount() { return dataSet.size(); } /** *  view holder-      *     *        */ class CustomViewHolder extends RecyclerView.ViewHolder { ... ... ... @BindView(R.id.ll_Data) LinearLayout ll_Data; @BindView(R.id.cb_Data) CheckBox cb_Data; ... ... ... private JSONDataSet cur; CustomViewHolder(View itemView) { super(itemView); ButterKnife.bind(this, itemView); } /** * ,      *       . */ ... ... ... } 

在我们列表的适配器的onBindViewHolder()方法的代码中,其元素包含一个复选框( CheckBox ),它对处理程序方法(holder'a)具有吸引力,在该方法中,从连接到适配器的集合中读取数据并基于该数据进行设置-复选框-状态以及必要的元素( Listener )连接到接口的各种元素:

清单2


  void showData(final int position) { cur = dataSet.get(position); cb_Data.setChecked(cur.isChecked()); ... ... ... cb_Data.setOnCheckedChangeListener(cb_DataOnCheckedChangeListener); ll_Data.setOnClickListener(ll_DataOnClickListener); } private OnClickListener ll_DataOnClickListener = new OnClickListener() { @Override public void onClick(View view) { cur.setChecked(!cur.isChecked()); cb_Data.setChecked(cur.isChecked()); } }; private OnCheckedChangeListener cb_DataOnCheckedChangeListener = new OnCheckedChangeListener() { @Override public void onCheckedChanged(CompoundButton compoundButton, boolean checked) { cur.setChecked(checked); compoundButton.setChecked(checked); setItemsColor(checked); if (checked) { if (...) { (frag).addSelectedItemsCounter(cur); } else { cur.setChecked(!checked); compoundButton.setChecked(!checked); setItemsColor(!checked); if (...) { createPrimaryDialog(); } else { createSecondaryDialog(); } } } else { (frag).remSelectedItemsCounter(cur); } } }; 

设置标志并满足特定条件时,侦听器将更改集合中的数据,而当不执行该侦听器时,侦听器将显示一个或另一个对话框。

原来是这样的:


在图1中-生成的列表。 在图2中-标记的列表项。 在图3中-对话,通知标记下一个元素时违反条件。

为了使用Figure-1列表布局管理器( LayoutManager )获得结果,执行以下调用必要功能的顺序:

算法1


  1. Rv_Adapter.getItemCount()-检查集合中的元素数;
  2. Rv_Adapter.onAttachedToRecyclerView()-适配器连接到小部件;
  3. 当列表空间中未填充列表元素时,将对列表执行算法2的以下步骤:

算法2


  1. Rv_Adapter.onCreateViewHolder()-为集合的每个元素创建自己的处理程序;
  2. CustomViewHolder()-处理程序的构造函数被执行;
  3. Rv_Adapter.onBindViewHolder()-对于每个实例,都会启动视图构建器;
  4. Rv_Adapter.onViewAttachedToWindow()-生成的视图连接到窗口;

一切都很棒! 如果不是“但是”。 而是, 但是!

问题


当滚动浏览包含至少几十个项目的长列表时,我们将收到来自图3的消息,而没有任何其他操作。

故障排除


原因是在编写适配器代码时,滚动时我们并没有考虑此处此处列出的回调函数的性能顺序 。 他是这样的:

算法3


  1. 当在国外隐藏处理程序的关联实例的每个列表项的可见性时,将执行Rv_Adapter.onViewDetachedFromWindow()方法,从而将隐藏视图与窗口断开连接;
  2. 当列表的每个新元素( itemView )从可见范围之外出现时,将对关联的处理程序实例执行算法2;

但这还不是全部。 使用标记管理器的“默认”设置,从窗口断开连接的每个列表项都不会保留在队列中以进行长时间访问。 一旦其中有2个,管理器便将它们移动到已处置实例的队列中,这由对每个使用的列表项的Rv_Adapter.onViewRecycled()方法的调用来标记,反之亦然。

因此,算法3实际上如下所示:

算法3'


 //   :  - true,  - false: bool direction; if(direction){ /** *          *      * (  directDetachedViews) */ Rv_Adapter.onViewDetachedFromWindow(holder); /** *        *   ,  max */ if(directDetachedViews.size() > max) { /** *       (holder) *      *      * (  directRecycleredHolders) */ Rv_Adapter.onViewRecycled(holder); } /** *     * (visiblePos)   ,  */ if(visiblePos < Rv_Adapter.getItemCount()) { /** *       *    (  reverseDetachedViews) *   (itemView),   *   (  visiblePos), */ if(reverseDetachedViews.content(itemView)){ /** *        *  (  reverseRecycleredHolders) *   holder,    *  ,  visiblePos,    */ Rv_Adapter.onCreateViewHolder(itemView) -> { holder = CustomViewHolder(itemView); }; } else { /** *  -       *   (reverseRecycleredHolders) */ holder = reverseRecycleredHolders.getHolder(visiblePos); } /** *        *   */ Rv_Adapter.onBindViewHolder(holder, visiblePos); } else { /** *  -      *     (reverseDetachedViews) */ holder = reverseDetachedViews.getHolder(visiblePos) } //     Rv_Adapter.onViewAttachedToWindow(holder); } else { ... ... ... ... ... } 


从上面的算法3'中可以看出,如果您在列表中滚动的次数超过最大数量,则将重新创建其中的视图位置数量,为此将使用Rv_Adapter.onBindViewHolder(holder,visiblePos)方法,该方法将重复用户的操作。

结论与建议


为了避免在按列表滚动大于最大位置数的列表时在onBindViewHolder(holder,visiblePos)方法中重复操作必要的:

  1. 用一个字段补充收集元素,该字段的标志是将关联的视图挤入已使用的处理程序队列中,例如, 布尔回收
  2. onViewRecycled(holder)方法中插入有关如何设置此标志说明 ,例如.... setRecycled(true) ;
  3. 将对这个符号检查插入到onBindViewHolder(holder,visiblePos)方法中 ,例如,如果(!Handler.cur.isRecycled())...;
  4. onViewAttachedToWindow(holder)方法中插入有关消除此症状的说明,例如.... setRecycled(false) ;

例如 ,像这样:

清单3


  @Override public void onViewRecycled(@NonNull CustomViewHolder holder) { super.onViewRecycled(holder); holder.cur.setRecycled(true); } @Override public void onBindViewHolder(@NonNull CustomViewHolder holder, int position) { if (!holder.cur.isRecycled()){ ... ... ... } } @Override public void onViewAttachedToWindow(@NonNull CustomViewHolder holder) { super.onViewAttachedToWindow(holder); holder.cur.setRecycled(false); } 

Source: https://habr.com/ru/post/zh-CN430402/


All Articles