ViewGroup
ViewGroup是一個特殊的View,可以包含其他視圖(稱為子視圖),而ViewGroup是View的子類,所以ViewGroup可以當成普通的UI組件使用,ViewGroup是布局和視圖容器的基類,該類還定義了ViewGroup.LayoutParams用作布局引數基類的類,

由于ViewGroup的直接子類和間接子類比較多,上圖描述了展示了部分子類,下面把放在android.widget包下的ViewGroup的全部子類展示出來,

繼承關系該寫的基本差不多了,下面咱學習一個自定義ViewGroup,
自定義 ViewGroup
ViewGroup常用重寫方法:
onMeasure()
遍歷自己的子View對自己的每一個子View進行measure,絕大多數時候對子View的measure都可以直接用measureChild()這個方法來替代,確定子View的寬高和自己的寬高以后 再呼叫setMeasuredDimension將ViewGroup自身的寬和高傳給它的父View,才可以繼續寫onLayout()方法,
onSizeChanged()
在onMeasure()后執行,只有大小發生了變化才會執行onSizeChange(),
onLayout()
排列所有子View的位置,通過getChildCount()獲取所有子view,getChildAt獲取childview呼叫各自的layout(int l, int t, int r, int b)方法來排列自己,
onDraw()
自定義ViewGroup默認不會觸發onDraw方法,需要設定背景色或者setWillNotDraw(false)來手動觸發,
注意: ViewGroup的onLayout()方法是必須重寫的,而onDraw()方法默認是不會呼叫,如果想執行onDraw方法,可以通過下面兩種方法:
-
1.設定透明背景:
- 在建構式中:setBackgroundColor(Color.TRANSPARENT);
- 在xml中:android:background="@color/transparent"
-
2.在建構式中添加setWillNotDraw(false)不進行自行繪制View,
下面咱們寫一個簡單的栗子,先看效果圖,

1.創建CustomLayout繼承ViewGroup
/**
* 撰寫自定義ViewGroup的示例,
*/
public class CustomLayout extends ViewGroup {
// private int childHorizontalSpace = 20;
// private int childVerticalSpace = 20;
private int childHorizontalSpace;
private int childVerticalSpace;
//從代碼創建視圖時使用的簡單建構式,
public CustomLayout(Context context) {
super(context);
}
//從XML使用視圖時呼叫的建構式,
public CustomLayout(Context context, AttributeSet attrs) {
super(context, attrs);
TypedArray attrArray = context.obtainStyledAttributes(attrs, R.styleable.CustomLayout);
if (attrArray != null) {
childHorizontalSpace = attrArray.getDimensionPixelSize(R.styleable.CustomLayout_horizontalSpace, 12);
childVerticalSpace = attrArray.getDimensionPixelSize(R.styleable.CustomLayout_verticalSpace, 12);
MLog.e(getClass().getName(),"HorizontalSpace:"+childHorizontalSpace+"|VerticalSpace:"+childVerticalSpace);
attrArray.recycle();
}
//此視圖是否自行繪制
setWillNotDraw(false);
}
/**
* 負責設定子控制元件的測量模式和大小 根據所有子控制元件設定自己的寬和高
*/
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
MLog.e(getClass().getName(),"onMeasure");
// 獲得它的父容器為它設定的測量模式和大小
int sizeWidth = MeasureSpec.getSize(widthMeasureSpec);
int sizeHeight = MeasureSpec.getSize(heightMeasureSpec);
int modeWidth = MeasureSpec.getMode(widthMeasureSpec);
int modeHeight = MeasureSpec.getMode(heightMeasureSpec);
// 如果是warp_content情況下,記錄寬和高
int width = 0;
int height = 0;
//記錄每一行的寬度,width不斷取最大寬度
int lineWidth = 0;
//每一行的高度,累加至height
int lineHeight = 0;
int count = getChildCount();
int left = getPaddingLeft();
int top = getPaddingTop();
// 遍歷每個子元素
for (int i = 0; i < count; i++) {
View child = getChildAt(i);
if (child.getVisibility() == GONE)
continue;
// 測量每一個child的寬和高
measureChild(child, widthMeasureSpec, heightMeasureSpec);
// 得到child的lp
ViewGroup.LayoutParams lp = child.getLayoutParams();
// 當前子空間實際占據的寬度
int childWidth = child.getMeasuredWidth() + childHorizontalSpace;
// 當前子空間實際占據的高度
int childHeight = child.getMeasuredHeight() + childVerticalSpace;
if (lp != null && lp instanceof MarginLayoutParams) {
MarginLayoutParams params = (MarginLayoutParams) lp;
childWidth += params.leftMargin + params.rightMargin;
childHeight += params.topMargin + params.bottomMargin;
}
//如果加入當前child,則超出最大寬度,則的到目前最大寬度給width,類加height 然后開啟新行
if (lineWidth + childWidth > sizeWidth - getPaddingLeft() - getPaddingRight()) {
width = Math.max(lineWidth, childWidth);// 取最大的
lineWidth = childWidth; // 重新開啟新行,開始記錄
// 疊加當前高度,
height += lineHeight;
// 開啟記錄下一行的高度
lineHeight = childHeight;
child.setTag(new Location(left, top + height, childWidth + left - childHorizontalSpace, height + child.getMeasuredHeight() + top));
} else {
// 否則累加值lineWidth,lineHeight取最大高度
child.setTag(new Location(lineWidth + left, top + height, lineWidth + childWidth - childHorizontalSpace + left, height + child.getMeasuredHeight() + top));
lineWidth += childWidth;
lineHeight = Math.max(lineHeight, childHeight);
}
}
width = Math.max(width, lineWidth) + getPaddingLeft() + getPaddingRight();
height += lineHeight;
sizeHeight += getPaddingTop() + getPaddingBottom();
height += getPaddingTop() + getPaddingBottom();
setMeasuredDimension((modeWidth == MeasureSpec.EXACTLY) ? sizeWidth : width, (modeHeight == MeasureSpec.EXACTLY) ? sizeHeight : height);
}
/**
* 記錄子控制元件的坐標
*/
public class Location {
public Location(int left, int top, int right, int bottom) {
this.left = left;
this.top = top;
this.right = right;
this.bottom = bottom;
}
public int left;
public int top;
public int right;
public int bottom;
}
//計算當前View以及子View的位置
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
MLog.e(getClass().getName(),"onLayout");
//獲取子View個數
int count = getChildCount();
for (int i = 0; i < count; i++) {
//獲取子View
View child = getChildAt(i);
//判斷是否顯示
if (child.getVisibility() == GONE)
continue;
//獲取子View的坐標
Location location = (Location) child.getTag();
//設定子View位置
child.layout(location.left, location.top, location.right, location.bottom);
}
}
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
MLog.e(getClass().getName(),"onSizeChanged");
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
MLog.e(getClass().getName(),"onDraw");
}
}
2.使用自定義CustomLayout
<?xml version="1.0" encoding="utf-8"?>
<com.scc.demo.view.CustomLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:custom="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_margin="@dimen/dimen_20"
custom:horizontalSpace="10dp"
custom:verticalSpace="20dp">
<!--一定記得添加前綴-->
<TextView
style="@style/TvStyle"
android:text="破陣子·為陳同甫賦壯詞以寄" />
<TextView
style="@style/TvStyle"
android:text="宋·辛棄疾" />
<TextView
style="@style/TvStyle"
android:text="醉里挑燈看劍" />
<TextView
style="@style/TvStyle"
android:text="夢回吹角連營" />
<TextView
style="@style/TvStyle"
android:text="八百里分麾下炙" />
<TextView
style="@style/TvStyle"
android:text="五十弦翻塞外聲" />
<TextView
style="@style/TvStyle"
android:text="沙場秋點兵" />
<TextView
style="@style/TvStyle"
android:text="馬作的盧飛快" />
<TextView
style="@style/TvStyle"
android:text="弓如霹靂弦驚(增加點長度)" />
<TextView
style="@style/TvStyle"
android:text="了卻君王天下事" />
<TextView
style="@style/TvStyle"
android:text="贏得生前身后名" />
<TextView
style="@style/TvStyle"
android:text="可憐白發生!" />
</com.scc.demo.view.CustomLayout>
自定義屬性
在app/src/main/res/values/attrs.xml中添加屬性
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="CustomLayout">
<attr name="verticalSpace" format="dimension" />
<attr name="horizontalSpace" format="dimension" />
</declare-styleable>
</resources>
使用自定義屬性
- 在xml中使用
一定要添加:xmlns:test=”schemas.android.com/apk/res-aut…
<com.scc.demo.view.CustomLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:custom="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_margin="@dimen/dimen_20"
custom:horizontalSpace="10dp"
custom:verticalSpace="20dp">
</com.scc.demo.view.CustomLayout>
- 在代碼中使用
TypedArray attrArray = context.obtainStyledAttributes(attrs, R.styleable.CustomLayout);
if (attrArray != null) {
//引數1:獲取xml中設定的引數;引數2:獲取失敗2使用引數作為默認值
childHorizontalSpace = attrArray.getDimensionPixelSize(R.styleable.CustomLayout_horizontalSpace, 12);
childVerticalSpace = attrArray.getDimensionPixelSize(R.styleable.CustomLayout_verticalSpace, 12);
MLog.e(getClass().getName(),"HorizontalSpace:"+childHorizontalSpace+"|VerticalSpace:"+childVerticalSpace);
//TypedArray物件池的大小默認為5,使用時記得呼叫recyle()方法將不用的物件回傳至物件池來達到重用的目的,
attrArray.recycle();
}
寫到這里自定義ViewGroup基本完成,
ViewGroup屬性
ViewGroup的XML屬性以及相關方法

ViewGroup.LayoutParams
LayoutParams 被視圖用來告訴他們的父組件他們想要如何布局, 基本的 LayoutParams 類只是描述了視圖的寬度(android:layout_height)和高度(android:layout_width)的大小,對于每個維度,它可以指定以下之一:
- a、MATCH_PARENT,這意味著視圖希望與其父視圖一樣大(減去填充)
- b、WRAP_CONTENT,這意味著視圖希望足夠大以包含其內容(加上填充)
- c、確切的數字
ViewGroup的不同子類都有LayoutParams的子類,例如,LinearLayout有自己的 LayoutParams子類,
ViewGroup.MarginLayoutParams
支持邊距的布局的每個子布局資訊, ViewGroup.MarginLayoutParams(子組件)的XML屬性及相關方法

ViewGroup寫到這里基本差不多了,更詳細的內容則通過后面的布局和視圖容器也深入了解,
最后
小編學習提升時,順帶從網上收集整理了一些 Android 開發相關的學習檔案、面試題、Android 核心筆記等等檔案,希望能幫助到大家學習提升,如有需要參考的可以直接去我 CodeChina地址:https://codechina.csdn.net/u012165769/Android-T3 訪問查閱,


轉載請註明出處,本文鏈接:https://www.uj5u.com/yidong/289633.html
標籤:其他
上一篇:稍等,我手機幫你遠程除錯下代碼!
