答:如果没有对wrap_content做处理 ,那即使你在xml里设置为wrap_content.其效果也和match_parent相同。看问题4的分析。我们可以知道view自己的layout为wrap,那mode就是at_most(不管父亲view是什么specmode).
这种模式下宽高就是等于specSize(getDefaultSize函数分析可知),而这里的specSize显然就是parentSize的大小。也就是父容器剩余的大小。那不就和我们直接设置成match_parent是一样的效果了么?
解决方式就是在onMeasure里 针对wrap 来做特殊处理 比如指定一个默认的宽高,当发现是wrap_content 就设置这个默认宽高即可。
8.ViewGroup有onMeasure方法吗?为什么?
答:没有,这个方法是交给子类自己实现的。不同的viewgroup子类 肯定布局都不一样,那onMeasure索性就全部交给他们自己实现好了。
9.为什么在activity的生命周期里无法获得测量宽高?有什么方法可以解决这个问题吗?
答:因为measure的过程和activity的生命周期 没有任何关系。你无法确定在哪个生命周期执行完毕以后 view的measure过程一定走完。可以尝试如下几种方法 获取view的测量宽高。
//重写activity的这个方法 public void onWindowFocusChanged(boolean hasFocus) { super.onWindowFocusChanged(hasFocus); if (hasFocus) { int width = tv.getMeasuredWidth(); int height = tv.getMeasuredHeight(); Log.v("burning", "width==" + width); Log.v("burning", "height==" + height); } }
或者重写这个方法
@Override protected void onStart() { super.onStart(); tv.post(new Runnable() { @Override public void run() { int width = tv.getMeasuredWidth(); int height = tv.getMeasuredHeight(); } }); }
再或者:
@Override protected void onStart() { super.onStart(); ViewTreeObserver observer = tv.getViewTreeObserver(); observer.addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() { @Override public void onGlobalLayout() { int width = tv.getMeasuredWidth(); int height = tv.getMeasuredHeight(); tv.getViewTreeObserver().removeOnGlobalLayoutListener(this); } }); }
10.layout和onLayout方法有什么区别?
答:layout是确定本身view的位置 而onLayout是确定所有子元素的位置。layout里面 就是通过serFrame方法设设定本身view的 四个顶点的位置。这4个位置以确定 自己view的位置就固定了
然后就调用onLayout来确定子元素的位置。view和viewgroup的onlayout方法都没有写。都留给我们自己给子元素布局
11.draw方法 大概有几个步骤?
答: 一共是4个步骤, 绘制背景---------绘制自己--------绘制chrildren----绘制装饰。
12.setWillNotDraw方法有什么用?
答:这个方法在view里。
/** * If this view doesn't do any drawing on its own, set this flag to * allow further optimizations. By default, this flag is not set on * View, but could be set on some View subclasses such as ViewGroup. * * Typically, if you override {@link #onDraw(android.graphics.Canvas)} * you should clear this flag. * * @param willNotDraw whether or not this View draw on its own */ public void setWillNotDraw(boolean willNotDraw) { setFlags(willNotDraw ? WILL_NOT_DRAW : 0, DRAW_MASK); }
用于设置标志位的 也就是说 如果你的自定义view 不需要draw的话,就可以设置这个方法为true。这样系统知道你这个view 不需要draw 可以优化执行速度。viewgroup 一般都默认设置这个为true,因为viewgroup多数都是只负责布局
不负责draw的。而view 这个标志位 默认一般都是关闭的。
13.自定义view 有哪些需要注意的点?
答:主要是要处理wrap_content 和padding。否则xml 那边设置这2个属性就根本没用了。还有不要在view中使用handler 因为人家已经提供了post方法。如果是继承自viewGroup,那在onMeasure和onLayout里面 也要考虑
padding和layout的影响。也就是说specSize 要算一下 。最后就是如果view的动画或者线程需要停止,可以考虑在onDetachedFromWindow里面来做。
针对上述的几点,给出几个简单的自定义view 供大家理解。
给出一个圆形的view 范例:
package com.example.administrator.motioneventtest; import android.content.Context; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; import android.util.AttributeSet; import android.view.View; /** * Created by Administrator on 2016/2/4. */ public class CircleView extends View { private int mColor = Color.RED; private Paint mPaint = new Paint(Paint.ANTI_ALIAS_FLAG); private void init() { mPaint.setColor(mColor); } @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); //处理为wrap_content时的情况 if (widthSpecMode == MeasureSpec.AT_MOST && heightSpecMode == MeasureSpec.AT_MOST) { setMeasuredDimension(200, 200); } else if (widthSpecMode == MeasureSpec.AT_MOST) { setMeasuredDimension(200, heightSpecSize); } else if (heightSpecMode == MeasureSpec.AT_MOST) { setMeasuredDimension(widthSpecSize, 200); } } @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 = Math.min(width, height) / 2; canvas.drawCircle(paddingLeft + width / 2, paddingTop + height / 2, radius, mPaint); } public CircleView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); init(); } public CircleView(Context context) { super(context); init(); } public CircleView(Context context, AttributeSet attrs) { super(context, attrs); init(); } }
然后下面再给出一个范例,稍微复杂一点是自定义viewgroup了(主要是加强对onMeasure和onLayout的理解), 需求如下:
一个水平的viewgroup,内部的子元素 为了简单 我们假定他们的宽高都是一样的。来写一个这样的简单的viewgroup。