ListViewのリサイクルメカニズムのしくみ


146

だから私は以前私が抱えていたこの問題を抱えており、当然私はここで助けを求めまし。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のように見え、さらに2を位置8に配置します。なぜこれが起こっているのか理解できません。私がこの質問をしている主な理由の1つは、おそらくListViewとGridViewのリサイクルビュー、またはこの記事でのScrapViewメカニズムのことを知らない人を助けるためです。

ここに画像の説明を入力してください

後で編集

リストビューの仕組みを理解するために基本的に必要なのは、Google IOトークへのリンクの追加です。リンクはコメントの上で死んでいた。したがって、user3427079はそのリンクを更新するのに十分なほど優れていました。ここでは、それは容易なアクセスのためです。


ハハ、そのリンクをありがとう。今すぐ視聴し始めました:)
Andy

前の質問のコードサンプルを完全に実装していません。アイデアは、他の行に対して行った変更(あなたの場合は位置0)を元に戻すためのコードを常に用意する必要があるということです。
Luksprog、2012

まあ、私はその@Luksprogを理解していますが、それがなぜビューが他の位置にも配置されるのかを説明していますか?どちらかと言えば、位置0だけが重複するビューを持っていると思っていました。Idk、それでもListViewがどのようにこれをバインドしているかを理解するのに問題があります:/
Andy

1
リサイクルとは、画面から消えたばかりの行ビュー(たとえば、1行下にスクロールした後の位置0)ListViewが、新しい行を表示する必要があるときに(下にスクロールし続ける/上にスクロールするなど)後で使用できることを意味します。問題は、消えたばかりのビューと将来必要な位置で使用されるビューがTextViewすでに追加されていることです。これが問題です。それを解決するにはgetViewgetViewメソッドが以外の位置で呼び出された場合、メソッドでそれを削除する必要があります0
Luksprog 2012

2
トップポストのリンクは死んでいる、これはそれだと思いますか?youtube.com/watch?v=N6YdwzAvwOA
G_V 2014年

回答:


330

最初はリストビューのリサイクルと変換ビューの使用メカニズムにも気づいていませんでしたが、丸一日の調査の結果、android.amberfogの画像を参照してリストビューのメカニズムをかなり理解しました。 ここに画像の説明を入力してください

リストビューがアダプターでいっぱいになると、基本的にリストビューが画面に表示できる数が表示され、リストをスクロールしても行数は増えません。これは、リストビューがより効率的かつ高速に機能するように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が画面の外に移動すると、アイテムは現在の状態でリサイクラーに送信されます(たとえば、TextView 'text'または私の場合、チェックボックスがオンの場合、ビューに関連付けられます。リサイクル業者に保管されています)。

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

重要なこと

。設定されたことがないlayout_heightlayout_widthのあなたのリストビューのwrap_contentようgetView()リストビューに描画されるとさえリストconvertviewを返すようないくつかの予期しない動作を引き起こす可能性がありますビューの高さを測定するためのいくつかの子供を得るためにあなたのアダプタを強制的に使用をscrolled.alwaysされていないmatch_parentか、固定幅高さ。

。リストビューの後にレイアウトまたはビューを使用したい場合は、リストビューの後にビューを表示すると画面が下がっても表示されないlayout_heightためfill_parent、質問が頭に浮かぶ可能性があるため、リストビューをレイアウト:たとえば、線形レイアウトを作成し、必要に応じてそのレイアウトの高さと幅を設定し、リストビューの高さ幅の属性をレイアウトのものにします(レイアウトの幅が320で高さが280の場合など)、リストビュー高さは同じである必要があります。これにより、レンダリングされるビューの正確な高さと幅が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

でも今は解決しました、私は説明があまり得意ではありませんが、私の一日をかけて理解したので、私のような他の初心者が私の経験を助けてくれると思いました。リストビュー、それがどのように動作するか、フレームワーク、それは本当に厄介かつトリッキーなので、初心者はそれを理解しすぎて問題を発見されたとして、


36
「私は説明があまり得意ではないことを知っています」>>>この回答に対する+37票は、説明で優れた仕事をしたことを示しています。あなたの知識を共有し続けてください!;)
2Dee、2014

1
すばらしい回答raja ji +1 .. :)
Ehsan Sajjad 14年

2
画像ありがとうございます。Androidを使い始めたとき、ListViewで多くのトラブルを経験した後、私はリサイクルにかなり苦労した後、リサイクルの原則をすでに知っていました。それでも、それはこの写真と文章を撮りました:if you had a checkbox and if you check it at position 0(let say item1 has also a checkbox and you checked it) so when you scroll down you will see item 8 checkbox is already checked,this is why listview is re using the same view not creating a new for you due to performance optimization.私が私の間違いを理解するために。私が遭遇したAndroid ListViewリサイクルに関する最高の投稿!
Kevin Cruijssen、2014

1
@MuhammadBabarはい、質問を投稿しました:stackoverflow.com/q/30122027/1318946
Pratik Butani

1
@MuhammadBabar素晴らしい説明男!時間を節約できます。知りたいだけですが、RecyclerViewも同じメカニズムで機能しますか?私の質問stackoverflow.com/questions/47897770/…で、私はタスクがで静かに不可能であることを知っていますlistView。私は試してみるべきrecyclerViewですか?
Shambhu

8

ホルダーパターンで、ホルダーオブジェクトに位置を設定する場合は、毎回設定する必要があります。たとえば、

@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..

1
この間ずっと私がしていることに気づかなかった。parent.setTag(holder);正しい方法の代わりにview.setTag(holder);
ArtiomLK

2

ホルダーパターンを使用すると、目的の結果を得ることができます。

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

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

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.