日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 运维知识 > Android >内容正文

Android

Android自定义实现FlowLayout

發(fā)布時(shí)間:2024/7/23 Android 31 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Android自定义实现FlowLayout 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

實(shí)現(xiàn)FlowLayout

何為FlowLayout,如果對(duì)Java的Swing比較熟悉的話一定不會(huì)陌生,就是控件根據(jù)ViewGroup的寬,自動(dòng)的往右添加,如果當(dāng)前行剩余空間不足,則自動(dòng)添加到下一行。有點(diǎn)所有的控件都往左飄的感覺,第一行滿了,往第二行飄~所以也叫流式布局。Android并沒有提供流式布局,但是某些場(chǎng)合中,流式布局還是非常適合使用的,比如關(guān)鍵字標(biāo)簽,搜索熱詞列表等,比如下圖:

簡(jiǎn)單的分析

1、對(duì)于FlowLayout,需要指定的LayoutParams,我們目前只需要能夠識(shí)別margin即可,即使用MarginLayoutParams.
2、onMeasure中計(jì)算所有childView的寬和高,然后根據(jù)childView的寬和高,計(jì)算自己的寬和高。(當(dāng)然,如果不是wrap_content,直接使用父ViewGroup傳入的計(jì)算值即可)
3、onLayout中對(duì)所有的childView進(jìn)行布局。

generateLayoutParams

因?yàn)槲覀冎恍枰С謒argin,所以直接使用系統(tǒng)的MarginLayoutParams

@Overrideprotected ViewGroup.LayoutParams generateLayoutParams(ViewGroup.LayoutParams p){return new MarginLayoutParams(p);}@Overridepublic ViewGroup.LayoutParams generateLayoutParams(AttributeSet attrs){return new MarginLayoutParams(getContext(), attrs);}@Overrideprotected ViewGroup.LayoutParams generateDefaultLayoutParams(){return new MarginLayoutParams(LayoutParams.MATCH_PARENT,LayoutParams.MATCH_PARENT);}

onMeasure

/*** 負(fù)責(zé)設(shè)置子控件的測(cè)量模式和大小 根據(jù)所有子控件設(shè)置自己的寬和高*/@Overrideprotected void onMeasure(int widthMeasureSpec, int heightMeasureSpec){super.onMeasure(widthMeasureSpec, heightMeasureSpec);// 獲得它的父容器為它設(shè)置的測(cè)量模式和大小int sizeWidth = MeasureSpec.getSize(widthMeasureSpec);int sizeHeight = MeasureSpec.getSize(heightMeasureSpec);int modeWidth = MeasureSpec.getMode(widthMeasureSpec);int modeHeight = MeasureSpec.getMode(heightMeasureSpec);Log.e(TAG, sizeWidth + "," + sizeHeight);// 如果是warp_content情況下,記錄寬和高int width = 0;int height = 0;/*** 記錄每一行的寬度,width不斷取最大寬度*/int lineWidth = 0;/*** 每一行的高度,累加至height*/int lineHeight = 0;int cCount = getChildCount();// 遍歷每個(gè)子元素for (int i = 0; i < cCount; i++){View child = getChildAt(i);// 測(cè)量每一個(gè)child的寬和高measureChild(child, widthMeasureSpec, heightMeasureSpec);// 得到child的lpMarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams();// 當(dāng)前子空間實(shí)際占據(jù)的寬度int childWidth = child.getMeasuredWidth() + lp.leftMargin+ lp.rightMargin;// 當(dāng)前子空間實(shí)際占據(jù)的高度int childHeight = child.getMeasuredHeight() + lp.topMargin+ lp.bottomMargin;/*** 如果加入當(dāng)前child,則超出最大寬度,則的到目前最大寬度給width,類加height 然后開啟新行*/if (lineWidth + childWidth > sizeWidth){width = Math.max(lineWidth, childWidth);// 取最大的lineWidth = childWidth; // 重新開啟新行,開始記錄// 疊加當(dāng)前高度,height += lineHeight;// 開啟記錄下一行的高度lineHeight = childHeight;} else// 否則累加值lineWidth,lineHeight取最大高度{lineWidth += childWidth;lineHeight = Math.max(lineHeight, childHeight);}// 如果是最后一個(gè),則將當(dāng)前記錄的最大寬度和當(dāng)前l(fā)ineWidth做比較if (i == cCount - 1){width = Math.max(width, lineWidth);height += lineHeight;}}setMeasuredDimension((modeWidth == MeasureSpec.EXACTLY) ? sizeWidth: width, (modeHeight == MeasureSpec.EXACTLY) ? sizeHeight: height);}

首先得到其父容器傳入的測(cè)量模式和寬高的計(jì)算值,然后遍歷所有的childView,使用measureChild方法對(duì)所有的childView進(jìn)行測(cè)量。然后根據(jù)所有childView的測(cè)量得出的寬和高得到該ViewGroup如果設(shè)置為wrap_content時(shí)的寬和高。最后根據(jù)模式,如果是MeasureSpec.EXACTLY則直接使用父ViewGroup傳入的寬和高,否則設(shè)置為自己計(jì)算的寬和高。

onLayout

/*** 存儲(chǔ)所有的View,按行記錄*/private List<List<View>> mAllViews = new ArrayList<List<View>>();/*** 記錄每一行的最大高度*/private List<Integer> mLineHeight = new ArrayList<Integer>();@Overrideprotected void onLayout(boolean changed, int l, int t, int r, int b){mAllViews.clear();mLineHeight.clear();int width = getWidth();int lineWidth = 0;int lineHeight = 0;// 存儲(chǔ)每一行所有的childViewList<View> lineViews = new ArrayList<View>();int cCount = getChildCount();// 遍歷所有的孩子for (int i = 0; i < cCount; i++){View child = getChildAt(i);MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams();int childWidth = child.getMeasuredWidth();int childHeight = child.getMeasuredHeight();// 如果已經(jīng)需要換行if (childWidth + lp.leftMargin + lp.rightMargin + lineWidth > width){// 記錄這一行所有的View以及最大高度mLineHeight.add(lineHeight);// 將當(dāng)前行的childView保存,然后開啟新的ArrayList保存下一行的childViewmAllViews.add(lineViews);lineWidth = 0;// 重置行寬lineViews = new ArrayList<View>();}/*** 如果不需要換行,則累加*/lineWidth += childWidth + lp.leftMargin + lp.rightMargin;lineHeight = Math.max(lineHeight, childHeight + lp.topMargin+ lp.bottomMargin);lineViews.add(child);}// 記錄最后一行mLineHeight.add(lineHeight);mAllViews.add(lineViews);int left = 0;int top = 0;// 得到總行數(shù)int lineNums = mAllViews.size();for (int i = 0; i < lineNums; i++){// 每一行的所有的viewslineViews = mAllViews.get(i);// 當(dāng)前行的最大高度lineHeight = mLineHeight.get(i);Log.e(TAG, "第" + i + "行 :" + lineViews.size() + " , " + lineViews);Log.e(TAG, "第" + i + "行, :" + lineHeight);// 遍歷當(dāng)前行所有的Viewfor (int j = 0; j < lineViews.size(); j++){View child = lineViews.get(j);if (child.getVisibility() == View.GONE){continue;}MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams();//計(jì)算childView的left,top,right,bottomint lc = left + lp.leftMargin;int tc = top + lp.topMargin;int rc =lc + child.getMeasuredWidth();int bc = tc + child.getMeasuredHeight();Log.e(TAG, child + " , l = " + lc + " , t = " + t + " , r ="+ rc + " , b = " + bc);child.layout(lc, tc, rc, bc);left += child.getMeasuredWidth() + lp.rightMargin+ lp.leftMargin;}left = 0;top += lineHeight;}

allViews的每個(gè)Item為每行所有View的List集合。
mLineHeight記錄的為每行的最大高度。
23-48行,遍歷所有的childView,用于設(shè)置allViews的值,以及mLineHeight的值。
57行,根據(jù)allViews的長(zhǎng)度,遍歷所有的行數(shù)
67-91行,遍歷每一行的中所有的childView,對(duì)childView的left , top , right , bottom 進(jìn)行計(jì)算,和定位。
92-93行,重置left和top,準(zhǔn)備計(jì)算下一行的childView的位置。

好了,到此完成了所有的childView的繪制區(qū)域的確定,到此,我們的FlowLayout的代碼也結(jié)束了~~靜下心來看一看是不是也不難~

布局文件

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"xmlns:tools="http://schemas.android.com/tools"android:layout_width="fill_parent"android:layout_height="fill_parent"android:background="#E1E6F6"android:orientation="vertical" ><com.zhy.zhy_flowlayout02.FlowLayout android:layout_width="fill_parent"android:layout_height="wrap_content" ><TextView style="@style/text_flag_01"android:text="Welcome" /><TextView style="@style/text_flag_01"android:text="IT工程師" /><TextView style="@style/text_flag_01"android:text="學(xué)習(xí)ing" /><TextView style="@style/text_flag_01"android:text="戀愛ing" /><TextView style="@style/text_flag_01"android:text="掙錢ing" /><TextView style="@style/text_flag_01"android:text="努力ing" /><TextView style="@style/text_flag_01"android:text="I thick i can" /></com.zhy.zhy_flowlayout02.FlowLayout><com.zhy.zhy_flowlayout02.FlowLayout android:layout_width="fill_parent"android:layout_height="wrap_content"android:layout_marginTop="20dp" ><TextView style="@style/text_flag_01"android:background="@drawable/flag_02"android:text="Welcome"android:textColor="#888888" /><TextView style="@style/text_flag_01"android:background="@drawable/flag_02"android:text="IT工程師"android:textColor="#888888" /><TextView style="@style/text_flag_01"android:background="@drawable/flag_02"android:text="學(xué)習(xí)ing"android:textColor="#888888" /><TextView style="@style/text_flag_01"android:background="@drawable/flag_02"android:text="戀愛ing"android:textColor="#888888" /><TextView style="@style/text_flag_01"android:background="@drawable/flag_02"android:text="掙錢ing"android:textColor="#888888" /><TextView style="@style/text_flag_01"android:background="@drawable/flag_02"android:text="努力ing"android:textColor="#888888" /><TextView style="@style/text_flag_01"android:background="@drawable/flag_02"android:text="I thick i can"android:textColor="#888888" /></com.zhy.zhy_flowlayout02.FlowLayout><com.zhy.zhy_flowlayout02.FlowLayout android:layout_width="fill_parent"android:layout_height="wrap_content"android:layout_marginTop="20dp" ><TextView style="@style/text_flag_01"android:background="@drawable/flag_03"android:text="Welcome"android:textColor="#43BBE7" /><TextView style="@style/text_flag_01"android:background="@drawable/flag_03"android:text="IT工程師"android:textColor="#43BBE7" /><TextView style="@style/text_flag_01"android:background="@drawable/flag_03"android:text="學(xué)習(xí)ing"android:textColor="#43BBE7" /><TextView style="@style/text_flag_01"android:background="@drawable/flag_03"android:text="戀愛ing"android:textColor="#43BBE7" /><TextView style="@style/text_flag_01"android:background="@drawable/flag_03"android:text="掙錢ing"android:textColor="#43BBE7" /><TextView style="@style/text_flag_01"android:background="@drawable/flag_03"android:text="努力ing"android:textColor="#43BBE7" /><TextView style="@style/text_flag_01"android:background="@drawable/flag_03"android:text="I thick i can"android:textColor="#43BBE7" /></com.zhy.zhy_flowlayout02.FlowLayout></LinearLayout>

參考鏈接

Android 自定義ViewGroup 實(shí)戰(zhàn)篇 -> 實(shí)現(xiàn)FlowLayout - Hongyang - 博客頻道 - CSDN.NET

源代碼

源代碼

最終效果如下

總結(jié)

以上是生活随笔為你收集整理的Android自定义实现FlowLayout的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。