前些天去面试,被考官为了一个问题,
如果我想自定义一个viewgroup,并且自带一条线,我们需要调用哪个方法?
关于自定义view之前我也看过一些文章,但是其实不能理解里面真正的东西,大致只是知道三个方法measure、layout、draw,所以没有答上来。
这里做个总结:
1、 onmeasure
如果控件是viewgroup类型,也就是 容器类控件,比如相对布局、线性布局、viewpager 等。
那么onmeasure方法里面重写的代码就是与 布局里面的孩子 设置view 尺寸大小相关的代码,这里设置的大小不一定是死的,只是给一个期望的大小,因为子view的大小最终还是以自己设置为主!
容器类控件
这个方法作用是:
(1)给childView计算出建议的宽和高和测量模式;
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
//...
}
int widthMeasureSpec, int heightMeasureSpec
看出2个参数就是从父容器传过来的。
子View的测量规格如何获取???
viewgroup的measureChild( )方法会根据ViewGroup的测量规格和子View的LayoutParams生成子View的测量规格,然后传递给子View的measure方法进行测量。
注:MeasureSpec代表一个32位的int值,高两位代表SpecMode,低30位表示SpecSize。SpecMode是指测量模式,而SpecSize是指在某种测量模式下的规格大小。
UNSPECIFIED :父容器没有给当前View强加任何约束,View想多大就多大,此种情况一般用于系统内部。
EXACTLY : 父容器已经决定了当前View的准确的大小。这个时候View的最终大小就是SpecSize所指定的值,它对应于LayoutParams中的match_parent和具体的数值。
AT_MOST :父容器指定了一个SpecSize,View的大小不能大于这个值,它对应于LayoutParams中的wrap_content。
(2)遍历测量了所有子view之后,完成自己宽和高的确定。
如果控件是
非容器类控件,比如textview,button,edittext 等,
这个方法实现的是给由父容器传递过来的viewspec 测量规格(期望的模式、和size尺寸)
来计算view控件自己的大小(长和宽)。
2、onLayout
这个方法是设置控件的位置,一般只有容器类控件才会重写这个方法,
用来控制孩子view在容器里面显示的位置。
比如自定义布局控件 CustomLayout ,要实现添加4个textview,会自动显示在在4个角,那个你的CustomLayout 需要重写这个方法。
3、ondraw
对于viewgroup 容器类控件:
1)容器自带背景,不是透明的
容器有背景时,才会调用ondraw( )方法画自己的content
也可以在自定义容器时候:
public GridRelativeLayout(Context context) {
super(context);
setWillNotDraw(false);
}
public GridRelativeLayout(Context context, AttributeSet attrs) {
super(context, attrs);
setWillNotDraw(false);
}
setWillNotDraw(false);调用这个方法,也会执行ondraw()方法。
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
所以考官问的问题答案就是这里,画线在自定义viewgroup的 ondraw方法
package com.example.viewdemo;
import android.R.integer;
import android.annotation.SuppressLint;
import android.content.Context;
import android.content.res.Resources;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.util.AttributeSet;
import android.util.DisplayMetrics;
import android.util.Log;
import android.widget.LinearLayout;
import android.widget.RelativeLayout;
public class GridRelativeLayout extends RelativeLayout {
private Canvas myCanvas;
private int horGrid = 108, verGrid = 177;// 水平网格和竖直网格
private int screenW = 1080, screenH = 1776;// 屏幕宽和高
private boolean initOver = true;// 初始化标签
public GridRelativeLayout(Context context) {
super(context);
setWillNotDraw(false);
}
public GridRelativeLayout(Context context, AttributeSet attrs) {
super(context, attrs);
setWillNotDraw(false);
}
public GridRelativeLayout(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
// /setWillNotDraw(false);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
this.myCanvas = canvas;
Paint paint = new Paint();
paint.setColor(android.graphics.Color.YELLOW);// 颜色
paint.setStrokeWidth(3);// 线宽
int verNum = (int) (screenH / verGrid);// 1776/177
int horNum = (int) (screenW / horGrid);
canvas.drawColor(Color.GREEN);
if (initOver) {
canvas.drawLine(0, 1 * verGrid - 1, screenW, 1 * verGrid - 1, paint);
for (int i = 0; i < verNum; i++) {
canvas.drawLine(0, i * verGrid, screenW, i * verGrid, paint);
}// 画横线
// 画竖线
for (int i = 0; i < horNum; i++) {
canvas.drawLine(i * horGrid, 0, i * horGrid, screenH, paint);
}
// 画圆圈
canvas.drawCircle(screenH / 2, screenW / 2, 100, paint);
}
}
/** 设置网格线参数 **/
public void setInf(int vergrid, int horgrid, int screenW, int screenH) {
this.verGrid = vergrid;
this.horGrid = horgrid;
this.screenW = screenW;
this.screenH = screenH;
initOver = true;
postInvalidate();
}
/** 擦除网格线 **/
public void clearLine() {
initOver = false;
postInvalidate();
}
}
2)容器没有背景,是透明的
看源码分析,这里不做细说,直接给结论。
透明背景,就是说viewgroup 自身不需要绘制任何东西,不会调用ondraw( )方法。
直接调用dispatchDraw( )来绘制孩子view。
针对非容器类控件:
通常只需要重写ondraw方法即可,来绘制满足项目的自定义视图。
比如圆角的imageview,就是通过重写ondraw方法绘制4个角。
还有一点关于自定义view重写onmeasure方法:
从源码getDefaultSize方法看,View的宽高由specSize决定。直接继承View需要重写onMeasure方法,并设置wrap_content时的自身大小,因为在getDefultSize方法中,AT_MOST模式下返回的是specSize。根据上面的表格可以看出,在AT_MOST模式下,specSize就是parentSize,也就是父容器最大可用空间。这种效果跟使用match_parent效果一样。
也即是说,对于一个直接继承自View的自定义View来说,比如:
CustomView extend View{
。。。
}
它的wrap_content和match_parent属性的效果是一样的,因此如果要实现自定义View的wrap_content,则要重写onMeasure方法,对wrap_content属性进行处理。
可以看这篇文章:
http://www.2cto.com/kf/201704/632345.html
正常情况我们是 extends Textview 吧
好了,就这些。源码大家可以自己研究~~~