編輯:關於Android編程
整個View樹的繪圖流程是在ViewRoot.java類的performTraversals()函數展開的,該函數做的執行過程可簡單概況為:
- 判斷是否需要重新計算視圖大小(measure)
- 判斷是否重新需要安置視圖的位置(layout)
- 判斷是否需要重繪(draw)
其整個流程圖如下:
圖片來自:Android 開源項目源碼解析 公共技術點中的 View 繪制流程
在Android中View的整個生命周期,調用invalidate和requestLayout會觸發一系列的方法,如圖所示
圖片來自:Android 開源項目源碼解析 公共技術點中的 View 繪制流程<喎?/kf/ware/vc/" target="_blank" class="keylink">vcD4NCrWxv6q3otXftffTw3JlcXVlc3RMYXlvdXS3vbeoyrGjrNa7u+G0pbeibWVhc3VyZbrNbGF5b3V0uf2zzCC1sb+qt6LV37X308NpbnZhbGlkYXRlt723qMqxo6y74bSlt6JkcmF3uf2zzA0KPGgzIGlkPQ=="measure">Measure 為整個View樹計算實際大小,每個View的實際大小由父控件和其本身共同決定 measure方法調用onMeasure方法,onMeasure方法裡通過setMeasuredDimension(注意padding和margin)設置View的大小 ViewGroup子類需要重寫onMeasure去遍歷測量其子View的大小 measure方法是final類型,不能被重寫,需要重寫的是onMeasure方法 整個測量過程就是對View樹的遞歸 一個View一旦測量完成,即可通過getMeasuredWidth() 和 getMeasuredHeight()獲得其寬度和高度 自定義的ViewGroup只需實現measure和layout過程
一個MeasureSpec對象由size和mode組成,MeasureSpec類通過將其封裝在一個int值中以減少對象的分配。其模式有以下三種,都為int型
- UNSPECIFIED
父視圖不對子視圖產生任何約束,如ListView,ScrollView
- EXACTLY
父視圖為子視圖指定一個確切的尺寸,子視圖以這個確切的值作為大小,比如match_parent或具體值20dp
- AT_MOST
父視圖為子視圖指定一個最大尺寸,子視圖必須在這個尺寸大小內,比如wrap_content
public static class MeasureSpec {
private static final int MODE_SHIFT = 30; //移位位數為30
//int類型占32位,向右移位30位,該屬性表示掩碼值,用來與size和mode進行&運算,獲取對應值。
private static final int MODE_MASK = 0x3 << MODE_SHIFT;
//向右移位30位,其值為00 + (30位0) , 即 0x0000(16進制表示)
public static final int UNSPECIFIED = 0 << MODE_SHIFT;
//向右移位30位,其值為01 + (30位0) , 即0x1000(16進制表示)
public static final int EXACTLY = 1 << MODE_SHIFT;
//向右移位30位,其值為02 + (30位0) , 即0x2000(16進制表示)
public static final int AT_MOST = 2 << MODE_SHIFT;
//創建一個整形值,其高兩位代表mode類型,其余30位代表長或寬的實際值。可以是WRAP_CONTENT、MATCH_PARENT或具體大小exactly size
public static int makeMeasureSpec(int size, int mode) {
return size + mode;
}
//獲取模式,與運算
public static int getMode(int measureSpec) {
return (measureSpec & MODE_MASK);
}
//獲取長或寬的實際值,與運算
public static int getSize(int measureSpec) {
return (measureSpec & ~MODE_MASK);
}
}
以上都是理論知識,也差不多是對多篇文章的總結性內容。下面開始實現一個自定義View和ViewGroup
其實自定義View的大部分邏輯都是在onDraw上,onLayout基本上無需重新,onMeasure需要實現測量邏輯。
下面是一個簡單的毫無任何作用的自定義View,其唯一目的就是演示onDraw和onMeasure
package cn.edu.zafu.sourcedemo;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Rect;
import android.util.AttributeSet;
import android.view.View;
/**
* Created by lizhangqu on 2015/5/3.
*/
public class CustomView extends View {
private Paint paint=null;
private Rect rect=null;
private int bgColor=Color.parseColor(#673AB7);//寫死背景色,實際是自定義屬性
private int minContentWidth=50;//最小內容寬度,不包含內邊距
private int minContentHeight=50;//最小內容高度,不包含內邊距
public CustomView(Context context) {
this(context, null);
}
public CustomView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public CustomView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}
/*初始化*/
private void init() {
paint=new Paint();
paint.setAntiAlias(true);
paint.setDither(true);
paint.setColor(bgColor);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
//其實所有邏輯可以簡單調用resolveSize函數進行測量,這裡自己實現一遍,理清思路
//獲得寬度和高度的mode和size
int widthMode = MeasureSpec.getMode(widthMeasureSpec);
int heightMode = MeasureSpec.getMode(heightMeasureSpec);
int widthSize = MeasureSpec.getSize(widthMeasureSpec);
int heightSize = MeasureSpec.getSize(heightMeasureSpec);
//最終的寬高存在這兩個變量中
int width;
int height;
if (widthMode == MeasureSpec.EXACTLY) {
// 父視圖指定了大小
width = widthSize;
} else {
//父視圖指定必須在這個大小內
//注意內邊距,再加上自身需要的寬度
width=getPaddingLeft()+getPaddingRight()+minContentWidth;
if (widthMode == MeasureSpec.AT_MOST) {
//如果是AT_MOST,必須在父控件指定的范圍內,取width和widthSize中小的那個
width = Math.min(width, widthSize);
}
}
if (heightMode == MeasureSpec.EXACTLY) {
// 父視圖指定了大小
height = widthSize;
} else {
//父視圖指定必須在這個大小內
//注意內邊距,再加上自身需要的高度
height =getPaddingTop()+getPaddingBottom()+minContentHeight;
if (heightMode == MeasureSpec.AT_MOST) {
//如果是AT_MOST,必須在父控件指定的范圍內,取width和widthSize中小的那個
height = Math.min(height, heightSize);
}
}
setMeasuredDimension(width, height);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
rect=new Rect(getPaddingLeft(),getPaddingTop(),getMeasuredWidth()-getPaddingRight(),getMeasuredHeight()-getPaddingBottom());//繪制的時候注意內邊距
canvas.drawRect(rect,paint);
}
}
實現一個縱向排布子View的ViewGroup,效果如圖所示,見代碼,解釋看注釋
package cn.edu.zafu.sourcedemo;
import android.content.Context;
import android.util.AttributeSet;
import android.view.View;
import android.view.ViewGroup;
/**
* Created by lizhangqu on 2015/5/3.
*/
public class CustomViewGroup extends ViewGroup {
public CustomViewGroup(Context context) {
this(context, null);
}
public CustomViewGroup(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public CustomViewGroup(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
//重寫onLayout抽象方法
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
final int count = getChildCount();
MyLayoutParams lp = null;
for (int i = 0; i < count; i++) {
View child = getChildAt(i);
//獲得當前View
lp = (MyLayoutParams) child.getLayoutParams();
//獲得LayoutParams,強制轉換為MyLayoutParams
child.layout(lp.x, lp.y, lp.x + child.getMeasuredWidth(), lp.y
+ child.getMeasuredHeight());
//調用當前View的layout方法進行布局
}
}
//重寫onMeasure實現測量邏輯
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int width = 0;
int lastWidth = 0;
int height = getPaddingTop();
final int count = getChildCount();
//獲得子View個數
for (int i = 0; i < count; i++) {
View child = getChildAt(i);
//獲得當前子View
measureChild(child, widthMeasureSpec, heightMeasureSpec);
//測量子View,必須調用
MyLayoutParams lp = (MyLayoutParams) child.getLayoutParams();
//獲得LayoutParams
width = Math.max(width, child.getMeasuredWidth() + lp.leftMargin + lp.rightMargin);
//比較當前View與之前的View寬度,取大者,注意這個寬度包含了margin
lp.x = getPaddingLeft() + lp.leftMargin;
//設置當前View的x左邊
lp.y = height + lp.topMargin;
//設置當前View的y左邊
height = height + lp.topMargin + child.getMeasuredHeight() + lp.bottomMargin;
//累加高度
}
width=width+getPaddingLeft() + getPaddingRight();
//加上左右內邊距
height = height + getPaddingBottom();
//加上下邊界
setMeasuredDimension(resolveSize(width, widthMeasureSpec), resolveSize(height, heightMeasureSpec));
//設置寬高,resolveSize方法會根據尺寸大小和MeasureSpec計算最佳大小
}
//重寫生成LayoutParams的三個方法
@Override
public MyLayoutParams generateLayoutParams(AttributeSet attrs) {
return new MyLayoutParams(getContext(), attrs);
}
//重寫生成LayoutParams的三個方法
@Override
protected MyLayoutParams generateDefaultLayoutParams() {
return new MyLayoutParams(LayoutParams.WRAP_CONTENT,
LayoutParams.WRAP_CONTENT);
}
//重寫生成LayoutParams的三個方法
@Override
protected MyLayoutParams generateLayoutParams(LayoutParams p) {
return new MyLayoutParams(p.width, p.height);
}
//繼承MarginLayoutParams實現自己的LayoutParams,x,y代表控件的左邊和上邊左邊
public static class MyLayoutParams extends MarginLayoutParams {
public int x;//左
public int y;//上
public MyLayoutParams(Context context, AttributeSet attrs) {
super(context, attrs);
}
public MyLayoutParams(int w, int h) {
super(w, h);
}
}
}
嵌入sdk是比較常見的吧,如接入廣告啊,新浪的分享...... 這些都需要接入sdk的,現在我們從最基本的做吧,其實這些和什麼引擎都是沒多大關系的,不管是unity,還是
一、簡介 1、地圖類型 百度地圖Android SDK 3.7.1提供了兩種類型的地圖資源(普通矢量地圖和衛星圖),開發者可以利用BaiduMap中的
進入多核時代已經很久了,大數據概念也吵得沸沸揚揚,不管你喜歡不喜歡,不管你遇到沒遇到,big-data或bigger-data都必須正視.處理大數據,基本都
使用Scroller實現絢麗的ListView左右滑動刪除Item效果這裡來給大家帶來使用Scroller的小例子,同時也能用來幫助初步解除的讀者更加熟悉的掌握Scrol