Bagaimana mekanisme daur ulang ListView bekerja

146

Jadi saya punya masalah ini sebelumnya, dan tentu saja saya meminta bantuan di sini . Jawaban Luksprog luar biasa karena saya tidak tahu tentang bagaimana ListView dan GridView dioptimalkan sendiri dengan daur ulang Tampilan. Jadi dengan sarannya saya bisa mengubah cara saya menambahkan Tampilan ke GridView saya. Masalahnya sekarang saya punya sesuatu yang tidak masuk akal. Ini milik getViewsaya 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;
    }

}

Masalahnya adalah ketika saya menggulir, ini terjadi, dan bukan pada posisi 0 ... Sepertinya posisi 6 dan posisi 8, ditambah menempatkan dua di posisi 8. Sekarang saya masih mencoba untuk menggunakan ListView dan GridView jadi saya lakukan tidak mengerti mengapa ini terjadi. Salah satu alasan utama saya mengajukan pertanyaan ini adalah untuk membantu orang lain yang mungkin tidak tahu tentang daur ulang ListView dan GridView, atau cara artikel ini mengatakannya, mekanisme ScrapView.

masukkan deskripsi gambar di sini

Kemudian Edit

Menambahkan tautan ke Google IO talk yang pada dasarnya adalah semua yang Anda butuhkan untuk memahami cara kerja ListView. Tautan mati di salah satu komentar. Jadi user3427079 cukup bagus untuk memperbarui tautan itu. Ini untuk akses mudah.

Andy
sumber
Haha, terima kasih untuk tautannya. Mulai menontonnya sekarang :)
Andy
Anda tidak sepenuhnya menerapkan contoh kode dari pertanyaan sebelumnya. Idenya adalah bahwa Anda selalu perlu memiliki sepotong kode untuk mengembalikan perubahan yang Anda buat untuk baris lain (dalam kasus Anda posisi 0).
Luksprog
Yah saya mengerti bahwa @Luksprog Tapi apakah itu menjelaskan mengapa ini juga menempatkan View di posisi lain? Jika ada, saya hanya mengharapkan posisi 0 untuk memiliki Tampilan duplikat. Idk, masih kesulitan memahami bagaimana ListView mengikat hal ini: /
Andy
1
Daur ulang berarti bahwa tampilan baris yang baru saja menghilang (misalnya posisi 0, setelah menggulir satu baris ke bawah) dari layar dapat digunakan nanti ketika ListViewbaris baru perlu ditampilkan (seperti melanjutkan untuk menggulir ke bawah / atas). Masalahnya adalah bahwa pandangan yang baru saja menghilang dan yang akan digunakan pada posisi yang dibutuhkan di masa depan TextViewtelah ditambahkan ke dalamnya, inilah masalahnya. Untuk mengatasinya Anda harus menghapusnya dalam getViewmetode jika getViewmetode dipanggil untuk posisi lain selain 0.
Luksprog
2
Tautan posting atas sudah mati, saya pikir ini dia? youtube.com/watch?v=N6YdwzAvwOA
G_V

Jawaban:

330

Awalnya, saya juga tidak mengetahui daur ulang listview dan mekanisme penggunaan convertview, tetapi setelah penelitian sepanjang hari saya cukup banyak memahami mekanisme tampilan daftar dengan mengacu pada gambar dari android.amberfog masukkan deskripsi gambar di sini

Setiap kali tampilan daftar Anda diisi oleh adaptor, pada dasarnya akan menunjukkan jumlah baris yang dapat ditampilkan pada layar dan jumlah baris tidak bertambah bahkan ketika Anda menggulir daftar. Ini adalah trik yang digunakan android agar listview bekerja lebih efisien dan cepat. Sekarang kisah di dalam tampilan daftar yang mengacu pada gambar, seperti yang Anda lihat, awalnya tampilan daftar memiliki 7 item yang terlihat, kemudian, jika Anda menggulir ke atas hingga item 1 tidak lagi terlihat, getView () melewati tampilan ini (yaitu item1) ke pendaur ulang dan Anda dapat menggunakan

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

di dalam

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;
    }

Anda akan melihat di logcat Anda, pada awalnya, convertview adalah null untuk semua baris yang terlihat, karena awalnya tidak ada tampilan (yaitu item) di pendaur ulang, sehingga getView Anda () membuat tampilan baru untuk setiap item yang terlihat, tetapi saat Anda menggulir ke atas dan item 1 bergerak keluar dari layar, item itu akan dikirim ke Recycler dengan statusnya yang sekarang (misalnya 'teks' TextView atau dalam case mine, jika kotak centang dicentang, itu akan dikaitkan dengan tampilan dan disimpan dalam pendaur ulang).

Sekarang ketika Anda menggulir ke atas / bawah, tampilan daftar Anda tidak akan membuat tampilan baru, itu akan menggunakan tampilan yang ada di pendaur ulang Anda. Di Logcat Anda, Anda akan melihat bahwa 'convertView' bukan nol, karena item baru 8 Anda akan ditarik menggunakan convertview, yaitu, pada dasarnya ia mengambil tampilan item 1 dari pendaur ulang dan mengembang item 8 di tempatnya, dan Anda dapat mengamati itu dalam kode saya. Jika Anda memiliki kotak centang dan jika Anda memeriksanya di posisi 0 (katakanlah item1 memiliki kotak centang dan Anda mencentangnya) sehingga ketika Anda menggulir ke bawah Anda akan melihat kotak centang item 8 sudah dicentang, inilah sebabnya mengapa tampilan daftar menggunakan kembali tampilan yang sama, tidak membuat yang baru untuk Anda karena optimasi kinerja.

Hal-hal penting

1 . Jangan pernah mengatur layout_heightdan layout_widthdari tampilan daftar Anda wrap_contentkarena getView()akan memaksa adaptor Anda untuk membuat beberapa anak untuk mengukur ketinggian tampilan yang akan ditarik dalam tampilan daftar dan dapat menyebabkan beberapa perilaku yang tidak terduga seperti mengembalikan konversi bahkan daftar tidak digulir. Selalu menggunakan match_parentatau memperbaiki lebar tinggi.

2 . Jika Anda ingin menggunakan beberapa Tata Letak atau melihat setelah tampilan daftar dan pertanyaan kekuatan datang dalam pikiran Anda jika saya mengatur layout_heightuntuk fill_parenttampilan setelah tampilan daftar tidak akan muncul sebagai kelanjutannya bawah layar, jadi lebih baik untuk menempatkan ListView Anda di dalam tata letak. Misalnya Tata Letak Linear dan mengatur tinggi dan lebar tata letak itu sesuai kebutuhan Anda dan membuat atribut tinggi dan lebar tampilan daftar Anda sebagai tata letak Anda (seperti jika lebar tata letak Anda adalah 320 dan tinggi adalah 280) maka tampilan daftar Anda harus memiliki tinggi dan lebar yang sama. Ini akan memberi tahu getView () tinggi dan lebar tampilan yang tepat untuk dirender, dan getView () tidak akan memanggil lagi dan lagi beberapa baris acak, dan masalah lain seperti mengembalikan tampilan konversi bahkan sebelum pengguliran tidak terjadi, saya punya tes ini sendiri, kecuali listview saya ada di dalam lineaLayout itu juga mengalami masalah seperti mengulangi panggilan panggilan dan mengubah tampilan sebagai, menempatkan Listview di dalam LinearLayout bekerja seperti sulap untuk saya. (tidak tahu mengapa)

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

Tapi sekarang ini sudah terpecahkan, saya tahu, saya tidak begitu bagus dalam menjelaskan tetapi karena saya menaruh seluruh hari saya untuk mengerti, jadi saya pikir pemula lain seperti saya bisa mendapatkan bantuan dari pengalaman saya dan saya harap sekarang Anda orang akan memiliki sedikit pemahaman dari ListView kerangka cara kerjanya, karena benar-benar berantakan dan rumit pemula jadi menemukan terlalu banyak masalah pemahaman itu

Muhammad Babar
sumber
36
"Aku tahu aku tidak begitu pandai menjelaskan" >>> +37 suara pada jawaban ini memberitahuku kamu melakukan pekerjaan yang sangat baik dalam menjelaskan. Silakan terus berbagi pengetahuan Anda! ;)
2Dee
1
Jawaban bagus raja ji +1 dari saya .. :)
Ehsan Sajjad
2
Terima kasih atas Gambarnya. Setelah mengalami banyak masalah dengan ListView ketika saya mulai dengan Android, saya sudah tahu prinsip daur ulang setelah banyak berjuang. Tetap saja, ini mengambil gambar dan kalimat: 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.bagi saya untuk menyadari kesalahan saya. Posting terbaik tentang daur ulang ListView Android yang pernah saya jumpai!
Kevin Cruijssen
1
@MuhammadBabar Ya, saya telah memposting Pertanyaan: stackoverflow.com/q/30122027/1318946
Pratik Butani
1
@MuhammadBabar penjelasan pria yang luar biasa! Ini menghemat waktu saya. Hanya ingin tahu, apakah RecyclerViewjuga bekerja pada mekanisme yang sama? Dalam pertanyaan saya stackoverflow.com/questions/47897770/… , saya tahu tugas itu tidak mungkin dilakukan listView. Haruskah saya coba recyclerView?
Shambhu
8

Hati-hati, dalam pola Pemegang, jika Anda mengatur posisi pada objek Pemegang Anda, Anda harus mengaturnya setiap waktu, misalnya:

@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();
}

ini adalah contoh dari kelas abstrak, di mana

getHolder(position, view, parent);

melakukan semua operasi pengaturan untuk

ImageViews, TextViews, etc..
Ahmed Adel Ismail
sumber
1
Tidak menyadari bahwa selama ini saya lakukan. parent.setTag(holder);alih-alih dengan cara yang benar,view.setTag(holder);
ArtiomLK
2

Menggunakan pola Pemegang Anda dapat mencapai apa yang Anda inginkan:

Anda dapat menemukan deskripsi pola ini di sini:

Daur ulang tampilan daftar terjadi ketika Anda menggulir layar ke bawah dan item tampilan daftar di atas disembunyikan. Mereka digunakan kembali untuk menampilkan item tampilan daftar baru.

Krishna
sumber