web-dev-qa-db-ja.com

ListViewのリサイクルメカニズムの仕組み

だから私は以前にこの問題を抱えていて、当然 here について助けを求めました。 Luksprogの答えは、ListViewとGridViewがリサイクルビューでどのように最適化されているのかわからなかったため、素晴らしかったです。そのため、彼のアドバイスで、GridViewにビューを追加する方法を変更することができました。問題は今では意味をなさないものを持っていることです。これは、私のgetViewからの私のBaseAdapterです:


public View getView(int position, View convertView, ViewGroup parent) {
        if(convertView == null) {
            LayoutInflater inflater = LayoutInflater.from(parent.getContext());
            convertView = inflater.inflate(R.layout.day_view_item, parent, false);
        }
        Log.d("DayViewActivity", "Position is: "+position);
        ((TextView)convertView.findViewById(R.id.day_hour_side)).setText(array[position]);
        LinearLayout layout = (LinearLayout)convertView.findViewById(R.id.day_event_layout);

        //layout.addView(new EventFrame(parent.getContext()));

        TextView create = new TextView(DayViewActivity.this);
        LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(0, (int)TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 62, getResources().getDisplayMetrics()), 1.0f);
        params.topMargin = (int)TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 1, getResources().getDisplayMetrics());
        params.bottomMargin = (int)TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 1, getResources().getDisplayMetrics());
        create.setLayoutParams(params);
        create.setBackgroundColor(Color.BLUE);
        create.setText("Test"); 
        //the following is my original LinearLayout.LayoutParams for correctly setting the TextView Height
        //new LinearLayout.LayoutParams(0, (int)TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 60, getResources().getDisplayMetrics()), 1.0f)   
        if(position == 0) {
            Log.d("DayViewActivity", "This should only be running when position is 0. The position is: "+position);
            layout.addView(create);
        }

        return convertView;
    }

}

問題は、スクロールすると、位置0ではなく、これが発生することです...位置6と位置8のように見えます。さらに、位置8に2を入れます。今でも、ListViewとGridViewを使用して、なぜこれが起こっているのか理解できない。私がこの質問をしている主な理由の1つは、おそらくListViewとGridViewのリサイクルビューについて知らない人、またはこの article の意味、ScrapViewメカニズムを知らない人を助けることです。

enter image description here

後の編集

Googleへのリンクの追加IOトークは、基本的にListViewの仕組みを理解するために必要なすべてです。リンクはコメントの中で死んでいた。したがって、user3427079はそのリンクを更新するのに十分なほど良かったです。 ここ これは簡単にアクセスするためのものです。

141
Andy

最初は、リストビューのリサイクルとconvertviewの使用メカニズムにも気づいていませんでしたが、丸1日の調査の後、 Android.amberfog の画像を参照することでリストビューのメカニズムをほとんど理解しました enter image description here

アダプタでリストビューがいっぱいになると、基本的にリストビューが画面に表示できるRowsの数が表示され、リストをスクロールしても行数は増えません。これはAndroidが使用するトリックであるため、リストビューがより効率的かつ高速に動作します。ご覧のとおり、画像を参照するリストビューの内部ストーリーでは、最初にリストビューに7つの表示項目があり、次に項目1が表示されなくなるまで上にスクロールすると、getView()はこのビュー(つまりitem1)をリサイクル業者と使用できます

System.out.println("getview:"+position+" "+convertView);

あなたの中に

public View getView(final int position, View convertView, ViewGroup parent)
{
    System.out.println("getview:"+position+" "+convertView);
    ViewHolder holder;
    View row=convertView;
    if(row==null)
    {
        LayoutInflater inflater=((Activity)context).getLayoutInflater();
        row=inflater.inflate(layoutResourceId, parent,false);

        holder=new PakistaniDrama();
        holder.tvDramaName=(TextView)row.findViewById(R.id.dramaName);
        holder.cbCheck=(CheckBox)row.findViewById(R.id.checkBox);

        row.setTag(holder);

    }
    else
    {
        holder=(PakistaniDrama)row.getTag();
    }
            holder.tvDramaName.setText(dramaList.get(position).getDramaName());
    holder.cbCheck.setChecked(checks.get(position));
            return row;
    }

Logcatでは、最初はrecyclerにビュー(つまりアイテム)がなかったため、最初にconvertviewがすべての表示行に対してnullであることに気付くでしょう。そのため、getView()は各表示アイテムに対して新しいビューを作成しますが、上にスクロールしてアイテム1が画面から出ると、現在の状態でRecyclerに送信されます(たとえば、TextView 'text'または私の場合、チェックボックスがオンの場合、ビューに関連付けられ、リサイクラに保存されます)。

上下にスクロールすると、リストビューは新しいビューを作成しません。リサイクラにあるビューを使用します。 Logcatでは、「convertView」がnullではないことがわかります。これは、新しいアイテム8がconvertviewを使用して描画されるためです。つまり、基本的にはリサイクラからアイテム1ビューを取得し、その場所、あなたは私のコードでそれを観察することができます。チェックボックスがあり、位置0でチェックすると(item1にチェックボックスがあり、チェックしたとしましょう)、下にスクロールすると、すでにチェックされたアイテム8のチェックボックスが表示されます、これがリストビューが同じビューを使用している理由ですパフォーマンスの最適化のために新しいものを作成していません。

重要なこと

1。リストビューのlayout_heightおよびlayout_widthwrap_contentに設定しないでください。getView()により、リストビューに描画されるビューの高さを測定するための子が取得されます。リストがスクロールされていなくてもconvertviewを返すなどの予期しない動作を引き起こす可能性があります。常にmatch_parentまたは固定幅/高さを使用します。

2layout_heightfill_parentに設定すると、リストビューと質問の後にいくつかのレイアウトまたはビューを使用したい場合、リストビューの後のビューが画面を下るときに表示されません。ので、リストビューをレイアウト内に配置することをお勧めします。たとえば、線形レイアウトで、要件に応じてそのレイアウトの高さと幅を設定し、heightおよびwidth属性を作成します。レイアウトの時点でのリストビューの(レイアウトの幅が2で高さが280)の場合、リストビューは同じheight。これは、レンダリングされるビューの正確な高さと幅をgetView()に伝え、getView()は何度か何度もランダムな行を呼び出さず、スクロールが発生しない前に変換ビューを返すなどの他の問題が発生します。私自身、リストビューがlineaLayout内にない限り、ビュー呼び出しとビューの変換を繰り返すなどの問題もあり、LinearLayout内にリストビューを置くことは私にとって魔法のように機能しました(理由はわかりませんでした)

01-01 14:49:36.606: I/System.out(13871): getview 0 null
01-01 14:49:36.636: I/System.out(13871): getview 0 Android.widget.RelativeLayout@406082c0
01-01 14:49:36.636: I/System.out(13871): getview 1 Android.widget.RelativeLayout@406082c0
01-01 14:49:36.646: I/System.out(13871): getview 2 Android.widget.RelativeLayout@406082c0
01-01 14:49:36.646: I/System.out(13871): getview 3 Android.widget.RelativeLayout@406082c0
01-01 14:49:36.656: I/System.out(13871): getview 4 Android.widget.RelativeLayout@406082c0
01-01 14:49:36.666: I/System.out(13871): getview 5 Android.widget.RelativeLayout@406082c0
01-01 14:49:36.666: I/System.out(13871): getview 0 Android.widget.RelativeLayout@406082c0
01-01 14:49:36.696: I/System.out(13871): getview 0 Android.widget.RelativeLayout@406082c0
01-01 14:49:36.706: I/System.out(13871): getview 1 null
01-01 14:49:36.736: I/System.out(13871): getview 2 null
01-01 14:49:36.756: I/System.out(13871): getview 3 null
01-01 14:49:36.776: I/System.out(13871): getview 4 null

しかし、今では解決しました、私は説明がそれほど得意ではありませんが、私は一日中理解するので、私のような他の初心者が私の経験の助けを得ることができると思ったので、あなたの人々が少し理解することを願っていますListViewフレームワークの仕組み。これは本当に乱雑で扱いにくいため、初心者は理解しづらい問題を見つけました。

318
Muhammad Babar

Holderオブジェクトに位置を設定する場合、Holderパターンでは注意してください。たとえば、毎回設定する必要があります。

@Override
public final View getView(int position, View view, ViewGroup parent) {
    Holder holder = null;
    if (view == null) {
        LayoutInflater inflater = (LayoutInflater) App.getContext()
                .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        view = inflater.inflate(getContainerView(), parent, false);
        holder = getHolder(position, view, parent);
        holder.setTag(tag);
        view.setTag(holder);
    } else {
        holder = (Holder) view.getTag();
    }
    holder.position = position;
    draw(holder);
    return holder.getView();
}

これは、抽象クラスの例です。

getHolder(position, view, parent);

のすべての設定操作を行います

ImageViews, TextViews, etc..
8

Holderパターンを使用すると、目的を達成できます。

このパターンの説明はここにあります:

画面を下にスクロールすると、リストビューのアイテムが非表示になったときに、リストビューのリサイクルが発生します。新しいリストビューアイテムを表示するために再利用されます。

2
Krishna

Recyclerviewは、動的ビューの追加を効率的な方法で、つまり既に作成されたビューを再利用することで管理する単なるラッパークラスです。

Recyclerviewアダプタを考えてみてください。メソッドを呼び出す順序は

  • getItemCount-これは最初に呼び出され、アダプターが作成または管理する必要があるビューの数を知るようにします。
  • getItemViewType-これは、どのタイプのビューを拡張する必要があるかを知るために次に呼び出されます。
  • onCreateViewHolder-これは、どのビューを拡大するかを知るために次に呼び出されます。
  • onBindViewHolder-このステップアダプターを使用すると、positionに関して膨張したビューを保持しているビューホルダーのインスタンスが提供されるため、ここでビューにデータを設定できます。
0
Shivam Yadav