近來忙着銀聯支付接口對接,部落格也沒有更新。在前幾篇Android自定義View基礎篇當中有幾處地方明顯不足。例如:view中使用handler 就不合理,view中自帶了post方法。在view小結篇當中主要講解自定義過程中的一些疑問及踩過的坑。
1、自定義View中的wrap_content
從ViewRoot的performTraversals開始,經過measure,layout,draw 三個流程。draw流程結束以後就可以在螢幕上看到view了。
經常我們會遇到自定義View在xml中設定wrap_content屬性效果跟match_parent一樣的。這個時候我們就需要在代碼中處理了:
int widthSpecMode = MeasureSpec.getMode(widthMeasureSpec);
int widthSpecSize = MeasureSpec.getSize(widthMeasureSpec);
int heightSpecMode = MeasureSpec.getMode(heightMeasureSpec);
int heightSpecSize = MeasureSpec.getSize(heightMeasureSpec);
if (widthSpecMode == MeasureSpec.AT_MOST && heightSpecMode == MeasureSpec.AT_MOST) {
setMeasuredDimension(, );
} else if (widthSpecMode == MeasureSpec.AT_MOST) {
setMeasuredDimension(, heightSpecSize);
} else if (heightSpecMode == MeasureSpec.AT_MOST) {
setMeasuredDimension(widthSpecSize, );
}
一般的處理方式是固定寬高,MeasureSpec.AT_MOST模式對應的xml布局中的wrap_content
2、自定義View中的padding
如果你繼承View,一般情況下在onDraw()方法處理;如果你繼承viewGroup,那在onMeasure和onLayout裡面也要考慮。
final int paddingLeft = getPaddingLeft();
final int paddingRight = getPaddingRight();
final int paddingTop = getPaddingTop();
final int paddingBottom = getPaddingBottom();
擷取目前控件的padding值。
3、線程、動畫
view提供了post方法處理線程,view的動畫或者線程需要停止,可以考慮在onDetachedFromWindow裡面來做。
4、在activity擷取View的寬高
measure的過程和activity的生命周期沒有任何關系。你無法确定在哪個生命周期執行完畢以後 view的measure過程一定完成。你可以使用如下幾種方法來擷取。
方法一:
@Override
public void onWindowFocusChanged(boolean hasFocus) {
super.onWindowFocusChanged(hasFocus);
if (hasFocus) {
int width = custom.getMeasuredWidth();
int height = custom.getMeasuredHeight();
}
}
方法二:
@Override
protected void onStart() {
super.onStart();
custom.post(new Runnable() {
@Override
public void run() {
int width = custom.getMeasuredWidth();
int height = custom.getMeasuredHeight();
}
});
}
方法三:
@Override
protected void onStart() {
super.onStart();
ViewTreeObserver observer = custom.getViewTreeObserver();
observer.addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
@TargetApi(Build.VERSION_CODES.JELLY_BEAN)
@Override
public void onGlobalLayout() {
int width = custom.getMeasuredWidth();
int height = custom.getMeasuredHeight();
custom.getViewTreeObserver().removeOnGlobalLayoutListener(this);
}
});
}
5、簡單案例
繼承View的案例:
public class CircleView extends View {
final Paint paint = new Paint();
public CircleView(Context context) {
this(context, null);
}
public CircleView(Context context, AttributeSet attrs) {
this(context, attrs, );
}
public CircleView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init(context);
}
private void init(Context context) {
paint.setAntiAlias(true);
paint.setStyle(Paint.Style.STROKE);
paint.setColor(Color.RED);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int widthSpecMode = MeasureSpec.getMode(widthMeasureSpec);
int widthSpecSize = MeasureSpec.getSize(widthMeasureSpec);
int heightSpecMode = MeasureSpec.getMode(heightMeasureSpec);
int heightSpecSize = MeasureSpec.getSize(heightMeasureSpec);
if (widthSpecMode == MeasureSpec.AT_MOST && heightSpecMode == MeasureSpec.AT_MOST) {
setMeasuredDimension(, );
} else if (widthSpecMode == MeasureSpec.AT_MOST) {
setMeasuredDimension(, heightSpecSize);
} else if (heightSpecMode == MeasureSpec.AT_MOST) {
setMeasuredDimension(widthSpecSize, );
}
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
//處理padding情況
final int paddingLeft = getPaddingLeft();
final int paddingRight = getPaddingRight();
final int paddingTop = getPaddingTop();
final int paddingBottom = getPaddingBottom();
int width = getWidth() - paddingLeft - paddingRight;
int height = getHeight() - paddingTop - paddingBottom;
int radius = (int) (Math.min(width, height) / * (f));
canvas.drawCircle(getWidth() / + paddingLeft, getHeight() / + paddingTop, radius, paint);
}
@Override
public boolean postDelayed(Runnable action, long delayMillis) {
return super.postDelayed(action, delayMillis);
}
/**
* 從窗體上分離
*/
@Override
protected void onDetachedFromWindow() {
super.onDetachedFromWindow();
}
}
繼承ViewGroup:
public class CustomHorizontalLayout extends ViewGroup {
//設定預設的控件最小是多少 這裡不提供自定義屬性了 寫死在代碼裡 你們可以自行拓展
final int minHeight = ;
final int minWidth = ;
public CustomHorizontalLayout(Context context) {
this(context, null);
}
public CustomHorizontalLayout(Context context, AttributeSet attrs) {
this(context, attrs, );
}
public CustomHorizontalLayout(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int measureWidth = ;
int measureHeight = ;
final int childCount = getChildCount();
measureChildren(widthMeasureSpec, heightMeasureSpec);
int widthSpecMode = MeasureSpec.getMode(widthMeasureSpec);
int widthSpecSize = MeasureSpec.getSize(widthMeasureSpec);
int heightSpecMode = MeasureSpec.getMode(heightMeasureSpec);
int heightSpecSize = MeasureSpec.getSize(heightMeasureSpec);
final int paddingLeft = getPaddingLeft();
final int paddingRight = getPaddingRight();
final int paddingTop = getPaddingTop();
final int paddingBottom = getPaddingBottom();
//沒有子控件
if (childCount == ) {
if (widthSpecMode == MeasureSpec.AT_MOST || heightSpecMode == MeasureSpec.AT_MOST) {
setMeasuredDimension(minWidth, minHeight);
} else {
//否則根據我們的layout屬性來
setMeasuredDimension(getLayoutParams().width, getLayoutParams().height);
}
} else if (widthSpecMode == MeasureSpec.AT_MOST && heightSpecMode == MeasureSpec.AT_MOST) {
for (int i = ; i < childCount; i++) {
final View view = getChildAt(i);
measureWidth += view.getMeasuredWidth();
measureHeight = measureHeight >= view.getMeasuredHeight() ? measureHeight : view.getMeasuredHeight();
}
setMeasuredDimension(paddingLeft + measureWidth + paddingRight, paddingTop + measureHeight + paddingBottom);
} else if (heightSpecMode == MeasureSpec.AT_MOST) {
for (int i = ; i < childCount; i++) {
final View view = getChildAt(i);
measureHeight = measureHeight >= view.getMeasuredHeight() ? measureHeight :view.getMeasuredHeight();
}
setMeasuredDimension(paddingLeft + paddingRight + widthSpecSize, paddingTop + paddingBottom + measureHeight);
} else if (widthSpecMode == MeasureSpec.AT_MOST) {
for (int i = ; i < childCount; i++) {
final View view = getChildAt(i);
measureWidth += view.getMeasuredWidth();
}
setMeasuredDimension(paddingLeft + paddingRight + measureWidth, paddingTop + paddingBottom + heightSpecSize);
}
}
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
final int paddingLeft = getPaddingLeft();
final int paddingRight = getPaddingRight();
final int paddingTop = getPaddingTop();
final int paddingBottom = getPaddingBottom();
//左邊初始位置為0
int childLeft = + paddingLeft;
final int childCount = getChildCount();
for (int i = ; i < childCount; i++) {
final View childView = getChildAt(i);
if (childView.getVisibility() == View.GONE) {
continue;
}
final int childWidth = childView.getMeasuredWidth();
childView.layout(childLeft, + paddingTop, childLeft + childWidth, paddingTop + childView.getMeasuredHeight());
childLeft += childWidth;
}
}
}
如有什麼疑問,請給我留言。