您的位置:首页 > 移动开发 > Android开发

【android】Adapter 的 convertView 复用浅析

2016-03-21 20:32 453 查看

目录

操作环境

导读

参数说明

源码浅析

操作环境

操作系统: 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;

参考资料

http://www.android100.org/html/201507/26/168809.html
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息