listview、ViewPager tips

1,listView的childView 与adapter 中数据项的对应关系

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

而 Adapter 的 getItemCount() 只包含数据项(对应cellView 的个数),

所以如果有通过listview的childView 找对应的数据item的话,使用时需要注意计算

//在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 包装起来:

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

保存了listView由于非数据变化原因导致layout时,其所有child view 保存在RecyleBin 的 mActiveView 中。

用到的position参数 都是指adapter中数据项位置。

    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:

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 操作

/**
* 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参数的高和宽的设置。

转载于:https://my.oschina.net/u/255456/blog/343665

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值