一起Talk Android吧(第一百四十四回:Android自定义View之Layout一)

各位看官们大家好,上一回中咱们说的是Android中自定义View之Measure的例子,这一回咱们说的例子是自定义View之Layout。闲话休提,言归正转。让我们一起Talk Android吧!

看官们,我们在前面章回中介绍了Measure的细节和流程,在接下来的章回中将介绍Layout的细节和流程,其实我们在前面章回中介绍过Layout的流程,在这里我们对其做进一步的完善:

    doTraversal()->performTraversals()->performLayout()->layout()->onLayout()

该流程中前三个函数在ViewRootImpl.java文件中,后两个函数在View.java文件中(这点和Measure流程一样)。明白流程后我们重点看看layout的源代码,具体如下:

 public void layout(int l, int t, int r, int b) {
     if ((mPrivateFlags3 & PFLAG3_MEASURE_NEEDED_BEFORE_LAYOUT) != 0) {
         onMeasure(mOldWidthMeasureSpec, mOldHeightMeasureSpec);
         mPrivateFlags3 &= ~PFLAG3_MEASURE_NEEDED_BEFORE_LAYOUT;
     }

     int oldL = mLeft;
     int oldT = mTop;
     int oldB = mBottom;
     int oldR = mRight;

     boolean changed = isLayoutModeOptical(mParent) ?
             setOpticalFrame(l, t, r, b) : setFrame(l, t, r, b);

     if (changed || (mPrivateFlags & PFLAG_LAYOUT_REQUIRED) == PFLAG_LAYOUT_REQUIRED) {
         onLayout(changed, l, t, r, b);

         if (shouldDrawRoundScrollbar()) {
             if(mRoundScrollbarRenderer == null) {
                 mRoundScrollbarRenderer = new RoundScrollbarRenderer(this);
             }
         } else {
             mRoundScrollbarRenderer = null;
         }

         mPrivateFlags &= ~PFLAG_LAYOUT_REQUIRED;

         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);
             }
         }
     }

     final boolean wasLayoutValid = isLayoutValid();

     mPrivateFlags &= ~PFLAG_FORCE_LAYOUT;
     mPrivateFlags3 |= PFLAG3_IS_LAID_OUT;

     if (!wasLayoutValid && isFocused()) {
         mPrivateFlags &= ~PFLAG_WANTS_FOCUS;
         if (canTakeFocus()) {
             // We have a robust focus, so parents should no longer be wanting focus.
             clearParentsWantFocus();
         } else if (getViewRootImpl() == null || !getViewRootImpl().isInLayout()) {
             // This is a weird case. Most-likely the user, rather than ViewRootImpl, called
             // layout. In this case, there's no guarantee that parent layouts will be evaluated
             // and thus the safest action is to clear focus here.
             clearFocusInternal(null, /* propagate */ true, /* refocus */ false);
             clearParentsWantFocus();
         } else if (!hasParentWantsFocus()) {
             // original requestFocus was likely on this view directly, so just clear focus
             clearFocusInternal(null, /* propagate */ true, /* refocus */ false);
         }
         // otherwise, we let parents handle re-assigning focus during their layout passes.
     } else if ((mPrivateFlags & PFLAG_WANTS_FOCUS) != 0) {
         mPrivateFlags &= ~PFLAG_WANTS_FOCUS;
         View focused = findFocus();
         if (focused != null) {
             // Try to restore focus as close as possible to our starting focus.
             if (!restoreDefaultFocus() && !hasParentWantsFocus()) {
                 // Give up and clear focus once we've reached the top-most parent which wants
                 // focus.
                 focused.clearFocusInternal(null, /* propagate */ true, /* refocus */ false);
             }
         }
     }

     if ((mPrivateFlags3 & PFLAG3_NOTIFY_AUTOFILL_ENTER_ON_LAYOUT) != 0) {
         mPrivateFlags3 &= ~PFLAG3_NOTIFY_AUTOFILL_ENTER_ON_LAYOUT;
         notifyEnterOrExitForAutoFillIfNeeded(true);
     }
 }

从上面的代码中可以看出layout首先通过setFrame()方法确定自己的位置,也就是我们前面章回中介绍过的View的坐标,然后调用onLayout()方法去确定子View的位置。不过onLayout()方法是空的,View类没有实现,而是把它留给了子类去实现。

各位看官,关于Androd中自定义View之Layout的例子咱们就介绍到这里,欲知后面还有什么例子,且听下回分解!

发布了520 篇原创文章 · 获赞 131 · 访问量 62万+

猜你喜欢

转载自blog.csdn.net/talk_8/article/details/100086576