listview、ViewPager tips

1,listView的childView 與adapter 中數據項的對應關係

listView.getChildCount() 包含了headerView、footerView 以及 cellView,java

而 Adapter 的 getItemCount() 只包含數據項(對應cellView 的個數),app

因此若是有經過listview的childView 找對應的數據item的話,使用時須要注意計算ide

//在listview 的 adapter中

int cc = listView.getChildCount();
int headerCount = listView.getHeaderViewsCount();
//int footerCount = listView.getFooterViewsCount();
int itemCount = getCount();

for (int i = 0; i < cc; i++) {
	View child = listView.getChildAt(i);
	int tmp_pos = listView.getPositionForView(child);
	int position = tmp_pos - headerCount;

	if (position < 0 || position >= itemCount) {
	    // 是headerView 或 footerView
	    continue;
	}
        
        // 如今的position 纔對應adapter的數據項的index
        //能夠進行 getItemType(position) 等操做
}


緣由是在listview 內部會檢查是否有 header 或footer ,有的話會用一個 HeaderViewListAdapter 包咱們提供的listAdapter 包裝起來:this

@Override
    public void setAdapter(ListAdapter adapter) {
        if (mAdapter != null && mDataSetObserver != null) {
            mAdapter.unregisterDataSetObserver(mDataSetObserver);
        }

        resetList();
        mRecycler.clear();

        if (mHeaderViewInfos.size() > 0|| mFooterViewInfos.size() > 0) {
            mAdapter = new HeaderViewListAdapter(mHeaderViewInfos, mFooterViewInfos, adapter);
        } else {
            mAdapter = adapter;
        }

        mOldSelectedPosition = INVALID_POSITION;
        mOldSelectedRowId = INVALID_ROW_ID;

        // AbsListView#setAdapter will update choice mode states.
        super.setAdapter(adapter);
        
        ...
        
  }
  
  
  ...
  
  
  public void addFooterView(View v, Object data, boolean isSelectable) {
        final FixedViewInfo info = new FixedViewInfo();
        info.view = v;
        info.data = data;
        info.isSelectable = isSelectable;
        mFooterViewInfos.add(info);

        // Wrap the adapter if it wasn't already wrapped.
        if (mAdapter != null) {
            if (!(mAdapter instanceof HeaderViewListAdapter)) {
                mAdapter = new HeaderViewListAdapter(mHeaderViewInfos, mFooterViewInfos, mAdapter);
            }

            // In the case of re-adding a footer view, or adding one later on,
            // we need to notify the observer.
            if (mDataSetObserver != null) {
                mDataSetObserver.onChanged();
            }
        }
    }


2,child view的複用

view的複用都體如今方法makeAndAddView調用的obtainView;
spa

保存了listView因爲非數據變化緣由致使layout時,其全部child view 保存在RecyleBin 的 mActiveView 中。code

用到的position參數 都是指adapter中數據項位置。server

    private View makeAndAddView(int position, int y, boolean flowDown, boolean selected) {
        View child;

        onChildCreated(position, flowDown);

        if (!mDataChanged) {
            // Try to use an existing view for this position
            child = mRecycleBin.getActiveView(position);
            if (child != null) {
                setupChild(child, position, y, flowDown, selected, true);
                return child;
            }
        }

        child = obtainView(position, mIsScrap);
        setupChild(child, position, y, flowDown, selected, mIsScrap[0]);

        return child;
    }


obtainView:get

private View obtainView(int position, boolean[] isScrap) {
        isScrap[0] = false;
        View scrapView;

        scrapView = mRecycleBin.getScrapView(position);

        View child;
        if (scrapView != null) {
            child = mAdapter.getView(position, scrapView, this);
            if (child != scrapView) {
                mRecycleBin.addScrapView(scrapView, position);
            }
            else {
                isScrap[0] = true;
            }
        }
        else {
            child = mAdapter.getView(position, null, this);
        }

        return child;
    }


3, ViewPager 的adapter 方法 instantiateItem 方法 必須執行 container.addView(..), 不然不能顯示,這和listView是徹底不一樣的。

public Object instantiateItem(ViewGroup container, final int arg1) {
    container.addView(mViews.get(arg1),0);
    return mViews.get(arg1);
}

 在源碼的註釋裏清楚標註了這一點:源碼

在finishUpdate (ViewGroup) 方法返回前須要完成addView 操做it

/**
* Create the page for the given position.  The adapter is responsible
* for adding the view to the container given here
, although it only
* must ensure this is done by the time it returns from
* {@link #finishUpdate(ViewGroup)}.
*
* @param container The containing View in which the page will be shown.
* @param position The page position to be instantiated.
* @return Returns an Object representing the new page.  This does not
* need to be a View, but can be some other container of the page.
*/
public Object instantiateItem(ViewGroup container, int position) {
   return instantiateItem((View) container, position);
}


那麼 instantiateItem 返回的object 到底什麼意義呢?從ViewPager的代碼片斷能夠看出這個object就是(必須)view,它被ViewPager 包裝進ItemInfo來記錄view的信息: 

static class ItemInfo {
    Object object;
    int position;
    boolean scrolling;
    float widthFactor;
    float offset;
}

ItemInfo infoForChild(View child) {
    for (int i=0; i<mItems.size(); i++) {
        ItemInfo ii = mItems.get(i);
        if (mAdapter.isViewFromObject(child, ii.object)) {
            return ii;
        }
}
    return null;
}


4, ViewPager 的ItemView 默認高和寬都是 match_parent, ViewPager的 onLayout方法裏不考慮childView的 layout參數的高和寬的設置。

相關文章
相關標籤/搜索