android 實現FlowLayout 流線佈局(自定義ViewGroup)
專案目的
1、深化自定義View的概念
2、將MeasureSpec、View的繪製流程、Layoutparams等分散的知識點整合成一個demo。
專案靈感
筆者經驗也是有限,此文章主要借鑑張鴻洋前輩的部落格。
原文地址:http://blog.csdn.net/lmj623565791/article/details/38352503
專案預覽(原始碼附文章結尾)
主要涉及知識點以及相關連結
1、MeasureSpec
快速理解android View的測量onMeasure()與MeasureSpec
2、Layoutparams以及MarginLayoutParams
Android開發:LayoutParams的用法
MarginLayoutParams–一個可以在程式碼中直接設定margin的方法
3、View的繪製流程
Android檢視繪製流程完全解析,帶你一步步深入瞭解View(二)
4、筆者自定View相關文章
給自定義View新增xml屬性
android 自定義控制元件(底部icon點選效果)
實現要點
1、重寫onMeasure()方法,讓FlowLayout能夠根據子部局的大小來確定自己的大小。
2、重寫onLayout()方法,讓FlowLayout中的子部局能夠即正確的排版,即對其間的子View進行佈局。
3、使用MarginLayoutParams,不僅能夠獲取到子部局的寬高,還能獲取到子佈局的margin引數。
主要程式碼
public class FlowLayout extends ViewGroup {
private static final String TAG = "FlowLayout";
public FlowLayout(Context context, AttributeSet attrs) {
super(context, attrs);
}
@Override
protected ViewGroup.LayoutParams generateLayoutParams(
ViewGroup.LayoutParams p) {
return new MarginLayoutParams(p);
}
@Override
public ViewGroup.LayoutParams generateLayoutParams(AttributeSet attrs) {
return new MarginLayoutParams(getContext(), attrs);
}
@Override
protected ViewGroup.LayoutParams generateDefaultLayoutParams() {
return new MarginLayoutParams(LayoutParams.MATCH_PARENT,
LayoutParams.MATCH_PARENT);
}
// 負責設定子控制元件的測量模式和大小 根據所有子控制元件設定自己的寬和高
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
// 獲得它的父容器為它設定的測量模式和大小
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;
int lineWidth = 0;//每行的寬度
int lineHeight = 0;//每行的高度
int cCount = getChildCount();
// 遍歷每個子元素
for (int i = 0; i < cCount; i++) {
View child = getChildAt(i);
// 測量每一個child的寬和高
measureChild(child, widthMeasureSpec, heightMeasureSpec);
// 得到child的lp
MarginLayoutParams lp = (MarginLayoutParams) child
.getLayoutParams();
int childWidth = child.getMeasuredWidth() + lp.leftMargin
+ lp.rightMargin;// 當前子空間實際佔據的寬度
int childHeight = child.getMeasuredHeight() + lp.topMargin
+ lp.bottomMargin;// 當前子空間實際佔據的高度
//如果加入當前child,超出最大寬度,則增加高度
if (lineWidth + childWidth > sizeWidth) {
width = Math.max(lineWidth, childWidth);// 取最大的
lineWidth = childWidth; // 重新開啟新行,開始記錄
height += lineHeight;// 疊加當前高度,
lineHeight = childHeight;// 開啟記錄下一行的高度
} else
// 否則累加值lineWidth,lineHeight取最大高度
{
lineWidth += childWidth;
lineHeight = Math.max(lineHeight, childHeight);
}
// 如果是最後一個,則將當前記錄的最大寬度和當前lineWidth做比較
if (i == cCount - 1) {
width = Math.max(width, lineWidth);
height += lineHeight;
}
}
//設定佈局寬高有三種情況
//如果是wrap_content,則根據所有子部局的大小來顯示
//如果是確切值,就根據確切值的大小顯示
//如果是match_parent,則顯示父佈局能顯示的最大值
setMeasuredDimension((modeWidth == MeasureSpec.EXACTLY) ? sizeWidth
: width, (modeHeight == MeasureSpec.EXACTLY) ? sizeHeight
: height);
}
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
int width = getWidth();
int lineWidth = 0;
int lineHeight = 0;
// 獲取孩子的個數
int cCount = getChildCount();
int left = 0;
int top = 0;
// 遍歷所有的孩子,
for (int i = 0; i < cCount; i++) {
View child = getChildAt(i);
MarginLayoutParams lp = (MarginLayoutParams) child
.getLayoutParams();
int childWidth = child.getMeasuredWidth();
int childHeight = child.getMeasuredHeight();
// 如果已經需要換行
if (childWidth + lp.leftMargin + lp.rightMargin + lineWidth > width) {
lineWidth = 0;// 重置行寬
left = 0;
top += lineHeight;
}
//計算childView的left,top,right,bottom,並且設定每個View的位置
int lc = left + lp.leftMargin;
int tc = top + lp.topMargin;
int rc = lc + child.getMeasuredWidth();
int bc = tc + child.getMeasuredHeight();
child.layout(lc, tc, rc, bc);
//最後不要忘記累加
left += child.getMeasuredWidth() + lp.rightMargin
+ lp.leftMargin;
lineWidth += childWidth + lp.leftMargin + lp.rightMargin;
lineHeight = Math.max(lineHeight, childHeight + lp.topMargin
+ lp.bottomMargin);
}
}
}
相關文章
- 自定義流式佈局:ViewGroup的測量與佈局View
- Android自定義控制元件之自定義ViewGroup實現標籤雲Android控制元件View
- 自定義ViewGroup,實現Android的側滑選單ViewAndroid
- Android 自定義氣泡佈局Android
- Android自定義ViewGroup(一)AndroidView
- android自定義View&自定義ViewGroup(下)AndroidView
- android自定義View&自定義ViewGroup(上)AndroidView
- 自定義viewgroup(5)--可滾動佈局,GestureDetector手勢監聽View
- Android自定義View實現流式佈局(熱門標籤效果)AndroidView
- 深入解析Android的自定義佈局Android
- Android自定義View:ViewGroup(三)AndroidView
- Android ViewDragHelper 自定義 ViewGroup 神器AndroidView
- Android自定義View(四)側滑佈局AndroidView
- android自定義佈局——城市選擇介面Android
- jQuery實現瀑布流佈局jQuery
- 自定義CollectionView UICollectionViewLayout實現橫向佈局分頁EmojiViewUI
- UICollectionView自定義佈局(二)UIView
- UICollectionView自定義佈局(一)UIView
- android自定義View(2):實現百分比自適應佈局AndroidView
- 原生 js 實現瀑布流佈局、React 版本的瀑布流佈局元件JSReact元件
- Android ViewDragHelper完全解析 自定義ViewGroup神器AndroidView
- 谷歌開發者工具自定義佈局谷歌
- Flutter自定義佈局-CustomMultiChildLayoutFlutter
- iOS CollectionView FlowLayout與風火輪佈局 (二)iOSView
- 【android】自定義佈局控制控制元件的位置可以通過繼承FrameLayout實現Android控制元件繼承
- Android技術分享| 自定義ViewGroup實現直播間大小屏無縫切換AndroidView
- 多佈局的自定義AdapterAPT
- 自定義佈局管理器-FormLayoutORM
- Android動畫效果之自定義ViewGroup新增布局動畫Android動畫View
- Android開發教程:自定義ViewGroup方法總結AndroidView
- 使用 yogaKit 實現一個資訊流佈局
- 瀑布流佈局實現程式碼詳解
- Android自定義拍照實現Android
- Android 實現自定義圓環Android
- 自定義collocationViewLayout實現多區瀑布流View
- ViewGroup篇:玩一下自定義ViewGroupView
- 自定義View:畫布實現自定義View(折線圖的實現)View
- java:佈局方法(流佈局)Java