android 开发中难免会遇到自定义view,或者自定义的layout。那么自定义的view或者layout应该注意哪些东西呢?
注意事项:
自定义view:
1,一般需要重载两个方法,一个是onMeasure(),用来测量控件尺寸,另一个是onDraw(),用来绘制控件的UI。
这一点我在android UI——2D绘图中应该注意的那些坑中已经清楚的说明了重载这两个方法的目的和原因。
onMeasure():在View第一次加载的时候会调用一次或者在系统认为需要重绘的时候也会被调用。
那么,如何让系统认为view需要重绘呢?
通过调用View的invalidate()函数或者postInvalidate()函数即可,前者用于UI线程,后者用于非UI线程。
注意,onDraw()每次被调用的时候,原来画布中的内容会被清空。
2,如果需要在自定义view中添加启动动画那么需要在onDraw()方法里面去处理postInvalidateDelayed(10)方法。
例如:我们绘制一条竖线,坐标是从(0,50%)到(100%,50%);这里百分比指具体屏幕的坐标。
那么我们可以利用view的重绘来实现动画。在onDraw()方法中调用postInvalidateDelayed(10)方法来不停的重绘达到动画的效果。
@Override
protected void onDraw(Canvas canvas) {
//super.onDraw(canvas);
startY -= 20;
//垂直的横线
canvas.drawLine(startX, startY, endX, endY, paint);
if (startY >= height/2)
postInvalidateDelayed(10);
}
只要调用了postInvalidateDelayed(10),这个view就会重新调用onDraw()方法,我们不断的改变绘制的坐标就可以实现进入动画了。
自定义layout:
1,ViewGroup类的onLayout()函数是abstract型,继承者必须实现。
2,重载onMeasure()方法的时候,需要注意的是,自定义ViewGroup的onMeasure()方法中,除了计算自身的尺寸外,还需要调用measureChildren()函数来计算子控件的尺寸。
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
measureChildren(widthMeasureSpec, heightMeasureSpec);
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
3,onlayout中获得总宽度是多少,这个值可以通过getMeasuredWidth()来得到,当然子控件的宽度也可以通过子控件对象的getMeasuredWidth()来得到。
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
int mViewGroupWidth = getMeasuredWidth(); //当前ViewGroup的总宽度
int mPainterPosX = left; //当前绘图光标横坐标位置
int mPainterPosY = top; //当前绘图光标纵坐标位置
int childCount = getChildCount();
for ( int i = 0; i < childCount; i++ ) {
View childView = getChildAt(i);
int width = childView.getMeasuredWidth();
int height = childView.getMeasuredHeight();
}
}
4,自定义layout的margin参数无效。
3中讲到的代码是没有处理设置margin后的结果。
那么我们如何去处理margin呢?
如果要自定义ViewGroup支持子控件的layout_margin参数,则自定义的ViewGroup类必须重载generateLayoutParams()函数。
并且在该函数中返回一个ViewGroup.MarginLayoutParams派生类对象,这样才能使用margin参数。
我们可以在自定义layout中创建一个静态内部类:
public class CustomViewGroup extends ViewGroup {
public static class LayoutParams extends ViewGroup.MarginLayoutParams {
public LayoutParams(Context c, AttributeSet attrs) {
super(c, attrs);
}
}
@Override
public LayoutParams generateLayoutParams(AttributeSet attrs) {
return new CustomViewGroup.LayoutParams(getContext(), attrs);
}
}
5,例子:自定义流布局
这个onLayout是借鉴的别人的
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
int mViewGroupWidth = getMeasuredWidth(); //当前ViewGroup的总宽度
int mViewGroupHeight = getMeasuredHeight(); //当前ViewGroup的总高度
int mPainterPosX = left; //当前绘图光标横坐标位置
int mPainterPosY = top; //当前绘图光标纵坐标位置
int childCount = getChildCount();
for ( int i = 0; i < childCount; i++ ) {
View childView = getChildAt(i);
int width = childView.getMeasuredWidth();
int height = childView.getMeasuredHeight();
CustomViewGroup.LayoutParams margins = (CustomViewGroup.LayoutParams)(childView.getLayoutParams());
//ChildView占用的width = width+leftMargin+rightMargin
//ChildView占用的height = height+topMargin+bottomMargin
//如果剩余的空间不够,则移到下一行开始位置
if( mPainterPosX + width + margins.leftMargin + margins.rightMargin > mViewGroupWidth ) {
mPainterPosX = left;
mPainterPosY += height + margins.topMargin + margins.bottomMargin;
}
//执行ChildView的绘制
childView.layout(mPainterPosX+margins.leftMargin, mPainterPosY+margins.topMargin,mPainterPosX+margins.leftMargin+width, mPainterPosY+margins.topMargin+height);
mPainterPosX += width + margins.leftMargin + margins.rightMargin;
}
}