編輯:關於Android編程
支持wrap_content
即當ViewGroup的寬、高使用wrap-content時,ViewGroup的高寬根據子View的實際大小來確定
如果你不處理的話,“wrap-content”的和 “match-parent”是一樣的
ViewGroup支持Padding
其子View支持margin
支持自定義屬性
例如:gravity
滑動事件沖突處理
touch事件分發
接下來,我們一步一步實現以上內容
onMeasure的寫法
onLayout的寫法
onMeasure中widthMeasureSpec、heightMeasureSpec的理解
這裡不具體解釋這個問題,只需知道,就是因為MeasureSpec的規則導致了,如果你不進行特殊處理,致使wrap-content的效果為match-parent
展開說一句:兒子的尺寸,不完全由兒子自己決定,與父親傳遞過來的測量規格是有關系的
onLayout中l,t,r,b的含義
解釋一下:int l, int t, int r, int b,這四個值
這四個值,就是通過onMeasure方法測量後,得到的這個MyViewGroup的左上右下值
注意:l,t,r,b是考慮了其所在的父ViewGroup的padding值的(MyviewGroup自身的padding值,當然也考慮進去了)
這四個值,大家可以打印出來看看就明白了,這個不清楚的話,很難寫好onLayout方法
l=自定義ViewGroup的父ViewGroup的leftPadding
t=自定義ViewGroup的父ViewGroup的topPadding
r=自定義ViewGroup的父ViewGroup的leftPadding+這個MyViewGroup的寬度(即上文的desireWidth)
t=自定義ViewGroup的父ViewGroup的topPadding+這個MyViewGroup的高度(即上文的desireHeight)
大家都知道,自定義View要經過onMeasure、onLayout、onDraw三個流程
要測量其內部所有的子View的寬高
measureChild(View child, int parentWidthMeasureSpec,
int parentHeightMeasureSpec)
measure(int widthMeasureSpec, int heightMeasureSpec)
上面兩個方法都可以使用,意思是一樣的
第一個是,測量每一個子View的方法 第二個是,讓每一個子View去測量自己的方法測量ViewGroup自己的寬高
你要是懶得支持wrap_content,只需要寫
super.onMeasure(widthMeasureSpec,heightMeasureSpec);
即可,
會調用父類的流程去測量你自定義的ViewGroup的寬高(不具體展開源碼,源碼其實調用的是setMeasuredDimension(int measuredWidth, int measuredHeight)
)
不支持wrap-content的意思就說:當你設置寬高為wrap-content時候,實際的效果卻是match_content
你要是支持wrap_content的話,
使用下面的方法來測量ViewGroup自身的寬高
setMeasuredDimension(resolveSize(desireWidth, widthMeasureSpec),
resolveSize(desireHeight, heightMeasureSpec));
你肯定想當你的ViewGroup寬度為wrap-content時,這個ViewGroup的寬度值是所有子View的寬度之和再加上左右padding(先不考慮子View的margin),
這就需要你在測量每一個子View的寬度時,累加所有的子View寬度再加上左右padding值作為ViewGroup的寬度(當ViewGroup的寬度值你設置的是wrap_content時,你要是設100dp,那就直接是100dp了),
由於本例是橫向排列的ViewGroup,所以,ViewGroup的高度就是子View裡最高的那個子View的高度再加上上下padding了
代碼裡的:
desireWidth=所有子View的寬度累加之和+左右padding
desireHeight=最高的那個子View的高度+上下padding
onMeasure模板代碼:(只是支持wrap_content和padding,還不支持margin)
` @Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
// ★1. 計算所有child view 要占用的空間
desireWidth = 0;//累加所有子View的寬度,作為MyViewGroup寬度設為wrap_content時的寬度
desireHeight = 0;//本例子是橫向排列的ViewGroup,所以,MyViewGroup高度設為wrap_content時,其高度是其所有子View中最高子View的高度
int count = getChildCount();
for (int i = 0; i < count; ++i) {
View v = getChildAt(i);
//1.1 開始遍歷測量所有的子View,並且根據實際情況累加子View的寬(或者高),為了計算整個viewgroup的寬度(高度)
if (v.getVisibility() != View.GONE) {//不去測量Gone的子View
measureChild(v, widthMeasureSpec,
heightMeasureSpec);
--------------只需要根據你的需求,更改虛線內部的代碼即可,其余的不用改--------------
//由於橫向排列,累加所有的子View的寬度
desireWidth += v.getMeasuredWidth();
//高度是子View中最高的高度
desireHeight = Math
.max(desireHeight, v.getMeasuredHeight());
--------------只需要根據你的需求,更改虛線內部的代碼即可,其余的不用改--------------
}
}
// 1.2 考慮padding值
//到目前為止desireWidth為所有子View的寬度的累加,作為MyViewGroup的總寬度,要加上左右padding值
desireWidth += getPaddingLeft() + getPaddingRight();
//高度同理略
desireHeight += getPaddingTop() + getPaddingBottom();
//★2.測量ViewGroup的寬高,如果不寫這一步,使用wrap_content時效果為match_parent的效果
// (下面的寫法比較簡潔,《Android群英傳》介紹了另外一種寫法,比這個稍微麻煩一點)
// see if the size is big enough
desireWidth = Math.max(desireWidth, getSuggestedMinimumWidth());
desireHeight = Math.max(desireHeight, getSuggestedMinimumHeight());
// super.onMeasure(widthMeasureSpec,heightMeasureSpec);
setMeasuredDimension(resolveSize(desireWidth, widthMeasureSpec),
resolveSize(desireHeight, heightMeasureSpec));
}`
` @Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
//這個自定義的ViewGroup的有效內容的四個邊界
final int parentLeft = getPaddingLeft();//這個MyViewGroup的最左邊的距離(純粹的內容的左邊界,不含padding)
final int parentTop = getPaddingTop();//這個MyViewGroup的最上邊的距離(純粹的內容的上邊界,不含padding)
final int parentRight = r - l - getPaddingRight();//這個MyViewGroup的最右邊的距離(純粹的內容的右邊界,不含padding)
final int parentBottom = b - t - getPaddingBottom();//這個MyViewGroup的最下邊的距離(純粹的內容的下邊界,不含padding)
if (BuildConfig.DEBUG)
Log.d("onlayout", "parentleft: " + parentLeft + " parenttop: "
+ parentTop + " parentright: " + parentRight
+ " parentbottom: " + parentBottom+"\n"+ " l: " + l+ " t: " + t+ " r: " + r+ " b: " + b);
int left = parentLeft;
int top = parentTop;
int count = getChildCount();
for (int i = 0; i < count; i++) {
View v = getChildAt(i);
if (v.getVisibility() != View.GONE) {
//得到每一個子View的測量後的寬高
final int childWidth = v.getMeasuredWidth();
final int childHeight = v.getMeasuredHeight();
//開始布局每一個子View(左、上、右=左+子View寬、下=上+子View高)
v.layout(left, top, left + childWidth, top + childHeight);
//由於本例是橫向排列的,所以每一個子View的left值要遞增
left += childWidth;
}
}
}`
完整代碼:
`
public class MyViewGroup extends ViewGroup {
private int desireWidth;
private int desireHeight;
public MyViewGroup(Context context) {
this(context, null);
}
public MyViewGroup(Context context, AttributeSet attrs) {
super(context, attrs);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
// ★1. 計算所有child view 要占用的空間
desireWidth = 0;//累加所有子View的寬度,作為MyViewGroup寬度設為wrap_content時的寬度
desireHeight = 0;//本例子是橫向排列的ViewGroup,所以,MyViewGroup高度設為wrap_content時,其高度是其所有子View中最高子View的高度
int count = getChildCount();
for (int i = 0; i < count; ++i) {
View v = getChildAt(i);
//1.1 開始遍歷測量所有的子View,並且根據實際情況累加子View的寬(或者高),為了計算整個viewgroup的寬度(高度)
if (v.getVisibility() != View.GONE) {//不去測量Gone的子View
measureChild(v, widthMeasureSpec,
heightMeasureSpec);
//由於橫向排列,累加所有的子View的寬度
desireWidth += v.getMeasuredWidth();
//高度是子View中最高的高度
desireHeight = Math
.max(desireHeight, v.getMeasuredHeight());
}
}
// 1.2 考慮padding值
//到目前為止desireWidth為所有子View的寬度的累加,作為MyViewGroup的總寬度,要加上左右padding值
desireWidth += getPaddingLeft() + getPaddingRight();
//高度同理略
desireHeight += getPaddingTop() + getPaddingBottom();
//★2.測量ViewGroup的寬高,如果不寫這一步,使用wrap_content時效果為match_parent的效果
// (下面的寫法比較簡潔,《Android群英傳》介紹了另外一種寫法,比這個稍微麻煩一點)
// see if the size is big enough
desireWidth = Math.max(desireWidth, getSuggestedMinimumWidth());
desireHeight = Math.max(desireHeight, getSuggestedMinimumHeight());
//super.onMeasure(widthMeasureSpec,heightMeasureSpec);
setMeasuredDimension(resolveSize(desireWidth, widthMeasureSpec),
resolveSize(desireHeight, heightMeasureSpec));
}
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
//這個自定義的ViewGroup的有效內容的四個邊界
final int parentLeft = getPaddingLeft();//這個MyViewGroup的最左邊的距離(純粹的內容的左邊界,不含padding)
final int parentTop = getPaddingTop();//這個MyViewGroup的最上邊的距離(純粹的內容的上邊界,不含padding)
final int parentRight = r - l - getPaddingRight();//這個MyViewGroup的最右邊的距離(純粹的內容的右邊界,不含padding)
final int parentBottom = b - t - getPaddingBottom();//這個MyViewGroup的最下邊的距離(純粹的內容的下邊界,不含padding)
if (BuildConfig.DEBUG)
Log.d("onlayout", "parentleft: " + parentLeft + " parenttop: "
+ parentTop + " parentright: " + parentRight
+ " parentbottom: " + parentBottom + "\n" + " l: " + l + " t: " + t + " r: " + r + " b: " + b);
int left = parentLeft;
int top = parentTop;
int count = getChildCount();
for (int i = 0; i < count; i++) {
View v = getChildAt(i);
if (v.getVisibility() != View.GONE) {
//得到每一個子View的測量後的寬高
final int childWidth = v.getMeasuredWidth();
final int childHeight = v.getMeasuredHeight();
//開始布局每一個子View(左、上、右=左+子View寬、下=上+子View高)
v.layout(left, top, left + childWidth, top + childHeight);
//由於本例是橫向排列的,所以每一個子View的left值要遞增
left += childWidth;
}
}
}
}
`
一般情況,自定義ViewGroup支持wrap-content和padding就夠用了
每一個自定義ViewGroup都必須,自定義這個類LayoutParams,以及後面的三個方法,否則強轉報異常,
其余沒什麼好說的,計算距離,布局的時候把margin考慮進去即可
`public class MyViewGroupMargin extends ViewGroup {
private int desireWidth;
private int desireHeight;
public MyViewGroupMargin(Context context) {
this(context, null);
}
public MyViewGroupMargin(Context context, AttributeSet attrs) {
super(context, attrs);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
// ★1. 計算所有child view 要占用的空間
desireWidth = 0;//累加所有子View的寬度,作為MyViewGroup寬度設為wrap_content時的寬度
desireHeight = 0;//本例子是橫向排列的ViewGroup,所以,MyViewGroup高度設為wrap_content時,其高度是其所有子View中最高子View的高度
int count = getChildCount();
for (int i = 0; i < count; ++i) {
View v = getChildAt(i);
//1.1 開始遍歷測量所有的子View,並且根據實際情況累加子View的寬(或者高),為了計算整個viewgroup的寬度(高度)
if (v.getVisibility() != View.GONE) {//不去測量Gone的子View
LayoutParams lp = (LayoutParams) v.getLayoutParams();
//▲變化1:將measureChild改為measureChildWithMargin
measureChildWithMargins(v, widthMeasureSpec, 0,
heightMeasureSpec, 0);
/*原來: measureChild(v, widthMeasureSpec,
heightMeasureSpec);*/
//▲變化2:這裡在累加所有的子View的寬度時加上他自己的margin
desireWidth += v.getMeasuredWidth() + lp.leftMargin + lp.rightMargin;
desireHeight = Math
.max(desireHeight, v.getMeasuredHeight() + lp.topMargin + lp.bottomMargin);
/*原來://由於橫向排列,累加所有的子View的寬度
desireWidth += v.getMeasuredWidth();
//高度是子View中最高的高度
desireHeight = Math
.max(desireHeight, v.getMeasuredHeight());*/
}
}
// 1.2 考慮padding值
//到目前為止desireWidth為所有子View的寬度的累加,作為MyViewGroup的總寬度,要加上左右padding值
desireWidth += getPaddingLeft() + getPaddingRight();
//高度同理略
desireHeight += getPaddingTop() + getPaddingBottom();
//★2.測量ViewGroup的寬高,如果不寫這一步,使用wrap_content時效果為match_parent的效果
// (下面的寫法比較簡潔,《Android群英傳》介紹了另外一種寫法,比這個稍微麻煩一點)
// see if the size is big enough
desireWidth = Math.max(desireWidth, getSuggestedMinimumWidth());
desireHeight = Math.max(desireHeight, getSuggestedMinimumHeight());
setMeasuredDimension(resolveSize(desireWidth, widthMeasureSpec),
resolveSize(desireHeight, heightMeasureSpec));
}
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
final int parentLeft = getPaddingLeft();
final int parentRight = r - l - getPaddingRight();
final int parentTop = getPaddingTop();
final int parentBottom = b - t - getPaddingBottom();
if (BuildConfig.DEBUG)
Log.d("onlayout", "parentleft: " + parentLeft + " parenttop: "
+ parentTop + " parentright: " + parentRight
+ " parentbottom: " + parentBottom);
int left = parentLeft;
int top = parentTop;
int count = getChildCount();
for (int i = 0; i < count; ++i) {
View v = getChildAt(i);
if (v.getVisibility() != View.GONE) {
LayoutParams lp = (LayoutParams) v.getLayoutParams();
final int childWidth = v.getMeasuredWidth();
final int childHeight = v.getMeasuredHeight();
//▲變化1:左側要加上這個子View的左側margin
left += lp.leftMargin;
//▲變化2:上側要加上子View的margin
top = parentTop + lp.topMargin;
if (BuildConfig.DEBUG) {
Log.d("onlayout", "child[width: " + childWidth
+ ", height: " + childHeight + "]");
Log.d("onlayout", "child[left: " + left + ", top: "
+ top + ", right: " + (left + childWidth)
+ ", bottom: " + (top + childHeight));
}
v.layout(left, top, left + childWidth, top + childHeight);
//▲變化3:因為是橫向排列的,所以下一個View的左側加上這個view的右側的margin(如果是縱向排列的則對應改變top)
left += childWidth + lp.rightMargin;
}
}
}
//★★★★★★★★★★★★★★★★★★★★★★★要使用margin必須寫下面的方法★★★★★★★★★★★★★★★★★★★★★
//***開始***每一個自定義ViewGroup都必須,自定義這個類LayoutParams,以及後面的三個方法,否則強轉報異常,模板代碼照抄即可**************
public static class LayoutParams extends MarginLayoutParams {
public LayoutParams(Context c, AttributeSet attrs) {
super(c, attrs);
}
public LayoutParams(int width, int height) {
super(width, height);
}
public LayoutParams(ViewGroup.LayoutParams source) {
super(source);
}
public LayoutParams(MarginLayoutParams source) {
super(source);
}
}
@Override
protected ViewGroup.LayoutParams generateDefaultLayoutParams() {
return new LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.MATCH_PARENT);
}
@Override
public ViewGroup.LayoutParams generateLayoutParams(
AttributeSet attrs) {
return new LayoutParams(getContext(), attrs);
}
@Override
protected ViewGroup.LayoutParams generateLayoutParams(
ViewGroup.LayoutParams p) {
return new LayoutParams(p);
}
//***結束***每一個自定義ViewGroup都必須,自定義這個類LayoutParams,以及後面的三個方法,否則強轉報異常,模板代碼照抄即可**************
}
`
對於大多數android開發者來說,ViewPager和ListView是再熟悉不過了,ViewPager的實現思路和ListView大同小異,具體參照前面的ListVi
1.基本思路①.創建已加鎖應用的數據庫(字段:_id,packagename),如果應用已加鎖,將加鎖應用的包名維護到數據庫中②.已加鎖+未加鎖 == 手機中所有應用(A
一、設置重復背景 在drawable文件夾下建一個mybackground.xml文件 在文件中寫入: tileMode 屬性就是用於定義背景的顯示模式:
在使用android類的手寫應用時,整體上都有這樣一個印象:android的手寫不流暢、不自然,和蘋果應用比起來相差太遠。本文結合作者親身經歷,介紹一下有效提高手寫流暢度