自定义View之onLayout方法学习

来源:互联网 发布:手机用的中文编程软件 编辑:程序博客网 时间:2024/05/23 00:10

关于onLayout的学习,也是在基于View视图树的递归调用实现。

本篇想说明的是,不去深究View源码关于onLayout,以及layout方法的实现原理。知道大概,目的是在会用。但是需要了解并掌握View内部关于onLayout方法的相关API使用。

在ViewGroup中,onLayout是一个抽象方法,所以如果继承了ViewGroup类,除了添加构造方法外,还必须要重写onLayout方法。

/**  * 当这个view和其子view被分配一个大小和位置时,被layout调用。  * @param changed 当前View的大小和位置改变了  * @param left 左部位置(相对于父视图)  * @param top 顶部位置(相对于父视图)  * @param right 右部位置(相对于父视图)  * @param bottom 底部位置(相对于父视图)  */  protected void onLayout(boolean changed, int left, int top, int right, int bottom)
此方法是在ViewGroup中的layout方法所调用:

@Override    public final void layout(int l, int t, int r, int b) {        if (!mSuppressLayout && (mTransition == null || !mTransition.isChangingLayout())) {            if (mTransition != null) {                mTransition.layoutChange(this);            }            super.layout(l, t, r, b);        } else {            // record the fact that we noop'd it; request layout when transition finishes            mLayoutCalledWhileSuppressed = true;        }    }
而ViewGroup中的layout方法,是继承自View类当中的layout方法,然后在看一下

View类中layout的方法实现。

/**  * 给View和其所有子View分配大小和位置  *  * 这是布局的第二个阶段(第一个阶段是测量)。在这个阶段中,每个父视图需要去调用layout去为他所有的子视图确定位置  * 派生的子类不应该重写layout方法,应该重写onLayout方法,在onlayout方法中应该去调用每一个view的layout  */  public void layout(int l, int t, int r, int b) {      // 将当前视图的左上右下记录为old值(参数中传入的为新的l,t,r,b值)      int oldL = mLeft;      int oldT = mTop;      int oldB = mBottom;      int oldR = mRight;            // setFrame方法的作用就是将新传入的ltrb属性赋值给View,然后判断当前View大小和位置是否发生了变化并返回      boolean changed = setFrame(l, t, r, b);            if (changed || (mPrivateFlags & PFLAG_LAYOUT_REQUIRED) == PFLAG_LAYOUT_REQUIRED) {          // 调用onLayout回调方法,具体实现由重写了onLayout方法的ViewGroup的子类去实现(后面详细说明)          onLayout(changed, l, t, r, b);          mPrivateFlags &= ~PFLAG_LAYOUT_REQUIRED;            // 调用所有重写了onLayoutChange监听的方法,通知View大小和位置发生了改变          ListenerInfo li = mListenerInfo;          if (li != null && li.mOnLayoutChangeListeners != null) {              ArrayList<OnLayoutChangeListener> listenersCopy =                      (ArrayList<OnLayoutChangeListener>)li.mOnLayoutChangeListeners.clone();              int numListeners = listenersCopy.size();              for (int i = 0; i < numListeners; ++i) {                  listenersCopy.get(i).onLayoutChange(this, l, t, r, b, oldL, oldT, oldR, oldB);              }          }      }      mPrivateFlags &= ~PFLAG_FORCE_LAYOUT;  } 
在这里边也有onLayout()方法,在View中的onLayout();中是一个实现了个空方法。

 protected void onLayout(boolean changed, int left, int top, int right, int bottom) {    }

关于layout,onLayout在View与ViewGroup中就这四个。

我们在ViewGroup中重写onLayout的目的

就是设置当前View与其所有的子View,在ViewGroup(或其继承ViewGroup的Layout)父布局当中的位置。

在onLayout方法中,我们可以获取当前布局,在父布局当中的位置:

getLeft();//子View左边界到父view左边界的距离getTop();//子View上边界到父view上边界的距离getRight();//子View右边界到父View左边界距离getBottom();//子View下边界到父View上边界距离。
还可以在onMeasure()方法执行后在onLayout方法中,测量出当前布局View,在父布局中的
宽度:getWidth();//在onLayout()方法中取得相对在父布局中的宽、高
高度:getHeight();//取值在getMeasuredHeight()方法之后。
此处要注意区分子View的

childView.getMeasuredWidth();//在onMeasure()方法之后取得View的实际宽、高

childView.getMeasuredHeight();

两个方法取值的区别就在如果View的大小没有超过父布局View的大小,二者是相等的。getWidth() == childView.getMeasuredWidth();

如果childView宽、高大于了父布局的宽、高 则:getWidth() + 超出区域 = childView.getMeasuredWidth();

来源:官方文档。详见jafsldkfj博客。

而计算viewChild在父布局中的位置,可以通过:

viewChild.layout();方法实现View的布局定位。

在ViewGroup中重写onLayout方法,在onLayout方法中对子View,viewChild.layout();布局定位,而layout中又调用了onLayout()方法

所以实现了View视图树递归调用的原理。跟onMeasure类似。

(在ViewGroup中,layout()方法是个final类型,所以不能被子类调用。在ViewGroup中,没有measure,onMeasure方法。)

(在View中,measure()方法是个final类型,也不能被子类调用。但是layout是一个普通方法,onLayout是一个实现了空方法。)

添加点一Tip:
① 在代码注释文档中/**{@hide}*/ 表示该方法不向子类开发,不提供外部使用。顺便可以学习下Java doc注释。
最终的学习,还是为了在项目中灵活运用,实践。



0 0
原创粉丝点击