public class XCFlowLayout extends ViewGroup {
// 存储所有子View
private List<List> mAllChildViews = new ArrayList<List>();
// 每一行的高度
private List mLineHeight = new ArrayList();
public XCFlowLayout(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
public XCFlowLayout(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public XCFlowLayout(Context context) {
this(context, null);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
// TODO Auto-generated method stub
// 父控件传进来的宽度和高度以及对应的测量模式
int sizeWidth = MeasureSpec.getSize(widthMeasureSpec);
int modeWidth = MeasureSpec.getMode(widthMeasureSpec);
int sizeHeight = MeasureSpec.getSize(heightMeasureSpec);
int modeHeight = MeasureSpec.getMode(heightMeasureSpec);
// 如果当前ViewGroup的宽高为wrap_content的情况
int width = 0; // 自己测量的宽度
int height = 0; // 自己测量的高度
int lineWidth = 0;// 每一行的宽度
int lineHeight = 0; // 每一行的高度
int childCount = getChildCount();// 获取子view的个数
for (int i = 0; i < childCount; i++) {
View child = getChildAt(i);
// 测量子View的宽和高
measureChild(child, widthMeasureSpec, heightMeasureSpec);
// 得到LayoutParams
MarginLayoutParams lp = (MarginLayoutParams) getLayoutParams();
// 得到子View占据的宽度
int childWidth = child.getMeasuredWidth() + lp.leftMargin
- lp.rightMargin;
// 得到子View占据的高度
int childHeight = child.getMeasuredHeight() + lp.topMargin
- lp.bottomMargin;
if (lineWidth + childWidth > sizeWidth) {// 需要进行换行
width = Math.max(width, lineWidth); // 得到最大宽度
lineWidth = childWidth; // 重置lineWidth
height += lineHeight; // 得到高度
lineHeight = childHeight;// 重置LineHeight
} else {// 不需要进行换行
lineWidth += childWidth;// 叠加行宽
lineHeight = Math.max(lineHeight, childHeight);
}
if (i == childCount - 1) {// 处理最后一个子View的情况
width = Math.max(width, lineWidth);
height += lineHeight;
}
}
// wrapcontent
-
setMeasuredDimension(modeWidth == MeasureSpec.EXACTLY ? sizeWidth
-
width, modeHeight == MeasureSpec.EXACTLY ? sizeHeight
-
height);
// super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
// TODO Auto-generated method stub
mAllChildViews.clear();
mLineHeight.clear();
int width = getWidth();// 获取当前ViewGroup宽度
int lineWidth = 0;
int lineHeight = 0;
List lineViews = new ArrayList();// 记录当前行的View
int childCount = getChildCount();
for (int i = 0; i < childCount; i++) {
View child = getChildAt(i);
MarginLayoutParams lp = (MarginLayoutParams) child
.getLayoutParams();
int childWidth = child.getMeasuredWidth();
int childHeight = child.getMeasuredHeight();
// 需要换行
if (lineWidth + childWidth + lp.leftMargin + lp.rightMargin > width) {
mLineHeight.add(lineHeight); // 记录lineHeight
mAllChildViews.add(lineViews); // 记录当前行的Views
// 重置 行的宽高
lineWidth = 0;
lineHeight = childHeight + lp.topMargin + lp.bottomMargin;
// 重置当前行的View集合;
lineViews = new ArrayList();
}
lineWidth += childWidth + lp.leftMargin + lp.rightMargin;
lineHeight = Math.max(lineHeight, childHeight + lp.topMargin
- lp.bottomMargin);
lineViews.add(child);
}
// 处理最后一行
mLineHeight.add(lineHeight);
mAllChildViews.add(lineViews);
// 设置子View的位置
int left = 0;
int top = 0;
// 获取行数
int lineCount = mAllChildViews.size();
for (int i = 0; i < lineCount; i++) {
// 当前行的views和高度
lineViews = mAllChildViews.get(i);
lineHeight = mLineHeight.get(i);
for (int j = 0; j < lineViews.size(); j++) {
View child = lineViews.get(j);
// 判断是否显示
if (child.getVisibility() == View.GONE) {
continue;
}
MarginLayoutParams lp = (MarginLayoutParams) child
.getLayoutParams();
int cLeft = left + lp.leftMargin;
int cTop = top + lp.topMargin;
int cRight = cLeft + child.getMeasuredWidth();
int cBottom = cTop + child.getMeasuredHeight();
// 进行子View进行布局
child.layout(cLeft, cTop, cRight, cBottom);
left += child.getMeasuredWidth() + lp.leftMargin
- lp.rightMargin;
}
left = 0;
top += lineHeight;
}
}
/**
- 与当前ViewGroup对应的LayoutParams
*/
@Override
public LayoutParams generateLayoutParams(AttributeSet attrs) {
return new MarginLayoutParams(getContext(), attrs);
}
}
二.xml部分
结语
由于篇幅限制,文档的详解资料太全面,细节内容太多,所以只把部分知识点截图出来粗略的介绍,每个小节点里面都有更细化的内容!以下是目录截图:
由于整个文档比较全面,内容比较多,篇幅不允许,下面以截图方式展示 。
再附一部分Android架构面试视频讲解:
《Android学习笔记总结+移动架构视频+大厂面试真题+项目实战源码》,点击传送门,即可获取!
由于篇幅限制,文档的详解资料太全面,细节内容太多,所以只把部分知识点截图出来粗略的介绍,每个小节点里面都有更细化的内容!以下是目录截图:
[外链图片转存中…(img-kPzUBizW-1715141292942)]
由于整个文档比较全面,内容比较多,篇幅不允许,下面以截图方式展示 。
再附一部分Android架构面试视频讲解:
[外链图片转存中…(img-CQ9dPciH-1715141292943)]
《Android学习笔记总结+移动架构视频+大厂面试真题+项目实战源码》,点击传送门,即可获取!