当前位置: 首页 > 科技观察

自定义控件常用方法总结

时间:2023-03-19 21:16:02 科技观察

除了绘图过程中涉及到的测量和布局绘制的API外,还有一些自定义控件中经常使用的API。在这里我做了一个总结,请同学们补充或指正。让大叔也扬起架势。inflateinflate方法常用于解析一个xml布局文件,常用于自定义组合控件。使用的手势包括:View.inflate(context,resource,root)LayoutInflater.from(getContext()).inflate(resource,root);而View.inflater其实是调用了LayoutInflater来解析一个xml:publicstaticViewinflate(Contextcontext,intresource,ViewGrouproot){LayoutInflaterfactory=LayoutInflater.from(context);returnfactory.inflater(resource,root);}那么这两种姿势是不是有什么区别呢?我们来讨论一下inflate(resouce,root)的返回值。参数resource为布局资源,root为传入的根节点,如果root传入null,inflate会解析该资源对应的xml,返回xml中的根节点。如果root不为null,inflate会解析xml布局添加到根节点root,然后返回根节点root。另外还有一个inflate方法,三个参数:inflate(intresource,ViewGrouproot,booleanattachToRoot)这里多了一个参数attachToRoot,如果root为null,则返回解析后的xml布局中的根节点;如果root不为null,attachToRoot为true,inflate会解析xml布局并添加到根节点root,然后返回根节点root;如果root不为null,attachToRoot为false,inflate会解析xml布局但不会将其添加到根节点root,然后返回解析后的xml布局中的根节点。这时候root的作用就是为xml中的根节点提供布局参数的属性,因为xml中的根节点并不知道它的父容器是谁,所以如果没有人给它提供的话,它的布局参数将作废。onFinishInflateonFinishInflate是所有子项都已解决时的调用。比如我们自定义一个ViewGroup,想找child做一些设置。这时候如果我们在自定义ViewGroup的构造函数中找到ViewById,就会返回一个null,因为这个child还没有解析,也就是还没有出生。.这时候我们可以重写onFinishInflate,等child解析完了再找。requestLayout关于requestLayout的介绍有很多。requestLayout()方法会触发measure过程和layout过程,不会调用draw过程,也不会重绘包括调用者本身在内的任何View。onSizeChange(intw,inth,intoldw,intoldh)onSizeChange在控件大小发生变化时调用,其调用轨迹为layout->setFrame->sizeChange->onSizeChange。控件第一次布局的时候肯定会被调用,我们可以通过重写这个方法来获取控件的大小。所以该方法通常用于初始化与控件大小相关的成员变量。invalidateinvalidate使用频率很高,会触发View的重绘,也就是绘图过程的draw过程,但不会调用测量和布局过程。postInvalidate我们都知道AndroidUI是单线程模型,UI只能在主线程更新,所以我们只能在主线程调用invalidate。如果想在子线程上更新UI,可以使用handler向主线程发送一个msg,然后在处理msg时调用invalidate。另外,我们可以在子线程中直接调用postInvalidate来更新UI。postInvalidate的内部实现也是使用一个handler将msg发送到主线程,然后调用invalidate。setWillNotDrawCustomViewGroup通常不绘制自己。如果你重写ViewGroup中的draw方法或者onDraw方法,你会发现它们根本不会被调用。但是如果你给你的ViewGroup设置了背景,你会发现draw方法和onDraw方法又会消失。我们知道ViewGroup本身就是一个View,它的绘制是由它的父容器发起的。具体位置是ViewGroup中的drawChild方法:protectedbooleanddrawChild(Canvascanvas,Viewchild,longdrawingTime){returnchild.draw(canvas,this,drawingTime);}注意这里的draw方法是三个参数,和我们平时说的一个参数的draw方法不一样。找到View类中三个参数的draw方法,发现有这么一段代码:;}else{draw(canvas);}}从这里我们可以看出一点端倪,通常一个ViewGroup会默认跳过绘制,即(mPrivateFlags&PFLAG_SKIP_DRAW)==PFLAG_SKIP_DRAW会返回一个true,然后就直接去给dispatchDraw方法绘制自己的children时,不会调用带参数的draw(canvas),但是当ViewGroup有背景或者setWillNotDraw(false)时,就会调用draw(canvas)方法。所以如果我们自定义一个ViewGroup,想要实现自己的绘图,可以给它设置一个背景,或者调用setWillNotDraw(false)。onAttachedToWindowonAttachedToWindow在视图绑定到窗口时被调用。根据View类中对该方法的注释,onAttachedToWindow肯定会在onDraw方法之前被调用。在自定义控件中,我们可以在onAttachedToWindow中注册一些广播接收器、观察者或者启动一些任务。可以参考TextClock中的实现。onDetachedFromWindowonDetachedFromWindow对应于onAttachedToWindow,是一个View从窗口中移除时的调用。如果在onAttachedWindow中注册了一些监听器,通常需要在onDetachedFromWindow中注销。ViewTreeObserverViewTreeObserver是视图树的观察者,监听视图树的一些全局变化,包括整个视图树的布局、开始绘制、触摸模式的变化等。我们不能直接初始化ViewTreeObserver对象,需要通过getViewTreeObserver()来获取。ViewTreeObserver.OnGlobalLayoutListener当视图树中的全局布局发生变化或者视图树中视图的视觉状态发生变化时,一般的使用姿势是:removeGlobalOnLayoutListener(this);//dosomethingyoulike//forexample,getviewwidthorheightheight}});ViewTreeObserver.OnPreDrawListener当视图树即将被绘制时,一般的使用姿势是: