1. ホーム
  2. android

[解決済み] ListViewのリサイクル機構の仕組み

2022-06-26 21:02:37

質問

というわけで、以前にもあったこの問題が発生し、当然ながら私は ここで . Luksprogの回答は、ListViewとGridViewがどのようにViewをリサイクルしながら最適化されるかについて全く知らなかったので、素晴らしいものでした。そのため、彼のアドバイスにより、GridViewにViewを追加する方法を変更することができました。問題は、今、私は意味をなさない何かを持っているということです。これは私の 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を使うコツを掴もうとしているので、なぜこれが起こるのか理解できません。この質問をする主な理由の一つは、おそらくListViewとGridViewのリサイクルビューについて知らない他の人を助けるため、または、この方法 記事 は、ScrapViewメカニズムについて説明しています。

後編集

ListViewの仕組みを理解するために基本的に必要なgoogle IO talkへのリンクを追加しました。リンクはコメント欄で切れていました。そこで、user3427079さんがリンクを更新してくれました。 ここに で、簡単にアクセスできます。

どのように解決するのですか?

当初は私もリストビューのリサイクルやconvertviewの使用メカニズムについて知りませんでしたが、丸一日かけて調べた結果、以下の画像を参照することでリストビューのメカニズムがほぼ理解できました。 android.amberfog

リストビューがアダプタによって満たされるときはいつでも、基本的には、リストビューに表示される 行数 を表示し、リストをスクロールしても行数は増えません。これは android が listview をより効率的かつ高速に動作させるために使用しているトリックです。 さて、画像を参照しながらリストビューの内部を説明すると、ご覧の通り、リストビューは最初は7つのアイテムが表示されています。そして、アイテム1が見えなくなるまでスクロールアップすると、 getView() はこのビュー(つまりアイテム1)をリサイクラーに渡して

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 を見るとわかりますが、最初は、見える行すべてに対して convertview は null です。これは、最初はリサイクラーにビュー(つまりアイテム)がなかったためで、getView() は見えるアイテムごとに新しいビューを作成しますが、上にスクロールしてアイテム 1 が画面の外に出た瞬間に、それは Recycler に現在の状態(例えばTextView 'text'、私の場合はcheckboxがチェックされていれば、そのビューと関連付けてrecyclerに格納されます)で格納されます。

これで、上下にスクロールするとき、リストビューは新しいビューを作成するのではなく、リサイクラーにあるビューを使用するようになります。あなたの ログキャット で、'convertView' が null でないことに気がつくと思いますが、これは新しいアイテム 8 が convertview を使って描画されるからです。つまり、基本的にはアイテム 1 のビューをリサイクラーから取得して、代わりにアイテム 8 を展開するのですが、私のコードでそれを確認できます。もしあなたがチェックボックスを持っていて、位置0にそれをチェックした場合(item1にチェックボックスがあり、あなたがそれをチェックしたとします)、あなたがスクロールダウンするとき、アイテム8のチェックボックスがすでにチェックされているのが見えるでしょう、これはlistviewが同じビューを再使用し、パフォーマンスの最適化のために新しいものを作らない理由です。

重要なこと

1 . 決して layout_heightlayout_width をリストビューの wrap_content として getView() を使用すると、リストビューに描画されるビューの高さを測定するための子プロセスをアダプタに取得させることになり、リストがスクロールされていないのに convertview を返すなど、予期せぬ振る舞いをする可能性があります。 <を使用します。 を使用するか、固定幅/高さを使用します。

2 . リストビューの後にレイアウトやビューを使いたい場合、リストビューの後に match_parentlayout_height リストビューの後のビューは画面の下に行くほど表示されないので、リストビューをレイアウトの中に置くとよいでしょう。 高さ そして 属性をレイアウトの幅に合わせます(例えば、レイアウトの幅が 320 で、高さが 280) であれば、リストビューには同じ 高さ . これは、レンダリングされるビューの正確な高さと幅のgetView()を教えて、getView()が何度も何度もいくつかのランダムな行を呼び出すことはありませんし、スクロールする前でも変換ビューを返すなどの他の問題は発生しません、私は私のリストビューがlineaLayout内部にない限り、それはまたとしてビュー呼び出しと変換ビューを繰り返すなどの問題を抱えて、LinearLayout内部のリストビューを入れて私のための魔法のように働いていました(理由は分かりませんでした)これを自分でテストしている。

fill_parent

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