Cơ chế tái chế của ListView hoạt động như thế nào


146

Vì vậy, tôi có vấn đề này tôi đã có trước đây, và tự nhiên tôi đã yêu cầu giúp đỡ ở đây . Câu trả lời của Luksprog rất hay vì tôi không biết về cách ListView và GridView tự tối ưu hóa với Chế độ xem tái chế. Vì vậy, với lời khuyên của anh ấy, tôi đã có thể thay đổi cách tôi thêm Chế độ xem vào GridView của mình. Vấn đề là bây giờ tôi có một cái gì đó không có ý nghĩa. Đây là của tôi getViewtừ 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;
    }

}

Vấn đề là khi tôi cuộn, điều này xảy ra, chứ không phải ở vị trí 0 ... Có vẻ như vị trí 6 và vị trí 8, cộng với nó đặt hai vị trí 8. Bây giờ tôi vẫn đang cố gắng sử dụng ListView và GridView. không hiểu tại sao điều này lại xảy ra Một trong những lý do chính mà tôi đưa ra câu hỏi này là để giúp những người khác có thể không biết về Chế độ xem tái chế của ListView và GridView, hoặc cách bài viết này đưa ra, cơ chế ScrapView.

nhập mô tả hình ảnh ở đây

Chỉnh sửa sau

Thêm liên kết vào một cuộc trò chuyện Google IO về cơ bản là tất cả những gì bạn cần để hiểu cách ListView hoạt động. Liên kết đã chết trong các ý kiến. Vì vậy, user3427079 đã đủ tốt để cập nhật liên kết đó. Đây là để dễ dàng truy cập.


Haha, cảm ơn vì liên kết đó Bắt đầu xem ngay bây giờ :)
Andy

Bạn đã không thực hiện đầy đủ mẫu mã từ câu hỏi trước. Ý tưởng là bạn luôn cần có một đoạn mã để hoàn nguyên các thay đổi bạn đã thực hiện cho các hàng khác (trong trường hợp của bạn là vị trí 0).
Luksprog

Vâng, tôi hiểu rằng @Luksprog Nhưng điều đó có giải thích tại sao nó cũng đặt Chế độ xem ở các vị trí khác không? Nếu có bất cứ điều gì, tôi chỉ mong đợi vị trí 0 có Lượt xem trùng lặp. Idk, vẫn gặp khó khăn khi hiểu cách ListView ràng buộc công cụ này: /
Andy

1
Tái chế có nghĩa là chế độ xem hàng vừa biến mất (ví dụ vị trí 0, sau khi cuộn một hàng xuống) từ màn hình có thể được sử dụng sau này khi ListViewcần một hàng mới để hiển thị (như tiếp tục cuộn xuống / lên). Vấn đề là quan điểm vừa biến mất và sẽ được sử dụng tại các vị trí cần thiết trong tương lai đã TextViewđược thêm vào nó, đây là vấn đề. Để giải quyết nó, bạn phải loại bỏ nó trong getViewphương thức nếu getViewphương thức được gọi cho bất kỳ vị trí nào khác ngoài 0.
Luksprog

2
Liên kết bài viết hàng đầu đã chết, tôi nghĩ rằng đây là nó? youtube.com/watch?v=N6YdwzAvwOA
G_V

Câu trả lời:


330

Ban đầu, tôi cũng không biết về tái chế listview và cơ chế sử dụng convertview, nhưng sau một ngày nghiên cứu, tôi hiểu khá rõ các cơ chế của chế độ xem danh sách bằng cách tham khảo một hình ảnh từ android.amberfog nhập mô tả hình ảnh ở đây

Bất cứ khi nào listview của bạn được lấp đầy bởi một bộ chuyển đổi, về cơ bản nó sẽ hiển thị số lượng Hàng mà listview có thể hiển thị trên màn hình và số lượng hàng không tăng ngay cả khi bạn cuộn qua danh sách. Đây là mẹo mà android sử dụng để listview hoạt động hiệu quả và nhanh chóng hơn. Bây giờ câu chuyện bên trong của listview đề cập đến hình ảnh, như bạn có thể thấy, ban đầu listview có 7 mục hiển thị, sau đó, nếu bạn cuộn lên cho đến khi mục 1 không còn hiển thị nữa, getView () chuyển chế độ xem này (tức là mục 1) sang tái chế và bạn có thể sử dụng

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

bên trong của bạn

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

Bạn sẽ nhận thấy trong logcat của mình, ban đầu, convertview là null cho tất cả các hàng hiển thị, vì ban đầu không có chế độ xem (tức là các mục) trong trình tái chế, do đó getView () của bạn tạo một chế độ xem mới cho từng mục hiển thị, nhưng Khi bạn cuộn lên và mục 1 di chuyển ra khỏi màn hình, nó sẽ được gửi đến Recycler với trạng thái hiện tại (ví dụ: TextView 'text' hoặc trong trường hợp của tôi, nếu hộp kiểm được chọn, nó sẽ được liên kết với chế độ xem và lưu trữ trong tái chế).

Bây giờ khi bạn cuộn lên / xuống, listview của bạn sẽ không tạo chế độ xem mới, nó sẽ sử dụng chế độ xem trong trình tái chế của bạn. Trong Logcat của bạn, bạn sẽ nhận thấy rằng 'convertView' không phải là null, bởi vì mục 8 mới của bạn sẽ được vẽ bằng convertview, tức là về cơ bản, nó sẽ xem mục 1 từ trình tái chế và thổi phồng mục 8 vào vị trí của nó và bạn có thể quan sát trong mã của tôi Nếu bạn có một hộp kiểm và nếu bạn kiểm tra nó ở vị trí 0 (giả sử item1 có một hộp kiểm và bạn đã kiểm tra nó) thì khi bạn cuộn xuống, bạn sẽ thấy hộp kiểm mục 8 đã được chọn, đây là lý do tại sao listview lại sử dụng cùng một chế độ xem, không tạo ra một cái mới cho bạn do tối ưu hóa hiệu suất.

Những thứ quan trọng

1 . Không bao giờ đặt các layout_heightlayout_widthcủa listview của bạn để wrap_contentnhư getView()sẽ buộc adapter của bạn để có được một số đứa trẻ để đo chiều cao của các quan điểm được rút ra theo quan điểm của danh sách và có thể gây ra một số hành vi bất ngờ như trở convertview ngay cả những danh sách không phải là scrolled.always sử dụng match_parenthoặc cố định chiều rộng / chiều cao.

2 . Nếu bạn muốn sử dụng một số Layout hoặc xem sau khi xem danh sách và câu hỏi sức mạnh đến trong tâm trí của bạn nếu tôi đặt layout_heightđể fill_parentquan điểm sau khi xem danh sách sẽ không hiển thị như nó đi xuống màn hình, vì vậy tốt hơn của mình để đưa listview của bạn bên trong một layout. Ví dụ: Bố cục tuyến tính và đặt chiều cao và chiều rộng của bố cục đó theo yêu cầu của bạn và đặt thuộc tính chiều caochiều rộng của listview thành bố cục của bạn (như nếu chiều rộng bố cục của bạn là 320 và chiều cao là 280) thì listview của bạn nên có cùng chiều caochiều rộng. Điều này sẽ cho getView () chiều cao và chiều rộng chính xác của chế độ xem được hiển thị và getView () sẽ không gọi đi gọi lại một số hàng ngẫu nhiên và các vấn đề khác như quay lại chế độ xem chuyển đổi ngay cả trước khi cuộn không xảy ra, tôi đã kiểm tra Bản thân tôi, trừ khi listview của tôi nằm trong lineaLayout, nó cũng gặp vấn đề như lặp lại cuộc gọi xem và chuyển đổi chế độ xem, đưa Listview vào trong linearLayout hoạt động như ma thuật đối với tôi. (không biết tại sao)

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

Nhưng bây giờ nó đã được giải quyết, tôi biết, tôi không giỏi giải thích nhưng vì tôi đã dành cả ngày để hiểu nên tôi nghĩ những người mới bắt đầu như tôi có thể giúp tôi trải nghiệm và tôi hy vọng bây giờ mọi người sẽ hiểu một chút của ListView framework hoạt động như thế nào, vì nó thực sự lộn xộn và phức tạp nên những người mới bắt đầu thấy quá nhiều vấn đề hiểu nó


36
"Tôi biết tôi không giỏi giải thích" >>> Phiếu bầu +37 cho câu trả lời này cho tôi biết bạn đã làm một công việc tuyệt vời để giải thích. Xin hãy tiếp tục chia sẻ kiến ​​thức của bạn! ;)
2Dee

1
Câu trả lời tuyệt vời raja ji +1 từ tôi .. :)
Ehsan Sajjad

2
Cảm ơn bạn cho hình ảnh. Sau khi gặp nhiều rắc rối với ListView khi tôi bắt đầu với Android, tôi đã biết nguyên tắc tái chế sau khi phải vật lộn với nó rất nhiều. Tuy nhiên, nó đã chụp bức ảnh này và câu: 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.để tôi nhận ra sai lầm của mình. Bài đăng hay nhất về tái chế Android ListView tôi đã xem qua!
Kevin Cruijssen

1
@MuhammadBabar Vâng, tôi đã đăng Câu hỏi: stackoverflow.com/q/30122027/1318946
Pratik Butani

1
@MuhammadBabar người đàn ông giải thích tuyệt vời! Nó tiết kiệm thời gian của tôi. Chỉ muốn biết, có phải RecyclerViewcũng làm việc trên cùng một cơ chế? Trong câu hỏi stackoverflow.com/questions/47897770/ của tôi, tôi biết nhiệm vụ là không thể yên tĩnh với listView. Có nên thử với recyclerView?
Shambhu

8

Hãy cẩn thận, trong mẫu Chủ sở hữu, nếu bạn đặt tư thế trong đối tượng Chủ sở hữu của mình, bạn nên đặt nó mỗi lần, ví dụ:

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

đây là một ví dụ từ một lớp trừu tượng, trong đó

getHolder(position, view, parent);

thực hiện tất cả các hoạt động cài đặt cho

ImageViews, TextViews, etc..

1
Tôi đã không nhận thấy rằng tất cả thời gian này tôi đang làm. parent.setTag(holder);thay vì cách chính xác,view.setTag(holder);
ArtiomLK

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.