Java中的Jpanel类和Android中的View的联系与区别

目录

 

联系:

区别:

1.JPanel

1.1 简介

1.1.2 所有已实现的接口:

1.1.3 直接已知子类:

1.2 使用方法

1.2.1 设置图层管理器

1.2.2 添加控件

1.2.3 警告

1.3 构造方法

2.Activity

2.1. Activity.setContentView中发生了什么

2.1.1 Activity.setContentView

2.1.2. Activity.getWindow

   2.1.4. PhoneWindow和WindowManger

2.1.5. PhoneWindow.setContentView

2.1.6 PhoneWindow.installDecor

2.1.7 WindowManager和WindowMangerService

2.1.8 Window.setWindowManager

2.1.9 WindowManagerImpl.createLocalWindowManager

2.1.10 WindowManagerImpl

2.1.11 WindowManagerGlobal源码

2.2. ActivityThread.handleResumeActivity 中将PhoneWindow的DecorView add到WindowManger上

2.3. ViewRootImpl.setView

2.3.1 ViewRootImpl.setView

2.3.2 ViewRootImpl.requestLayout

2.3.3 ViewRootImpl.scheduleTraversals()

2.3.4 ViewRootImpl的TraversalRunnable对象

2.3.5 ViewRootImpl.doTraversal()

2.3.6 ViewRootImpl.performTraversals()  

2.3.7 ViewRootImpl的performMeasure、performLayout、performDraw

2.4. View和ViewGroup相关

2.4.1 ViewGroup.measureChildWithMargins

2.4.2 ViewGroup.getChildMeasureSpec


  • 联系:

Jpanel类和View类通常都作为GUI界面中的组件容器来使用,它们也作为UI组件使用。所以,它们可以作为容器包含其他UI组件,也可包含自身的子类组件,具有嵌套组合的功能。组件层次如下图:

 

                     

  • 区别:

1.JPanel

JPanel 是 Java图形用户界面(GUI)工具包swing中的面板容器类,包含在javax.swing 包中,是一种轻量级容器,可以加入到JFrame窗体中。JPanel默认的布局管理器是FlowLayout,其自身可以嵌套组合,在不同子容器中可包含其他组件(component),如JButton、JTextArea、JTextField 等,功能是对窗体上的这些控件进行组合,相当于C++和C#中的Panel类。

1.1 简介

JPanel 是 Java图形用户界面(GUI)工具包swing中的面板容器类,包含在javax.swing 包中,可以进行嵌套,功能是对窗体中具有相同逻辑功能的组件进行组合。

1.1.2 所有已实现的接口:

ImageObserver, MenuContainer, Serializable, Accessible

1.1.3 直接已知子类:

AbstractColorChooserPanel, JSpinner.DefaultEditor

JPanel 是一般轻量级容器. [1] 

1.2 使用方法

JPanel 可以为添加到窗体中的轻型控件提供通用的容器。默认情况下,面板容器不会向控件添加任何除自身背景之外的颜色,但是,可以轻松地向其添加边框(borders) 并根据需要改制样式。

在大多数Java GUI外观体验(look and feel)中,面板容器默认是不透明。不透明的面板容器跟普通的目录窗格功能差别不大,并且可以有效帮助样式改进。 [2] 

1.2.1 设置图层管理器

和其他容器(container)一样,面板容器 panel 也使用布局管理器(Layout Manager)对添加到容器中的组件(compnent)进行定位和设置尺寸大小。默认情况下,面板容器的布局管理器是FlowLayout(流式布局)类的一个实例,这个类对放置在容器中的空间按行进行布局(从左到右诸行排列)。 [3]  在创建面板容器时,你可以轻松地使用任何其他布局管理器通过调用setLayout方法或指定一个布局管理器。

如下是一个在创建面板容器时设置图层管理器的例子:

JPanel p = new JPanel(new BorderLayout());

但这种方法不能用在BoxLayout(盒子布局)中,因为BoxLayout 构造器需要一个预留(pre-existing)容器。如下所示:

JPanel p = new JPanel();

p.setLayout(new BoxLayout(p, BoxLayout.PAGE_AXIS));

1.2.2 添加控件

向面板容器中添加组件时使用add()方法,而向add()方法中传递的参数决定于该面板容器使用哪个布局管理器。当所用的布局管理器是FlowLayout,BoxLayout,GridLayout,或SpringLayout 时,像通常那样向add()方法传递单一的参数即可,如下所示: [2] 

aFlowPanel.add(aComponent);

aFlowPanel.add(anotherComponent);

而当布局管理器为BorderLayout时,还需要提供另一个参数来指定添加到容器中控件的位置,例如:

aBorderPanel.add(aComponent, BorderLayout.CENTER);

aBorderPanel.add(anotherComponent, BorderLayout.PAGE_END);

对于GridBagLayout布局管理器,可以使用上述任一方法,但必须以某种方式对每个组件指定GridBagConstraints,例如:

JPanel pane = new JPanel(new GridBagLayout());

GridBagConstraints c = new GridBagConstraints();

1.2.3 警告

Swing 不是线程安全的。

此类的序列化对象与以后的 Swing 版本不兼容。当前序列化支持适用于短期存储,或适用于在运行相同 Swing 版本的应用程序之间进行 RMI(Remote Method Invocation,远程方法调用)

1.3 构造方法

JPanel() 创建具有双缓冲和流布局的新 JPanel。

JPanel(boolean isDoubleBuffered) 创建具有 FlowLayout 和指定缓冲策略的新 JPanel。

JPanel(LayoutManager layout) 创建具有指定布局管理器的新缓冲 JPanel。

JPanel(LayoutManager layout, boolean isDoubleBuffered) 创建具有指定布局管理器和缓冲策略的新 JPanel


 


2.Activity

      Activity是Android开发者写第一行代码起就开始接触到的。而在onCreate方法中调用setContentView(R.layout.main_activity),恐怕也是绝大多数开发者的头等任务。然后我们可以调用findViewById(R.id.xxx)来获取布局中的某一个View。通过给View设置点击事件的监听来响应用户的操作。就这么简单我们和Android的View过了一段幸福的时光,直到有一天,在某个编码过后的深夜,在刚要合上电脑的一刹那,View低沉却又如心头一击的一句话,打破了很长时间以来看似平淡幸福的生活局面,“喂,我们在一起这么久了,可是你真的了解我吗?”。是啊,我了解View吗,它从哪里来?它和Activity的关系是什么?为什么有时和它相处,它的脾气好像总是有点怪,比如在onCreate方法中获取View的width,一直为0?还有onMeasure onLayout onDraw方法是从什么时候开始调用的?好吧是时候带你一起来认识认识你觉得熟悉,却又不是那么熟悉的View。

在开始本文前,还是按照惯例,抛出几个问题让读者思考,让读者带着思考来阅读文章,一来可以让读者更有针对性,二来也可以反映出本文的大概内容,让读者一眼就能看到文章内容的大概

setContentView一般都是在onCreate中调用,可以在onResume中调用吗?
Activity的ContentView是什么时候在Activity上显示给用户看的
Window、Activity、View他们三者之间的关系是什么
WindowManager是什么,它和View之间的关系是什么
WindowManagerService在整个View体系中充当什么角色
ViewRootImpl是什么,它是什么时候创建的,它与View之间的关系是什么
View的measure、onMeasure、layout、onLayout、draw、onDraw是什么意思,它们之间有什么关系
View的MeasureSpec是什么,它是怎么控制View和子View的测量的
View的requestLayout和invalidate区别是什么

上面9个问题,你可能平时或多或少都有思考过。如果这些问题你都能答上来,那么恭喜你,你对View的掌握可以说是通透了。如果不是,那么请跟我一起探寻View的秘密

2.1. Activity.setContentView中发生了什么


2.1.1 Activity.setContentView

首先我们来看一下源码,因为只有两行,所以直接贴代码吧

public void setContentView(@LayoutRes int layoutResID) {
        getWindow().setContentView(layoutResID);
        initWindowDecorActionBar();
    }



2.1.2. Activity.getWindow

原来是调用了getWindow().setContentView(layoutResID),好吧顺藤摸瓜,看看getWindow()源码

public Window getWindow() {
        return mWindow;
    }



2.1.3. Activity.attach

      好了,就不跟你贴mWindow的定义的源码了,请你直接翻阅源码,mWindow是Window对象。具体来说是PhoneWindow对象。接下来我们看下mWindow在什么时候初始化的,查询一番,发现是在Activity的attach方法中,被赋值的,如果你对attach方法感觉到陌生又好奇,可以参考这篇文章。在这里我也对attach方法做一个简单的讲解,省得读者被中断。

       简单来说Activity并不是一个真正的Context对象,Activity只是有名无实,真正的Context对象是通过调用Activity的attach(Context context, ActivityThread aThread,Instrumentation instr, IBinder token, int ident,Application application, Intent intent, ActivityInfo info, CharSequence title, Activity parent, String id, NonConfigurationInstances lastNonConfigurationInstances, Configuration config, String referrer, IVoiceInteractor voiceInteractor, Window window)方法赋值给Activity的。同时在该方法中,初始化了mWindow对象

 final void attach(Context context, ActivityThread aThread,
            Instrumentation instr, IBinder token, int ident,
            Application application, Intent intent, ActivityInfo info,
            CharSequence title, Activity parent, String id,
            NonConfigurationInstances lastNonConfigurationInstances,
            Configuration config, String referrer, IVoiceInteractor voiceInteractor,
            Window window) {
        attachBaseContext(context);

        mFragments.attachHost(null /*parent*/);

        mWindow = new PhoneWindow(this, window);
        mWindow.setWindowControllerCallback(this);
        mWindow.setCallback(this);
        mWindow.setOnWindowDismissedCallback(this);
        mWindow.getLayoutInflater().setPrivateFactory(this);
        ...省略一些代码
        //调用WindowManagerImpl.createLocalWindowManager返回WindowManagerImpl
        mWindow.setWindowManager(
                (WindowManager)context.getSystemService(Context.WINDOW_SERVICE),
                mToken, mComponent.flattenToString(),
                (info.flags & ActivityInfo.FLAG_HARDWARE_ACCELERATED) != 0);
        if (mParent != null) {
            //如果mParent不为空
            mWindow.setContainer(mParent.getWindow());
        }
        mWindowManager = mWindow.getWindowManager();

    }

   

2.1.4. PhoneWindow和WindowManger

从上面Activity.attach方法中,我们看到mWindow = new PhoneWindow(this,window)、mWindow.setWindowManager()和mWindowManager = mWindow.getWindowManager()这几行比较重要的代码。首先我们来看下PhoneWindow的源码,看下PhoneWindow.setContentView做了什么。关于WindowManager我们在介绍完了PhoneWindow后我们再来讲解

2.1.5. PhoneWindow.setContentView

 @Override
    public void setContentView(int layoutResID) {

        //如果mContentParent==null初始化DecorView
        if (mContentParent == null) {
            installDecor();
        } else if (!hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
            mContentParent.removeAllViews();
        }

        if (hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
            final Scene newScene = Scene.getSceneForLayout(mContentParent, layoutResID,
                    getContext());
            transitionTo(newScene);
        } else {
            mLayoutInflater.inflate(layoutResID, mContentParent);
        }
        mContentParent.requestApplyInsets();
        final Callback cb = getCallback();
        if (cb != null && !isDestroyed()) {
            cb.onContentChanged();
        }
        mContentParentExplicitlySet = true;
    }



乍一看这个方法看起来也很简单啊,只是mContentParent这是个什么?好吧,先留下这个问题,接着看installDecor方法

2.1.6 PhoneWindow.installDecor

private void installDecor() {
        mForceDecorInstall = false;
        if (mDecor == null) {
            //return new DecorView
            mDecor = generateDecor(-1);
            mDecor.setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);
            mDecor.setIsRootNamespace(true);
            if (!mInvalidatePanelMenuPosted && mInvalidatePanelMenuFeatures != 0) {
                mDecor.postOnAnimation(mInvalidatePanelMenuRunnable);
            }
        } else {
            mDecor.setWindow(this);
        }
        //如果mContentParent==null 初始化mContentParent
        if (mContentParent == null) {
            mContentParent = generateLayout(mDecor);
            ...省略代码
            }
        }
    }


好吧这里还引出一个DecorView mDecor对象。我们看下DecorView的源码可以看到它是FrameLayout的子类。关于mDecor和mContentParent对象我们可以这样认为。mDecor对象代表的PhoneWindow的根View。而mContentParent对象是mDecor对象的子View。而mContentParent是Activity.setContentView的contentView的父View。由此我们可以知道。Activity的contentView是依附在PhoneWindow的DecorView上的。Activity的ActionBar之类的控件也正是PhoneWindow代为操劳的。

好吧,至此我们应该明白一个道理,Activity里所有用户肉眼能够看到的组件,其实都是依附在PhoneWindow上。你可以把PhoneWindow简单认为是Activity的ContentView。那么PhoneWindow是如何被添加到Activity上被用户看到的呢?答案是通过WindowManager。

2.1.7 WindowManager和WindowMangerService

WindowMangerService是一个系统级的服务,在开机的时候,系统会启动该服务。至于WindowManagerService的实现原理不是本文的任务。WindowManager和WindowMangerService的关系 与ActivityManager和ActivityManagerService是一样的。WMS是系统级的服务,它是真正将View添加到手机屏幕上展示给用户看的。而WindowManger你可以简单认为是WMS运行在应用程序端的远程代理对象(真正的代理对象是WindoManagerGlobal的sWindowManagerService对象)。因为我们的应用程序是无法直接直接访问WMS等系统服务,需要通过AIDL来实现跨进程通信。WindowManager和WMS正是AIDL中的远程代理对象和本地服务对象(不懂AIDL的同学需要去补补课哟,这里默认大家都懂了)。通过WindowManager.addView(View view, ViewGroup.LayoutParams params)可以把View展示给用户

2.1.8 Window.setWindowManager

public void setWindowManager(WindowManager wm, IBinder appToken, String appName,
            boolean hardwareAccelerated) {
        mAppToken = appToken;
        mAppName = appName;
        mHardwareAccelerated = hardwareAccelerated
                || SystemProperties.getBoolean(PROPERTY_HARDWARE_UI, false);
        if (wm == null) {
            wm = (WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE);
        }
        mWindowManager = ((WindowManagerImpl)wm).createLocalWindowManager(this);
    }



WindowManager mWindowManager对象原来是WindowManagerImpl.createLocalWindowManager方法生成的

2.1.9 WindowManagerImpl.createLocalWindowManager

public WindowManagerImpl createLocalWindowManager(Window parentWindow) {
        return new WindowManagerImpl(mContext, parentWindow);
    }


原来是直接new WindowManagerImpl对象啊。好吧Activity的mWindowManager原来是WindowManagerImpl对象,好吧我们来看下WindowManagerImpl源码

2.1.10 WindowManagerImpl

public final class WindowManagerImpl implements WindowManager {
    private final WindowManagerGlobal mGlobal = WindowManagerGlobal.getInstance();
    private final Context mContext;
    private final Window mParentWindow;

     public WindowManagerImpl createLocalWindowManager(Window parentWindow) {
        return new WindowManagerImpl(mContext, parentWindow);
    }


    @Override
    public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
        applyDefaultToken(params);
        mGlobal.addView(view, params, mContext.getDisplay(), mParentWindow);
    }

    @Override
    public void updateViewLayout(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
        applyDefaultToken(params);
        mGlobal.updateViewLayout(view, params);
    }

    @Override
    public void removeView(View view) {
        mGlobal.removeView(view, false);
    }
}



原来WindowManagerImpl addView是借助WindowManagerGlobal mGlobal来实现的。有经验的老司机一眼就能看出WindowManagerGlobal实现了单例模式

2.1.11 WindowManagerGlobal源码

public final class WindowManagerGlobal {
    private static final String TAG = "WindowManager";

    //WindowManagerGlobal单例对象
    private static WindowManagerGlobal sDefaultWindowManager;
    //WindowManagerService在客户端的远程代理对象
    private static IWindowManager sWindowManagerService;
    //单个App客户端与WindowManagerService对应的一个IWindowSession
    private static IWindowSession sWindowSession;

    private final Object mLock = new Object();

    //App进程中所有Activity的DecorView
    private final ArrayList<View> mViews = new ArrayList<View>();

    //App进程中所有Activity的DecorView对应的ViewRootImpl
    private final ArrayList<ViewRootImpl> mRoots = new ArrayList<ViewRootImpl>();
    private final ArrayList<WindowManager.LayoutParams> mParams =
            new ArrayList<WindowManager.LayoutParams>();
    //要结束生命的Views
    private final ArraySet<View> mDyingViews = new ArraySet<View>();

    private Runnable mSystemPropertyUpdater;

    private WindowManagerGlobal() {
    }

    public static void initialize() {
        getWindowManagerService();
    }

    public static WindowManagerGlobal getInstance() {
        synchronized (WindowManagerGlobal.class) {
            if (sDefaultWindowManager == null) {
                sDefaultWindowManager = new WindowManagerGlobal();
            }
            return sDefaultWindowManager;
        }
    }

    /**获取WindowMangerService在APP进程端的远程对象**/
    public static IWindowManager getWindowManagerService() {
        synchronized (WindowManagerGlobal.class) {
            if (sWindowManagerService == null) {
                sWindowManagerService = IWindowManager.Stub.asInterface(
                        ServiceManager.getService("window"));
                try {
                    sWindowManagerService = getWindowManagerService();
                    ValueAnimator.setDurationScale(sWindowManagerService.getCurrentAnimatorScale());
                } catch (RemoteException e) {
                    throw e.rethrowFromSystemServer();
                }
            }
            return sWindowManagerService;
        }
    }

    /**
    *   1.如果view已经被add了,报错
    *   2.创建ViewRootImpl,调用ViewRootImpl.setView
    **/
    public void addView(View view, ViewGroup.LayoutParams params,
            Display display, Window parentWindow) {
        if (view == null) {
            throw new IllegalArgumentException("view must not be null");
        }
        if (display == null) {
            throw new IllegalArgumentException("display must not be null");
        }
        if (!(params instanceof WindowManager.LayoutParams)) {
            throw new IllegalArgumentException("Params must be WindowManager.LayoutParams");
        }

        final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams) params;
        if (parentWindow != null) {
            parentWindow.adjustLayoutParamsForSubWindow(wparams);
        } else {
            // If there's no parent, then hardware acceleration for this view is
            // set from the application's hardware acceleration setting.
            final Context context = view.getContext();
            if (context != null
                    && (context.getApplicationInfo().flags
                            & ApplicationInfo.FLAG_HARDWARE_ACCELERATED) != 0) {
                wparams.flags |= WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED;
            }
        }

        ViewRootImpl root;
        View panelParentView = null;

        synchronized (mLock) {
            // Start watching for system property changes.
            if (mSystemPropertyUpdater == null) {
                mSystemPropertyUpdater = new Runnable() {
                    @Override public void run() {
                        synchronized (mLock) {
                            for (int i = mRoots.size() - 1; i >= 0; --i) {
                                mRoots.get(i).loadSystemProperties();
                            }
                        }
                    }
                };
                SystemProperties.addChangeCallback(mSystemPropertyUpdater);
            }

            int index = findViewLocked(view, false);
            if (index >= 0) {
                if (mDyingViews.contains(view)) {
                    // Don't wait for MSG_DIE to make it's way through root's queue.
                    mRoots.get(index).doDie();
                } else {
                    throw new IllegalStateException("View " + view
                            + " has already been added to the window manager.");
                }
                // The previous removeView() had not completed executing. Now it has.
            }

            // If this is a panel window, then find the window it is being
            // attached to for future reference.
            if (wparams.type >= WindowManager.LayoutParams.FIRST_SUB_WINDOW &&
                    wparams.type <= WindowManager.LayoutParams.LAST_SUB_WINDOW) {
                final int count = mViews.size();
                for (int i = 0; i < count; i++) {
                    if (mRoots.get(i).mWindow.asBinder() == wparams.token) {
                        panelParentView = mViews.get(i);
                    }
                }
            }

            root = new ViewRootImpl(view.getContext(), display);

            view.setLayoutParams(wparams);

            mViews.add(view);
            mRoots.add(root);
            mParams.add(wparams);
        }

        // do this last because it fires off messages to start doing things
        try {
            root.setView(view, wparams, panelParentView);
        } catch (RuntimeException e) {
            // BadTokenException or InvalidDisplayException, clean up.
            synchronized (mLock) {
                final int index = findViewLocked(view, false);
                if (index >= 0) {
                    removeViewLocked(index, true);
                }
            }
            throw e;
        }
    }

    public void updateViewLayout(View view, ViewGroup.LayoutParams params) {
        if (view == null) {
            throw new IllegalArgumentException("view must not be null");
        }
        if (!(params instanceof WindowManager.LayoutParams)) {
            throw new IllegalArgumentException("Params must be WindowManager.LayoutParams");
        }

        final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams)params;

        view.setLayoutParams(wparams);

        synchronized (mLock) {
            int index = findViewLocked(view, true);
            ViewRootImpl root = mRoots.get(index);
            mParams.remove(index);
            mParams.add(index, wparams);
            root.setLayoutParams(wparams, false);
        }
    }

    public void removeView(View view, boolean immediate) {
        if (view == null) {
            throw new IllegalArgumentException("view must not be null");
        }

        synchronized (mLock) {
            int index = findViewLocked(view, true);
            View curView = mRoots.get(index).getView();
            removeViewLocked(index, immediate);
            if (curView == view) {
                return;
            }

            throw new IllegalStateException("Calling with view " + view
                    + " but the ViewAncestor is attached to " + curView);
        }
    }

    private void removeViewLocked(int index, boolean immediate) {
        ViewRootImpl root = mRoots.get(index);
        View view = root.getView();

        if (view != null) {
            InputMethodManager imm = InputMethodManager.getInstance();
            if (imm != null) {
                imm.windowDismissed(mViews.get(index).getWindowToken());
            }
        }
        boolean deferred = root.die(immediate);
        if (view != null) {
            view.assignParent(null);
            if (deferred) {
                mDyingViews.add(view);
            }
        }
    }

    void doRemoveView(ViewRootImpl root) {
        synchronized (mLock) {
            final int index = mRoots.indexOf(root);
            if (index >= 0) {
                mRoots.remove(index);
                mParams.remove(index);
                final View view = mViews.remove(index);
                mDyingViews.remove(view);
            }
        }
        if (ThreadedRenderer.sTrimForeground && ThreadedRenderer.isAvailable()) {
            doTrimForeground();
        }
    }

    private int findViewLocked(View view, boolean required) {
        final int index = mViews.indexOf(view);
        if (required && index < 0) {
            throw new IllegalArgumentException("View=" + view + " not attached to window manager");
        }
        return index;
    }

}


总结WindowManagerGlobal功能如下

获取WMS的远程代理对象
获取WMS的IWindowSession对象
创建ViewRootImpl对象
通过ViewRootImpl对象来addView removeView updateViewLayout
好吧,至此我们引出了View体系中至关重要的ViewRootImpl对象,我们放在一讲讲解,我们回头再想想,在Activity的onCreate中调用setContentView,生成的PhoneWindow的DecorView对象什么时候被WindowManger调用addView的呢

2.2. ActivityThread.handleResumeActivity 中将PhoneWindow的DecorView add到WindowManger上

final void handleResumeActivity(IBinder token,
            boolean clearHide, boolean isForward, boolean reallyResume, int seq, String reason) {
        ActivityClientRecord r = mActivities.get(token);
        if (!checkAndUpdateLifecycleSeq(seq, r, "resumeActivity")) {
            return;
        }

        // If we are getting ready to gc after going to the background, well
        // we are back active so skip it.
        unscheduleGcIdler();
        mSomeActivitiesChanged = true;

        //调用Activity的onResume方法
        r = performResumeActivity(token, clearHide, reason);

        if (r != null) {
            final Activity a = r.activity;

            if (localLOGV) Slog.v(
                TAG, "Resume " + r + " started activity: " +
                a.mStartedActivity + ", hideForNow: " + r.hideForNow
                + ", finished: " + a.mFinished);

            final int forwardBit = isForward ?
                    WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION : 0;

            // If the window hasn't yet been added to the window manager,
            // and this guy didn't finish itself or start another activity,
            // then go ahead and add the window.
            boolean willBeVisible = !a.mStartedActivity;
            //如果不可见说明在启动另一个Activity
            if (!willBeVisible) {
                try {
                    willBeVisible = ActivityManagerNative.getDefault().willActivityBeVisible(
                            a.getActivityToken());
                } catch (RemoteException e) {
                    throw e.rethrowFromSystemServer();
                }
            }
            if (r.window == null && !a.mFinished && willBeVisible) {
                //PhoneWindow 在Activity的attach中初始化的
                r.window = r.activity.getWindow();

                View decor = r.window.getDecorView();
                //刚开始的时候 设置成不可见
                decor.setVisibility(View.INVISIBLE);

                //返回的是WindowManagerImpl
                ViewManager wm = a.getWindowManager();
                WindowManager.LayoutParams l = r.window.getAttributes();
                a.mDecor = decor;
                l.type = WindowManager.LayoutParams.TYPE_BASE_APPLICATION;

                l.softInputMode |= forwardBit;
                if (r.mPreserveWindow) {
                    a.mWindowAdded = true;
                    r.mPreserveWindow = false;
                    // Normally the ViewRoot sets up callbacks with the Activity
                    // in addView->ViewRootImpl#setView. If we are instead reusing
                    // the decor view we have to notify the view root that the
                    // callbacks may have changed.
                    ViewRootImpl impl = decor.getViewRootImpl();
                    // 第一次应该是为null
                    if (impl != null) {
                        impl.notifyChildRebuilt();
                    }
                }
                if (a.mVisibleFromClient && !a.mWindowAdded) {
                    a.mWindowAdded = true;
                    //这里会把decor加载到WindowManagerImpl中
                    wm.addView(decor, l);
                }

            // If the window has already been added, but during resume
            // we started another activity, then don't yet make the
            // window visible.
            } else if (!willBeVisible) {
                if (localLOGV) Slog.v(
                    TAG, "Launch " + r + " mStartedActivity set");
                r.hideForNow = true;
            }

            // Get rid of anything left hanging around.
            cleanUpPendingRemoveWindows(r, false /* force */);

            // The window is now visible if it has been added, we are not
            // simply finishing, and we are not starting another activity.
            if (!r.activity.mFinished && willBeVisible
                    && r.activity.mDecor != null && !r.hideForNow) {
                if (r.newConfig != null) {
                    performConfigurationChangedForActivity(r, r.newConfig, REPORT_TO_ACTIVITY);
                    if (DEBUG_CONFIGURATION) Slog.v(TAG, "Resuming activity "
                            + r.activityInfo.name + " with newConfig " + r.activity.mCurrentConfig);
                    r.newConfig = null;
                }
                if (localLOGV) Slog.v(TAG, "Resuming " + r + " with isForward="
                        + isForward);
                WindowManager.LayoutParams l = r.window.getAttributes();
                if ((l.softInputMode
                        & WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION)
                        != forwardBit) {
                    l.softInputMode = (l.softInputMode
                            & (~WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION))
                            | forwardBit;
                    if (r.activity.mVisibleFromClient) {
                        ViewManager wm = a.getWindowManager();
                        View decor = r.window.getDecorView();
                        wm.updateViewLayout(decor, l);
                    }
                }
                r.activity.mVisibleFromServer = true;
                mNumVisibleActivities++;
                if (r.activity.mVisibleFromClient) {
                    r.activity.makeVisible();
                }
            }

            if (!r.onlyLocalRequest) {
                r.nextIdle = mNewActivities;
                mNewActivities = r;
                if (localLOGV) Slog.v(
                    TAG, "Scheduling idle handler for " + r);
                Looper.myQueue().addIdleHandler(new Idler());
            }
            r.onlyLocalRequest = false;

            // Tell the activity manager we have resumed.
            if (reallyResume) {
                try {
                    ActivityManagerNative.getDefault().activityResumed(token);
                } catch (RemoteException ex) {
                    throw ex.rethrowFromSystemServer();
                }
            }

        } else {
            // If an exception was thrown when trying to resume, then
            // just end this activity.
            try {
                ActivityManagerNative.getDefault()
                    .finishActivity(token, Activity.RESULT_CANCELED, null,
                            Activity.DONT_FINISH_TASK_WITH_ACTIVITY);
            } catch (RemoteException ex) {
                throw ex.rethrowFromSystemServer();
            }
        }
    }


从源码中我们看到在handleResumeActivity中通过wm.add(decor,l)把Activity的DecorView加载到WindowManger上的。从ActivityThread中处理Activity的生命周期顺序是 onCreate -> onResume -> wm.add(decor,l)。所以这里能解释不少问题了,1.可以在onResume中调用setContentView 2.在onCreate中调用View.getWidth等方方法是返回不了正确值的。

 

2.3. ViewRootImpl.setView


2.3.1 ViewRootImpl.setView

public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
        synchronized (this) {
            //如果mView==null 表示是新增加的
            if (mView == null) {
                mView = view;

                ...省略
                requestLayout();

                try {
                    mOrigWindowType = mWindowAttributes.type;
                    mAttachInfo.mRecomputeGlobalAttributes = true;
                    collectViewAttributes();
                    res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,
                            getHostVisibility(), mDisplay.getDisplayId(),
                            mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,
                            mAttachInfo.mOutsets, mInputChannel);
                } catch (RemoteException e) {
                    mAdded = false;
                    mView = null;
                    mAttachInfo.mRootView = null;
                    mInputChannel = null;
                    mFallbackEventHandler.setView(null);
                    unscheduleTraversals();
                    setAccessibilityFocus(null, null);
                    throw new RuntimeException("Adding window failed", e);
                } finally {
                    if (restore) {
                        attrs.restore();
                    }
                }


                if (res < WindowManagerGlobal.ADD_OKAY) {
                    mAttachInfo.mRootView = null;
                    mAdded = false;
                    mFallbackEventHandler.setView(null);
                    unscheduleTraversals();
                    setAccessibilityFocus(null, null);
                    switch (res) {
                        case WindowManagerGlobal.ADD_BAD_APP_TOKEN:
                        case WindowManagerGlobal.ADD_BAD_SUBWINDOW_TOKEN:
                            throw new WindowManager.BadTokenException(
                                    "Unable to add window -- token " + attrs.token
                                    + " is not valid; is your activity running?");
                        case WindowManagerGlobal.ADD_NOT_APP_TOKEN:
                            throw new WindowManager.BadTokenException(
                                    "Unable to add window -- token " + attrs.token
                                    + " is not for an application");
                        case WindowManagerGlobal.ADD_APP_EXITING:
                            throw new WindowManager.BadTokenException(
                                    "Unable to add window -- app for token " + attrs.token
                                    + " is exiting");
                        case WindowManagerGlobal.ADD_DUPLICATE_ADD:
                            throw new WindowManager.BadTokenException(
                                    "Unable to add window -- window " + mWindow
                                    + " has already been added");
                        case WindowManagerGlobal.ADD_STARTING_NOT_NEEDED:
                            // Silently ignore -- we would have just removed it
                            // right away, anyway.
                            return;
                        case WindowManagerGlobal.ADD_MULTIPLE_SINGLETON:
                            throw new WindowManager.BadTokenException("Unable to add window "
                                    + mWindow + " -- another window of type "
                                    + mWindowAttributes.type + " already exists");
                        case WindowManagerGlobal.ADD_PERMISSION_DENIED:
                            throw new WindowManager.BadTokenException("Unable to add window "
                                    + mWindow + " -- permission denied for window type "
                                    + mWindowAttributes.type);
                        case WindowManagerGlobal.ADD_INVALID_DISPLAY:
                            throw new WindowManager.InvalidDisplayException("Unable to add window "
                                    + mWindow + " -- the specified display can not be found");
                        case WindowManagerGlobal.ADD_INVALID_TYPE:
                            throw new WindowManager.InvalidDisplayException("Unable to add window "
                                    + mWindow + " -- the specified window type "
                                    + mWindowAttributes.type + " is not valid");
                    }
                    throw new RuntimeException(
                            "Unable to add window -- unknown error code " + res);
                }

            }
        }
    }


上面我保留了两行比较重要的方法requestLayout()和 res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,getHostVisibility(), mDisplay.getDisplayId(), mAttachInfo.mContentInsets, mAttachInfo.mStableInsets, mAttachInfo.mOutsets, mInputChannel);

requestLayout()主要是让View经历measure layout draw 三个阶段,mWindowSession.addToDisplay应该是交给WMS去请求其他服务渲染界面。我们主要来看下requestLayout()

我们来跟踪下requestLayout源码

 

2.3.2 ViewRootImpl.requestLayout

@Override
    public void requestLayout() {
        if (!mHandlingLayoutInLayoutRequest) {
            checkThread();
            //注意这个变量
            mLayoutRequested = true;
            scheduleTraversals();
        }
    }


注意mLayoutRequested = true,只有mLayoutRequested为true,measure layout draw三个阶段才会全部经历,否则只会经历draw阶段,这也就是View的reqeustLayout和invalidate的区别,requestLayout会经历完整的三个阶段

2.3.3 ViewRootImpl.scheduleTraversals()

void scheduleTraversals() {
        if (!mTraversalScheduled) {
            mTraversalScheduled = true;
            mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
            mChoreographer.postCallback(
                    Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
            if (!mUnbufferedInputDispatch) {
                scheduleConsumeBatchedInput();
            }
            notifyRendererOfFramePending();
            pokeDrawLockIfNeeded();
        }
    }


从源码中可以看出交由mChoreographer对象去执行mTraversalRunnable对象

2.3.4 ViewRootImpl的TraversalRunnable对象

final class TraversalRunnable implements Runnable {
        @Override
        public void run() {
            doTraversal();
        }
    }


最终调用了ViewRootImpl.doTraversal()

2.3.5 ViewRootImpl.doTraversal()

void doTraversal() {
        if (mTraversalScheduled) {
            mTraversalScheduled = false;
            mHandler.getLooper().getQueue().removeSyncBarrier(mTraversalBarrier);

            if (mProfile) {
                Debug.startMethodTracing("ViewAncestor");
            }

            performTraversals();

            if (mProfile) {
                Debug.stopMethodTracing();
                mProfile = false;
            }
        }
    }


跟踪performTraversals()代码

2.3.6 ViewRootImpl.performTraversals()  

 private void performTraversals() {
        // cache mView since it is used so much below...
        final View host = mView;

        if (DBG) {
            System.out.println("======================================");
            System.out.println("performTraversals");
            host.debug();
        }

        if (host == null || !mAdded)
            return;

        //正在遍历
        mIsInTraversal = true;
        //马上要画
        mWillDrawSoon = true;
        //windowSize是否改变
        boolean windowSizeMayChange = false;
        boolean newSurface = false;
        boolean surfaceChanged = false;
        WindowManager.LayoutParams lp = mWindowAttributes;

        //预期的window的宽度
        int desiredWindowWidth;
        int desiredWindowHeight;

        //当前View是否可见
        final int viewVisibility = getHostVisibility();

        //view的可见性是否改变(1.不是第一次而且和上次的可见性不一样)
        final boolean viewVisibilityChanged = !mFirst
                && (mViewVisibility != viewVisibility || mNewSurfaceNeeded);
        //用户肉眼看到的View的可见性是否改变
        final boolean viewUserVisibilityChanged = !mFirst &&
                ((mViewVisibility == View.VISIBLE) != (viewVisibility == View.VISIBLE));

        ...省略代码

        Rect frame = mWinFrame;
        if (mFirst) {
            //如果是第一次
            mFullRedrawNeeded = true;
            //需要请求layout
            mLayoutRequested = true;

            //如果是状态栏或者输入法 需要使用真实的size
            if (shouldUseDisplaySize(lp)) {
                // NOTE -- system code, won't try to do compat mode.
                Point size = new Point();
                mDisplay.getRealSize(size);
                desiredWindowWidth = size.x;
                desiredWindowHeight = size.y;
            } else {
                Configuration config = mContext.getResources().getConfiguration();
                desiredWindowWidth = dipToPx(config.screenWidthDp);
                desiredWindowHeight = dipToPx(config.screenHeightDp);
            }

            ...省略代码

            //如果是第一次 会调用View的onAttachedToWindow
            host.dispatchAttachedToWindow(mAttachInfo, 0);
            mAttachInfo.mTreeObserver.dispatchOnWindowAttachedChange(true);
            dispatchApplyInsets(host);
            //Log.i(mTag, "Screen on initialized: " + attachInfo.mKeepScreenOn);

        } else {
            desiredWindowWidth = frame.width();
            desiredWindowHeight = frame.height();
            if (desiredWindowWidth != mWidth || desiredWindowHeight != mHeight) {
                if (DEBUG_ORIENTATION) Log.v(mTag, "View " + host + " resized to: " + frame);
                mFullRedrawNeeded = true;
                mLayoutRequested = true;
                windowSizeMayChange = true;
            }
        }

        if (viewVisibilityChanged) {//如果hostView的可见性变了 说明window的可见性变了
            mAttachInfo.mWindowVisibility = viewVisibility;
            host.dispatchWindowVisibilityChanged(viewVisibility);
            if (viewUserVisibilityChanged) {//如果肉眼可见性变了
                host.dispatchVisibilityAggregated(viewVisibility == View.VISIBLE);
            }
            if (viewVisibility != View.VISIBLE || mNewSurfaceNeeded) {
                endDragResizing();
                destroyHardwareResources();
            }
            if (viewVisibility == View.GONE) {
                // After making a window gone, we will count it as being
                // shown for the first time the next time it gets focus.
                mHasHadWindowFocus = false;
            }
        }

        // Non-visible windows can't hold accessibility focus.
        if (mAttachInfo.mWindowVisibility != View.VISIBLE) {
            //如果window不可见把Accessibility clear掉
            host.clearAccessibilityFocus();
        }

        // Execute enqueued actions on every traversal in case a detached view enqueued an action
        getRunQueue().executeActions(mAttachInfo.mHandler);

        boolean insetsChanged = false;

        boolean layoutRequested = mLayoutRequested && (!mStopped || mReportNextDraw);

        //如果是requestLayout
        if (layoutRequested) {

            final Resources res = mView.getContext().getResources();

            if (mFirst) {
                // make sure touch mode code executes by setting cached value
                // to opposite of the added touch mode.
                mAttachInfo.mInTouchMode = !mAddedTouchMode;
                ensureTouchModeLocally(mAddedTouchMode);
            } else {
                if (!mPendingOverscanInsets.equals(mAttachInfo.mOverscanInsets)) {
                    insetsChanged = true;
                }
                if (!mPendingContentInsets.equals(mAttachInfo.mContentInsets)) {
                    insetsChanged = true;
                }
                if (!mPendingStableInsets.equals(mAttachInfo.mStableInsets)) {
                    insetsChanged = true;
                }
                if (!mPendingVisibleInsets.equals(mAttachInfo.mVisibleInsets)) {
                    mAttachInfo.mVisibleInsets.set(mPendingVisibleInsets);
                    if (DEBUG_LAYOUT) Log.v(mTag, "Visible insets changing to: "
                            + mAttachInfo.mVisibleInsets);
                }
                if (!mPendingOutsets.equals(mAttachInfo.mOutsets)) {
                    insetsChanged = true;
                }
                if (mPendingAlwaysConsumeNavBar != mAttachInfo.mAlwaysConsumeNavBar) {
                    insetsChanged = true;
                }
                if (lp.width == ViewGroup.LayoutParams.WRAP_CONTENT
                        || lp.height == ViewGroup.LayoutParams.WRAP_CONTENT) {
                    windowSizeMayChange = true;

                    if (shouldUseDisplaySize(lp)) {
                        // NOTE -- system code, won't try to do compat mode.
                        Point size = new Point();
                        mDisplay.getRealSize(size);
                        desiredWindowWidth = size.x;
                        desiredWindowHeight = size.y;
                    } else {
                        Configuration config = res.getConfiguration();
                        desiredWindowWidth = dipToPx(config.screenWidthDp);
                        desiredWindowHeight = dipToPx(config.screenHeightDp);
                    }
                }
            }


            //1.开始测量
            windowSizeMayChange |= measureHierarchy(host, lp, res,
                    desiredWindowWidth, desiredWindowHeight);
        }

        ...省略代码

        final boolean didLayout = layoutRequested && (!mStopped || mReportNextDraw);
        boolean triggerGlobalLayoutListener = didLayout
                || mAttachInfo.mRecomputeGlobalAttributes;
        if (didLayout) {
            //2 开始layout
            performLayout(lp, mWidth, mHeight);

            ... 省略代码
        }

        ... 省略代码
        boolean cancelDraw = mAttachInfo.mTreeObserver.dispatchOnPreDraw() || !isViewVisible;

        if (!cancelDraw && !newSurface) {
            if (mPendingTransitions != null && mPendingTransitions.size() > 0) {
                for (int i = 0; i < mPendingTransitions.size(); ++i) {
                    mPendingTransitions.get(i).startChangingAnimations();
                }
                mPendingTransitions.clear();
            }
        //3. 开始draw
            performDraw();
        } else {
            if (isViewVisible) {
                // Try again
                scheduleTraversals();
            } else if (mPendingTransitions != null && mPendingTransitions.size() > 0) {
                for (int i = 0; i < mPendingTransitions.size(); ++i) {
                    mPendingTransitions.get(i).endChangingAnimations();
                }
                mPendingTransitions.clear();
            }
        }

        mIsInTraversal = false;
    }


总结这个函数的功能测量View的大小
摆放View的位置(layout)
画View(draw)


2.3.7 ViewRootImpl的performMeasure、performLayout、performDraw

 private void performMeasure(int childWidthMeasureSpec, int childHeightMeasureSpec) {
        Trace.traceBegin(Trace.TRACE_TAG_VIEW, "measure");
        try {
            mView.measure(childWidthMeasureSpec, childHeightMeasureSpec);
        } finally {
            Trace.traceEnd(Trace.TRACE_TAG_VIEW);
        }
    }


    private void performLayout(WindowManager.LayoutParams lp, int desiredWindowWidth,
            int desiredWindowHeight) {
       ...省略代码

            host.layout(0, 0, host.getMeasuredWidth(), host.getMeasuredHeight());
         ...省略代码
        mInLayout = false;
    }


由上可知会分别调用View的measure、layout、draw方法

2.4. View和ViewGroup相关


2.4.1 ViewGroup.measureChildWithMargins

protected void measureChildWithMargins(View child,
            int parentWidthMeasureSpec, int widthUsed,
            int parentHeightMeasureSpec, int heightUsed) {
        final MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams();

        final int childWidthMeasureSpec = getChildMeasureSpec(parentWidthMeasureSpec,
                mPaddingLeft + mPaddingRight + lp.leftMargin + lp.rightMargin
                        + widthUsed, lp.width);
        final int childHeightMeasureSpec = getChildMeasureSpec(parentHeightMeasureSpec,
                mPaddingTop + mPaddingBottom + lp.topMargin + lp.bottomMargin
                        + heightUsed, lp.height);        child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
    }

2.4.2 ViewGroup.getChildMeasureSpec

public static int getChildMeasureSpec(int spec, int padding, int childDimension) {
        int specMode = MeasureSpec.getMode(spec);
        int specSize = MeasureSpec.getSize(spec);

        int size = Math.max(0, specSize - padding);

        int resultSize = 0;
        int resultMode = 0;

        switch (specMode) {
        // Parent has imposed an exact size on us
        case MeasureSpec.EXACTLY:
            if (childDimension >= 0) {
                resultSize = childDimension;
                resultMode = MeasureSpec.EXACTLY;
            } else if (childDimension == LayoutParams.MATCH_PARENT) {
                // Child wants to be our size. So be it.
                resultSize = size;
                resultMode = MeasureSpec.EXACTLY;
            } else if (childDimension == LayoutParams.WRAP_CONTENT) {
                // Child wants to determine its own size. It can't be
                // bigger than us.
                resultSize = size;
                resultMode = MeasureSpec.AT_MOST;
            }
            break;

        // Parent has imposed a maximum size on us
        case MeasureSpec.AT_MOST:
            if (childDimension >= 0) {
                // Child wants a specific size... so be it
                resultSize = childDimension;
                resultMode = MeasureSpec.EXACTLY;
            } else if (childDimension == LayoutParams.MATCH_PARENT) {
                // Child wants to be our size, but our size is not fixed.
                // Constrain child to not be bigger than us.
                resultSize = size;
                resultMode = MeasureSpec.AT_MOST;
            } else if (childDimension == LayoutParams.WRAP_CONTENT) {
                // Child wants to determine its own size. It can't be
                // bigger than us.
                resultSize = size;
                resultMode = MeasureSpec.AT_MOST;
            }
            break;

        // Parent asked to see how big we want to be
        case MeasureSpec.UNSPECIFIED:
            if (childDimension >= 0) {
                // Child wants a specific size... let him have it
                resultSize = childDimension;
                resultMode = MeasureSpec.EXACTLY;
            } else if (childDimension == LayoutParams.MATCH_PARENT) {
                // Child wants to be our size... find out how big it should
                // be
                resultSize = View.sUseZeroUnspecifiedMeasureSpec ? 0 : size;
                resultMode = MeasureSpec.UNSPECIFIED;
            } else if (childDimension == LayoutParams.WRAP_CONTENT) {
                // Child wants to determine its own size.... find out how
                // big it should be
                resultSize = View.sUseZeroUnspecifiedMeasureSpec ? 0 : size;
                resultMode = MeasureSpec.UNSPECIFIED;
            }
            break;
        }
        //noinspection ResourceType
        return MeasureSpec.makeMeasureSpec(resultSize, resultMode);
    }



以上代码可以用一张表来总结

ChildDeMission   
 
 EXACTLYAL_MOST   UNSPECIFIED
大于0  EXACTLY   EXACTLYEXACTLY
MATCH_PARENT EXACTLYAL_MOST   UNSPECIFIED
WRAP_CONTENT  AL_MOSTAL_MOSTUNSPECIFIED


 

另外本人还开设了个人公众号:JiandaoStudio ,会在公众号内定期发布行业信息,以及各类免费代码、书籍、大师课程资源。

 

                                            

扫码关注本人微信公众号,有惊喜奥!公众号每天定时发送精致文章!回复关键词可获得海量各类编程开发学习资料!

例如:想获得Python入门至精通学习资料,请回复关键词Python即可。


 

 

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值