convertViews 是否在 ListView 中被重用?

Are convertViews being reused in a ListView?

我正在使用此代码来布局我的 ListView,使用基于某些数据的不同布局:

@Override
public View getView(int i, View convertView, ViewGroup viewGroup) {
    ViewHolder viewHolder;

    MyInfo myInfo = getItem(i);
    String label = myInfo.getLabel();

    if (convertView == null) {
        if (!"".equals(label)) {
            convertView = LayoutInflater.from(getContext()).inflate(R.layout.info_grey, null);
            Log.d(SapphireApplication.TAG, "GREY, label=" + label);
        } else {
            convertView = LayoutInflater.from(getContext()).inflate(R.layout.info_plain, null);
            Log.d(SapphireApplication.TAG, "PLAIN, label=" + label);
        }

        viewHolder = new ViewHolder();
        viewHolder.tvLabel = convertView.findViewById(R.id.tvLabel);

        convertView.setTag(viewHolder);
    } else {
        viewHolder = (ViewHolder)convertView.getTag();
    }

    viewHolder.tvLabel.setText(label);

    return convertView;
}

但是,Log.d 从未对列表中的某些项目完成。这是否意味着 Android 重新使用现有的 convertView,导致它(在本例中)使用错误的布局?

是的。它们正在被重新使用。这就是您只看到少数项目的消息日志的原因。

@Override
public View getView(int i, View convertView, ViewGroup viewGroup) {

    if (convertView == null) {
        // convertView is null. It means the ListView does not have a view to give to you
        // This way, you need to create a new one.
        // You will enter here until the ListView has enough Views to fill
        // the screen.
        // So, just inflate the view and set the View holder here.
        // Don't customize your view here (set text, contenet etc)
        // So, any log message here will be printed only when the ListView becomes visible (and when you scroll to next item)
        // After that, views will be re-used so convertView will no longer be null
    } else {
        // ListView gave a convertView to you. It means that you are receiving a View
        // that was created in the past and it is be re-used now.
        // At this moment, convertView still has the content of the old item it was 
        // representing.
        // This view was created in the statement above and after user scrolled the ListView
        // it becomes hidden and ready to be re-used.
        // Don't customize the view here.. just get the ViewHolder from the View
    }

    // Here you customize the View. Set content, text, color, background etc
    // The ViewHolder is just a helpful class to help you to access
    // all View inside the convertView without needing to perform the
    // findViewById again.

    return convertView;
}

然而,在这个基本示例中,所有 convertViews 都是相似的。它们是从相同的布局中充气的。 这适用于每行只有一个视图类型的情况。所有项目都相似但内容不同。 如果差异很小,这仍然有效。例如,您可以膨胀相同的布局并根据位置控制其某些 Views 的可见性(位置 1 有图像而位置 2 没有)。

但是,在某些情况下,您确实需要为每行填充不同的布局。 例如,"grey" 布局与 "plain" 布局有很大不同。在这种情况下,您需要更新您 代码如下:

private static final int GREY = 1;
private static final int PLAIN = 2;
private static final int TOTAL_VIEW_TYPES = 2; // Grey and Plain

@Override
public int getViewTypeCount() {
    // Tell the list view that you have two types of Views (Grey and plain)
    return TOTAL_VIEW_TYPES;
}

@Override
public int getItemViewType(int position) {
    // You must inform view type for given position
    String label = myInfo.getLabel();
    if (!"".equals(label)) {
        return GREY; 
    } else {
        return PLAIN;
    }
}

    @Override
public View getView(int position, View convertView, ViewGroup viewGroup) {

    if (convertView == null) {
        // If view is null, you must create the view. But you need to create the
        // correct view for given position
        if(getItemViewType(position) == GREY) {
            // Inflate grey;
        } else { 
            // inflate plain
        }
    } else {
        // convertView is not null. It is being reused.
        // Android will give you the proper view here. If you are expecting
        // a plain type, that's what you will get. Android won't re-use
        // plain layout where you are expecting to have the grey layout.
        // It will re-use the proper view for each position (following to the getItemViewType()).
        // ListView is very robust.
    }

    // Update the view here.. Just remember that here you may have two different
    // types of view.. grey or plain.
    return convertView;
}

这些概念最酷的地方在于它们对使用此视图<->适配器关系的任何视图都有效。 RecyclerViewListViewSpinnerPagerView