【android】Adapter 的 convertView 复用浅析

本文深入探讨了ListView中Adapter的convertView复用机制,通过分析源码,揭示了如何利用convertView避免OOM,以及AbsListView如何通过mActiveViews和mScrapViews管理convertView的回收。了解这些可以有效优化ListView性能。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

目录

  • 操作环境
  • 导读
  • 参数说明
  • 源码浅析

操作环境

  • 操作系统: win7-64bit 旗舰版
  • android 版本: android-23

导读

平时在使用 ListView 或 GridView 时,常需要自己复写 Adapter 的 View getView(int position, View convertView, ViewGroup parent) 方法,但对其中的 convertView 却不太了解,没法做到物尽其用;殊不知其用途可不小,可以在一定程度上避免 OOM

参数说明

  • 在 Adapter.java 中,对参数 convertView 的说明如下:

    • 说明: convertView 是一个可重用的 View,当然,也有可能是 Null
    • 良好的习惯是,在使用之前都检测一下;
    /**
     * Get a View that displays the data at the specified position in the data set. You can either
     * create a View manually or inflate it from an XML layout file. When the View is inflated, the
     * parent View (GridView, ListView...) will apply default layout parameters unless you use
     * {@link android.view.LayoutInflater#inflate(int, android.view.ViewGroup, boolean)}
     * to specify a root view and to prevent attachment to the root.
     * 
     * @param position The position of the item within the adapter's data set of the item whose view
     *        we want.
     * @param convertView The old view to reuse, if possible. Note: You should check that this view
     *        is non-null and of an appropriate type before using. If it is not possible to convert
     *        this view to display the correct data, this method can create a new view.
     *        Heterogeneous lists can specify their number of view types, so that this View is
     *        always of the right type (see {@link #getViewTypeCount()} and
     *        {@link #getItemViewType(int)}).
     * @param parent The parent that this view will eventually be attached to
     * @return
     */
    View getView(int position, View convertView, ViewGroup parent);

源码浅析

  • 再来看 AbsListView.java 的 obtainView 函数:
    • 说明:该函数的主要作用之一就是对 convertView 的复用;
    • AbsListView 维持:View[] mActiveViewsArrayList mScrapViews 来对 convertView 进行回收管理的;
    • mScrapViews 主要用于存放不在 ListView 屏幕内那些 convertView,其内部的 convertView 的排列是没有一定顺序的,使用的时候需留意;
    • mActiveViews 则专门用于保存在 ListView 中显示的 convertView;
    • 对于具有临时状态的 convertView,那么该 View 将不被纳入回收操作;
    • 对于类型为 ITEM_VIEW_TYPE_HEADER_OR_FOOTER 的视图,也是不进行回收操作的;
    • 对于其他的 convertView:首先通过 getScrapView( position ) 从 mScrapViews 中查询( 如果不存在,则需由之后的 getView 创建;如果存在,则将其返回,实现View的复用查询操作 );在调用 getView 函数后,如果得到一个新的 convertView,那么之前的 converView 将被继续保留在 mScrapViews 中,等待下一次根据 position 的查询;
    /**
     * Get a view and have it show the data associated with the specified
     * position. This is called when we have already discovered that the view is
     * not available for reuse in the recycle bin. The only choices left are
     * converting an old view or making a new one.
     *
     * @param position The position to display
     * @param isScrap Array of at least 1 boolean, the first entry will become true if
     *                the returned view was taken from the scrap heap, false if otherwise.
     *
     * @return A view displaying the data associated with the specified position
     */
    View obtainView(int position, boolean[] isScrap) {
        Trace.traceBegin(Trace.TRACE_TAG_VIEW, "obtainView");

        isScrap[0] = false;

        // Check whether we have a transient state view. Attempt to re-bind the
        // data and discard the view if we fail.
        final View transientView = mRecycler.getTransientStateView(position);
        if (transientView != null) {
            final LayoutParams params = (LayoutParams) transientView.getLayoutParams();

            // If the view type hasn't changed, attempt to re-bind the data.
            if (params.viewType == mAdapter.getItemViewType(position)) {
                final View updatedView = mAdapter.getView(position, transientView, this);

                // If we failed to re-bind the data, scrap the obtained view.
                if (updatedView != transientView) {
                    setItemViewLayoutParams(updatedView, position);
                    mRecycler.addScrapView(updatedView, position);
                }
            }

            isScrap[0] = true;

            // Finish the temporary detach started in addScrapView().
            transientView.dispatchFinishTemporaryDetach();
            return transientView;
        }

        final View scrapView = mRecycler.getScrapView(position);
        final View child = mAdapter.getView(position, scrapView, this);
        if (scrapView != null) {
            if (child != scrapView) {
                // Failed to re-bind the data, return scrap to the heap.
                mRecycler.addScrapView(scrapView, position);
            } else {
                isScrap[0] = true;

                // Finish the temporary detach started in addScrapView().
                child.dispatchFinishTemporaryDetach();
            }
        }

        if (mCacheColorHint != 0) {
            child.setDrawingCacheBackgroundColor(mCacheColorHint);
        }

        if (child.getImportantForAccessibility() == IMPORTANT_FOR_ACCESSIBILITY_AUTO) {
            child.setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_YES);
        }

        setItemViewLayoutParams(child, position);

        if (AccessibilityManager.getInstance(mContext).isEnabled()) {
            if (mAccessibilityDelegate == null) {
                mAccessibilityDelegate = new ListItemAccessibilityDelegate();
            }
            if (child.getAccessibilityDelegate() == null) {
                child.setAccessibilityDelegate(mAccessibilityDelegate);
            }
        }

        Trace.traceEnd(Trace.TRACE_TAG_VIEW);

        return child;
    }
  • 总结:
    • RecycleBin 提供了 mScrapViews 和 mActiveViews 来实现复用机制;
    • AbsListView 已经提供了 RecycleBin 机制,为我们尽可能的提供复用机制;但在平时开发使用的过程中,我们还是应当尽量避免无谓的 new 操作;
    • 对于常用的 ListView 或者 GridView,如果 convertView 的布局大体都相同,可以尽量重用,避免 item 数量过大或过于分配频繁导致 OOM;

参考资料

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值