最近根据设计图写listView 的时候,listView个item 之间存在间距,左右上下都有,一开始的想法是在item 的根布局 设置margin 属性,但是在listView 中margin 无法生效,所以在此研究下失效的原因。而解决办法就是在加一层布局,作为根布局 设置padding ,或者 新增根布局的下一次布局设置margin.
为什么设置margin不起作用呢?
这是我看到的一段解释,
The fact is that, the margin of LinearLayout (child) asks its parent layout (container) to give child layout a margin of x value.
So if the parent layouts’ LayoutParams support the margins then that margin is honored and applied.
ListView uses AbsListView.LayoutParams by default, which doesn’t include any margin support, just the height and width, thats why, it simply ignores the params value for margins.
ListView extends AbsListView 查看 AbsListView 如何加入布局参数的
private void setItemViewLayoutParams(View child, int position) {
final ViewGroup.LayoutParams vlp = child.getLayoutParams(); //直接使用子布局的布局参数
LayoutParams lp;
if (vlp == null) {
lp = (LayoutParams) generateDefaultLayoutParams();
} else if (!checkLayoutParams(vlp)) {
lp = (LayoutParams) generateLayoutParams(vlp);
} else {
lp = (LayoutParams) vlp;
}
if (mAdapterHasStableIds) {
lp.itemId = mAdapter.getItemId(position);
}
lp.viewType = mAdapter.getItemViewType(position);
lp.isEnabled = mAdapter.isEnabled(position);
if (lp != vlp) {
child.setLayoutParams(lp);
}
}
有一个MarginLayoutParams,ViewGroup中的margin参数有这个类计算
在ViewGroup有一个测量子类Margin的方法measureChildWithMargins
protected void measureChildWithMargins(View child,
int parentWidthMeasureSpec, int widthUsed,
int parentHeightMeasureSpec, int heightUsed) {
final MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams();
final int childWidthMeasureSpec = getChildMeasureSpec(parentWidthMeasureSpec,
mPaddingLeft + mPaddingRight + lp.leftMargin + lp.rightMargin
+ widthUsed, lp.width); //---------获取margin参数-----------
final int childHeightMeasureSpec = getChildMeasureSpec(parentHeightMeasureSpec,
mPaddingTop + mPaddingBottom + lp.topMargin + lp.bottomMargin
+ heightUsed, lp.height);
child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
}
在ListView的父类AbsListView中没有实现这个类,不会计算子控件的Margin.所以在ListView的ItemView设置Margin是无效的。
padding有效的原因
下面是AblistView的onMeasure , 里面对selection的padding边距进行了处理。
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
if (mSelector == null) {
useDefaultSelector();
}
final Rect listPadding = mListPadding;
listPadding.left = mSelectionLeftPadding + mPaddingLeft;
listPadding.top = mSelectionTopPadding + mPaddingTop;
listPadding.right = mSelectionRightPadding + mPaddingRight;
listPadding.bottom = mSelectionBottomPadding + mPaddingBottom;
// Check if our previous measured size was at a point where we should scroll later.
if (mTranscriptMode == TRANSCRIPT_MODE_NORMAL) {
final int childCount = getChildCount();
final int listBottom = getHeight() - getPaddingBottom();
final View lastChild = getChildAt(childCount - 1);
final int lastBottom = lastChild != null ? lastChild.getBottom() : listBottom;
mForceTranscriptScroll = mFirstPosition + childCount >= mLastHandledItemCount &&
lastBottom <= listBottom;
}
}