繼承ViewGroup實(shí)現(xiàn)一個(gè)簡(jiǎn)單的自動(dòng)換行流布局
可實(shí)現(xiàn)同比等寬旷太、自適應(yīng)寬度兩種樣式
源碼:https://github.com/Beckboy/FlowLayoutTextViewDome.git
前段時(shí)間在工作中有遇到瀑布流布局,給自己造成一定困擾销睁。
通過(guò)這段時(shí)間的實(shí)用和總結(jié)供璧,自己也相應(yīng)的封裝了一套,會(huì)在接下來(lái)的一段時(shí)間不斷完善冻记,方便今后能直接使用睡毒。
標(biāo)簽流.gif
步驟
- 繼承ViewGroup;
- 復(fù)寫(xiě)onMeasure()冗栗,計(jì)算每個(gè)子view的寬和高演顾,根據(jù)全部子view的寬和高,獲取整個(gè)布局的寬和高隅居;
- 復(fù)寫(xiě)onLayout()钠至,計(jì)算每個(gè)子view的位置并設(shè)置布局;
- 提供獲取行數(shù)的方法getLines()胎源;
- 提供獲取當(dāng)前行高度的方法getCloseHeight()棉钧;
實(shí)現(xiàn)
- 繼承ViewGroup
public class FlowLayout extends ViewGroup
- 初始化數(shù)據(jù)源
//存儲(chǔ)當(dāng)前ViewGroup的所有view,在Activity中直接用addView(View view)添加涕蚤;
private List<List<View>> mAllViews = new ArrayList<List<View>>();
//把每一行數(shù)據(jù)的高度存儲(chǔ)到List
private List<Integer> mHeightList = new ArrayList<Integer>();
- 復(fù)寫(xiě)onLayout
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
mAllViews.clear();
mHeightList.clear();
int width = getWidth();
int overWidth = 0; //每一行view所占據(jù)的總寬度margin宪卿,padding
int overHeight = 0; //每一個(gè)view所占據(jù)的總高度
List<View> lineViews = new ArrayList<View>();
int viewCount = getChildCount(); //所有View的總數(shù)量
for (int i = 0;i < viewCount ; i++){
View child = getChildAt(i); //每一個(gè)子View
MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams();
int childViewWidth = child.getMeasuredWidth();
int childViewHeight = child.getMeasuredHeight();
//當(dāng)前View超過(guò)一行時(shí),換行處理
if (childViewWidth + overWidth + lp.leftMargin + lp.rightMargin > width - getPaddingLeft() - getPaddingRight()){ //換行判斷
mHeightList.add(overHeight);
mAllViews.add(lineViews);
//重置行寬和行高
overWidth = 0;
overHeight = childViewHeight + lp.topMargin + lp.bottomMargin;
//重置我們的View集合
lineViews = new ArrayList<View>();
}
overWidth += childViewWidth + lp.leftMargin + lp.rightMargin;
overHeight = Math.max(overHeight,childViewHeight + lp.topMargin +lp.bottomMargin);
lineViews.add(child);
}
//處理最后一行
mHeightList.add(overHeight);
mAllViews.add(lineViews);
//設(shè)置每一個(gè)子View的位置
int childLeft = getPaddingLeft();
int childTop = getPaddingTop();
//當(dāng)前行數(shù)
int linesNum = mAllViews.size();
for (int i = 0; i < linesNum; i++){
//當(dāng)前行的所有view
lineViews = mAllViews.get(i);
overHeight = mHeightList.get(i);
for (int j = 0;j < lineViews.size();j++){
View child = lineViews.get(j);
//判斷當(dāng)前View的狀態(tài)
if (child.getVisibility() == View.GONE){
continue;
}
MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams();
int lc = childLeft + lp.leftMargin;
int tc = childTop + lp.topMargin;
int rc = lc + child.getMeasuredWidth();
int bc = tc + child.getMeasuredHeight();
child.layout(lc,tc,rc,bc); //為子View進(jìn)行布局
childLeft +=child.getMeasuredWidth() + lp.leftMargin + lp.rightMargin;
}
childLeft = getPaddingLeft();
childTop += overHeight;
}
}
-復(fù)寫(xiě)onMeasure
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int sizeWidth = MeasureSpec.getSize(widthMeasureSpec);
int modeWidth = MeasureSpec.getMode(widthMeasureSpec);
int sizeHeight = MeasureSpec.getSize(heightMeasureSpec);
int modeHeight = MeasureSpec.getMode(heightMeasureSpec);
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
//wrap_content模式下的寬度和高度
int width = 0;
int height = 0;
//記錄每一行的寬度和高度
int lineWidth = 0;
int lineHeight = 0;
//獲取內(nèi)容的View元素個(gè)數(shù)
int cCount = getChildCount();
for (int i = 0; i < cCount; i++){
View child = getChildAt(i);
//測(cè)量子View的寬度和高度
measureChild(child,widthMeasureSpec,heightMeasureSpec);
//得到LayoutParams
MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams();
//子View占據(jù)的寬度
int childWidth = child.getMeasuredWidth() + lp.leftMargin + lp.rightMargin;
//子View占據(jù)的高度
int childHeight = child.getMeasuredHeight() + lp.topMargin + lp.bottomMargin;
//換行處理
if (lineWidth + childWidth > sizeWidth - getPaddingLeft() - getPaddingRight()){
//對(duì)比得到的最大寬度
width = Math.max(width,lineWidth);
//重置
lineWidth = childWidth;
//記錄行高
height += lineHeight;
lineHeight = childHeight;
}else {
lineWidth += childWidth;
//獲取當(dāng)前行最大的高度
lineHeight = Math.max(lineHeight,childHeight);
}
if (i == cCount - 1){ //如果是最后一個(gè)控件
width = Math.max(lineWidth,width);
height +=lineHeight;
}
}
setMeasuredDimension(
modeWidth == MeasureSpec.EXACTLY ? sizeWidth : width + getPaddingLeft() + getPaddingRight(),
modeHeight == MeasureSpec.EXACTLY ? sizeHeight : height + getPaddingTop() + getPaddingBottom());
}
- 獲取指定行數(shù)的高度
public int getCloseHeight(int lines){
int hh = 0;
if (lines > mHeightList.size()){
lines = mHeightList.size();
}
for (int i = 0; i < lines;i++){
hh += mHeightList.get(i);
}
return hh;
}
- 獲取總行數(shù)
public int getLines(){
return mAllViews.size();
}
使用
- 初始化數(shù)據(jù)源
// 數(shù)據(jù)源
private String[] collegData = new String[]{"清華大學(xué)","北京大學(xué)","復(fù)旦大學(xué)","浙江大學(xué)","南開(kāi)大學(xué)","同濟(jì)大學(xué)"
,"蘇州大學(xué)","吉林大學(xué)","哈佛大學(xué)","斯坦福大學(xué)","麻省理工大學(xué)","斯坦福大學(xué)","加州理工學(xué)院"};
- 初始化子布局
//寬高
private int width = ViewGroup.LayoutParams.WRAP_CONTENT;
private int height = ViewGroup.LayoutParams.WRAP_CONTENT;
- 在代碼中動(dòng)態(tài)添加子view
/**
* 初始化瀑布流列表
*/
private void initData() {
for (String colleg : collegData){
loadFlowCircleList(colleg);
}
}
/**
* 加載流標(biāo)簽
*/
private void loadFlowCircleList(String colleg) {
CheckBox cbTag = (CheckBox) getLayoutInflater().inflate(R.layout.item_topic_tag, mFlow, false);
cbTag.setBackgroundDrawable(getResources().getDrawable(R.drawable.selector_bg_check_circle));
ViewGroup.MarginLayoutParams lp = new ViewGroup.MarginLayoutParams(width,height);
lp.leftMargin = 20; //左間距
lp.rightMargin = 20; //右間距
lp.topMargin = 40; //上間距
cbTag.setLayoutParams(lp);
cbTag.setText(colleg);
mFlow.addView(cbTag);
}
- 設(shè)置子view的點(diǎn)擊事件
public void onclick(View view) {
mFlow.removeAllViews();
switch (view.getId()){
case R.id.btn_left: //加載自適應(yīng)流布局
width = ViewGroup.LayoutParams.WRAP_CONTENT;
break;
case R.id.btn_right: //加載等寬流布局
int column = 3; //列數(shù)
int max_width = getWindowManager().getDefaultDisplay().getWidth(); //獲取屏幕寬度
width = (max_width - 40 * column)/column;
break;
}
initData();
}
- 布局文件
FlowLayout布局文件
<com.example.hunter_j.flowlayoutdemo.view.FlowLayout
android:id="@+id/flowlayouot"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"/>
??子view布局文件
<CheckBox
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="54px"
android:paddingTop="14px"
android:paddingBottom="14px"
android:paddingLeft="20px"
android:paddingRight="20px"
android:gravity="center"
android:textSize="13sp"
android:button="@null"
android:textColor="@drawable/selector_color_txt">
</CheckBox>
??xml設(shè)置子view字體赞季、背景變色自動(dòng)
<!--drawable資源文件:selector_bg_check_circle-->
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_checked="false" >
<shape android:shape="rectangle">
<solid android:color="#f3f6f7"/>
<corners android:radius="50px"/>
</shape>
</item>
<item android:state_checked="true">
<shape android:shape="rectangle">
<solid android:color="#ffc84a"/>
<corners android:radius="50px"/>
</shape>
</item>
</selector>
<!--drawable資源文件:selector_color_txt-->
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_focused="false" android:state_enabled="true" android:state_pressed="false"
android:color="@drawable/color_cdac8d" />
<item android:state_enabled="false" android:color="@drawable/color_cdac8d" />
<item android:state_pressed="true" android:color="@drawable/color_f9" />
<item android:state_focused="true" android:color="@drawable/color_f9" />
</selector>
<!--color資源文件-->
<resources>
<!-- 字體顏色的選擇器 -->
<drawable name="color_f9">#F9F9F9</drawable>
<drawable name="color_cdac8d">#CDAC8D</drawable>
</resources>
源碼:
源碼:https://github.com/Beckboy/FlowLayoutTextViewDome.git
新手上路愧捕,歡迎同學(xué)們吐槽評(píng)論奢驯,如果你覺(jué)得對(duì)你有幫助
那么就留個(gè)言或者點(diǎn)下喜歡吧(^-^)