developer.android.comは、RecyclerViewについて何を言っていますか?

AndroidアプリケーションまたはAndroidアプリケーションのフラグメントのライフサイクルの問題は、Android(Android開発者)の練習にとって非常に重要です。 なんで? ライフサイクルの状態に関連するすべてのメソッド( onCreate()onStart()など)のコールバックの実行順序はハードコーディングされており、その誤ったアプリケーションはアプリケーションの動作不能につながるためです。 ライフサイクルはそれと何の関係がありますか? -丁寧なhabretchitelに聞いてください。 結局のところ、タイトルは、彼に関するものではないようです。 私は答えます:アクティビティのライフサイクルと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 )が含まれ、ハンドラーメソッド( ホルダー '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-次の要素をマークするときに条件の違反を通知するダイアログ。





図-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)メソッドが使用され、ユーザーのアクションが繰り返されることがわかります。







結論と推奨事項



maxを超える位置数でリストをスクロールするときにonBindViewHolder(holder、visiblePos)メソッドで操作を繰り返すことを避けるために必要です:







  1. コレクション要素に、関連するビューを使用中のハンドラーのキューに詰め込む兆候のあるフィールドを追加します(たとえば、 boolリサイクル)
  2. .... setRecycled(true)のように、このフラグonViewRecycled(ホルダー)メソッドに設定する方法に関する指示を挿入します。
  3. onBindViewHolder(holder、visiblePos)メソッドにこの記号のチェックを挿入します。たとえば、if(!Handler.cur.isRecycled())...;
  4. onViewAttachedToWindow(ホルダー)メソッドに、この症状を除去するための指示を挿入します。例えば、 .... 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); }
      
      






All Articles