效果圖
使用
在app level的build.gradle中加入項(xiàng)目依賴, 確保在project level的build.gradle中包含jcenter()中心倉(cāng)庫(kù)。
compile 'cn.wolfspider:autowraplinelayout:1.0.1'
添加完以后就可以開(kāi)始使用了,具體的使用和設(shè)置樣式的方法還請(qǐng)到項(xiàng)目的Github主頁(yè)(https://github.com/Hector1990/AutoWrapLineLayoutDemo)去瀏覽,當(dāng)然別忘記Star一下哈。
如果只是想使用它,那么到這里就可以了感帅。
實(shí)現(xiàn)原理
首先需要繼承ViewGroup, 在這里我們需要重寫它的onMeasure和onLayout方法。本項(xiàng)目中它的子View有兩種填充方式,一種是Fill_PARENT娶耍,一種是WRAP_CONTENT,看名字應(yīng)該能知道是什么意思吧饼酿。簡(jiǎn)單起見(jiàn)榕酒,這里只介紹WRAP_CONTENT的這一種實(shí)現(xiàn)方式胚膊。
首先來(lái)看onMeasure
childOfLine = new ArrayList<>(); //記錄每行的view數(shù)
int childCount = getChildCount();
int totalHeight = 0; //ViewGroup的總高度
int totalWidth = MeasureSpec.getSize(widthMeasureSpec); //ViewGroup的總長(zhǎng)度
int curLineChildCount = 0; //當(dāng)前行的view數(shù)
int curLineWidth = 0; //當(dāng)前行的寬度
int maxHeight = 0; //當(dāng)前行的最大高度
for (int i = 0; i < childCount; i++) {
View childItem = getChildAt(i);
measureChild(childItem, widthMeasureSpec, heightMeasureSpec);
int childHeight = childItem.getMeasuredHeight();
int childWidth = childItem.getMeasuredWidth();
if (curLineWidth + childWidth <= totalWidth) {
/** 當(dāng)前行的寬度加上此view的寬度小于總長(zhǎng)度 **/
curLineWidth += childWidth;
maxHeight = Math.max(childHeight, maxHeight);
curLineChildCount++;
} else {
/** 當(dāng)前行的寬度加上此view的寬度大于總長(zhǎng)度,換行 **/
childOfLine.add(curLineChildCount);
curLineWidth = childWidth;
curLineChildCount = 1;
totalHeight += maxHeight;
maxHeight = childHeight;
}
}
childOfLine.add(curLineChildCount);
for (int i = 0; i < childOfLine.size(); i++) {
if (childOfLine.get(i) == 0) {
childOfLine.remove(i);
}
}
/** 計(jì)算得到ViewGroup的總高度 **/
totalHeight += (mVerticalGap * (childOfLine.size() - 1) + maxHeight);
setMeasuredDimension(totalWidth, totalHeight);
根據(jù)上面的代碼想鹰,我們可以看到紊婉,第一步是測(cè)量childView的大小,然后根據(jù)它的長(zhǎng)度加上當(dāng)前行的寬度和總長(zhǎng)度進(jìn)行比較辑舷,如果小于等于則繼續(xù)喻犁,否則換行。這里注意的是換行的同時(shí)何缓,要把上一行的childView的數(shù)量加入list中肢础,并且ViewGroup的總長(zhǎng)度要加上上一行的最大高度。
在所有的childView測(cè)量完之后碌廓,我們要進(jìn)行最后的結(jié)算传轰,首先是最后一行的childView數(shù)量要加入list中,其次總高度要加上最后一行的maxHeight谷婆,以及垂直方向的間距慨蛙。
接下來(lái)看onLayout
int index = 0;
int curHeight = 0; //當(dāng)前行的起始高度
for (int i = 0; i < childOfLine.size(); i++) {
int childCount = childOfLine.get(i);
int maxHeight = 0;
int lineWidth = 0;
int target = index + childCount;
for ( ; index < target; index++) {
View item = getChildAt(index);
maxHeight = Math.max(maxHeight, item.getMeasuredHeight());
item.layout(lineWidth, curHeight, lineWidth + item.getMeasuredWidth(), curHeight + item.getMeasuredHeight());
lineWidth += item.getMeasuredWidth() + mHorizontalGap;
}
curHeight += maxHeight + mVerticalGap;
}
這里的代碼應(yīng)該比較容易理解,根據(jù)上一行記錄的每一行的childView數(shù)纪挎,可以知道每一個(gè)childView屬于哪一行股淡,然后就可以進(jìn)行布局了。
項(xiàng)目完整的代碼在Github上廷区,有興趣的朋友可以去看下順便提點(diǎn)意見(jiàn)唯灵。當(dāng)然別忘了Star一下哦。
Github地址:https://github.com/Hector1990/AutoWrapLineLayoutDemo