1 坐标系

Android系统里面有两种坐标系:Android坐标系、View坐标系。

1.1 Android坐标系


Android的坐标系是以手机上可见的屏幕左上角顶点为坐标系原点,但是xy轴的方向和我们以前知道的有所不同,需要注意,从原点向右为x轴正方向,而从原点向下为y轴正方向。

android.view.MotionEvent下面有两个方法getRawX()getRawY()可以获得当前触摸位置的Android坐标系坐标。

View坐标系

可以说Android坐标系是屏幕绝对坐标,View坐标系是viewgroup的相对坐标,这两者可以结合使用,做到更加精确的控制。


方法 从属 含义
getX() MotionEvent 触摸位置距离控件左边缘距离
getY() MotionEvent 触摸位置距离控件上边缘距离
getTop() View 当前view到其父布局上边缘距离
getBottom() View 当前view下边缘到其父布局上边缘距离
getLeft() View 当前view左边缘到其父布局左边缘距离
getRight() View 当前view右边缘到其父布局左边缘边缘距离

2 源码解析Activity的view体系

2.1 view体系

我们都知道要在Activity的onCreate()方法里面用setContentView()方法加载布局,那布局在源码里是如何被加载出来的,来分析一下。
首先进入setContentView,看到方法如下:

public void setContentView(@LayoutRes int layoutResID) {    getWindow().setContentView(layoutResID);    initWindowDecorActionBar();}     *// @return Window The current window, or null if the activity is not visual.     */       public Window getWindow() {    return mWindow;}

这个方法里面首先先去调用getWindow()将布局塞入到获得的window对象里面,注释说明这个mWindow是当前的window,如果activity不可见就返回null

再来看看window是如何被初始化出来的,我们在attach方法里面找到了赋值代码:

mWindow = new PhoneWindow(this, window, activityConfigCallback);

mWindow就是PhoneWindow对象的实例,所以getWindow().setContentView()是调用了PhoneWindow对象的setContentView方法:

// This is the view in which the window contents are placed. It is either    // mDecor itself, or a child of mDecor where the contents go.    ViewGroup mContentParent;    public void setContentView(int layoutResID) {        // Note: FEATURE_CONTENT_TRANSITIONS may be set in the process of installing the window        // decor, when theme attributes and the like are crystalized. Do not check the feature        // before this happens.        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;    } private void installDecor() {        mForceDecorInstall = false;        if (mDecor == null) {            mDecor = generateDecor(-1);            mDecor.setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);            mDecor.setIsRootNamespace(true);            if (!mInvalidatePanelMenuPosted && mInvalidatePanelMenuFeatures != 0) {                mDecor.postOnAnimation(mInvalidatePanelMenuRunnable);            }        } else {            mDecor.setWindow(this);        }        if (mContentParent == null) {            mContentParent = generateLayout(mDecor);            ...        }        ... }

该方法判断mContentParent是否存在,不存在就会调用 installDecor()方法去创建DecorView,存在的话就把布局加载到mContentParent中,按照注释,这个mContentParent就是DecorView本身或者DecorView的content部分。

再来看installDecor方法,会先判断DecorView是否存在,不存在先调用generateDecor创建DecorView,然后判断mContentParent是否存在,不存在就把调用generateLayout(mDecor)创建mContentParent。
而generateLayout(mDecor)又会加载一个R.layout.screen_title的布局,这里面有TitleView布局和ContentView布局。



总结一下:Activity在首先会加载一个PhoneWindow对象,然后再去加载DecorView对象,DecorView会作为activity的根view存在,activity的setContentView最终会调用DecorView的setContentView方法,将布局加载到DecorView上,DecorView由title和content两部分组成。

2.2 DecorView添加到window

上文介绍了view的层次和DecorView的创建过程,现在需要将创建好的DecorView添加到window中去。


 if (sMainThreadHandler == null) {            sMainThreadHandler = thread.getHandler();        }        if (false) {            Looper.myLooper().setMessageLogging(new                    LogPrinter(Log.DEBUG, "ActivityThread"));        }        // End of event ActivityThreadMain.        Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);        Looper.loop();

每当我们启动一个Activity的时候,都会执行ActivityThread.main()方法,main方法里面有一个Looper一直在在等待主线程的消息,可以看到sMainThreadHandler会接受一个handler,这是一个内部类H,继承自Handler。看一下它的handleMessage()方法,

public void handleMessage(Message msg) {     if (DEBUG_MESSAGES) Slog.v(TAG, ">>> handling: " + codeToString(msg.what));            switch (msg.what) {            ....            case RELAUNCH_ACTIVITY:                    handleRelaunchActivityLocally((IBinder) msg.obj);                    break;            }

接收到启动activity的消息之后就会调用handleRelaunchActivityLocally,然后该方法会调用handleLaunchActivity(r, pendingActions, customIntent),handleLaunchActivity又会调用performLaunchActivity(r, customIntent),方法内部会调用mInstrumentation.callActivityOnCreate去回调生命周期方法onCreate,这样就完成DecorView的创建。

onCreate之后会走到onStart,handleStartActivity方法里面会走一些延迟加载的任务,保存状态等。

然后就是onResume,handleResumeActivity会执行将DecorView加载到window的操作,看一下源码:

@Overridepublic void handleResumeActivity(IBinder token, boolean finalStateRequest,boolean isForward,String reason) {    ...    if (r.window == null && !a.mFinished && willBeVisible) {            r.window = r.activity.getWindow();            View decor = r.window.getDecorView();            decor.setVisibility(View.INVISIBLE);            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();                if (impl != null) {                    impl.notifyChildRebuilt();                }            }            if (a.mVisibleFromClient) {                if (!a.mWindowAdded) {                    a.mWindowAdded = true;                    wm.addView(decor, l);                } else {                    // The activity will get a callback for this {@link LayoutParams} change                    // earlier. However, at that time the decor will not be set (this is set                    // in this method), so no action will be taken. This call ensures the                    // callback occurs with the decor set.                    a.onWindowAttributesChanged(l);                }            }            ...    }}

这个方法内会将onCreate时创建的DecorView通过getWindowManager返回的wm的实例方法wm.addView(decor, l)添加到WindowManager中,WindowManager的实例是WindowManagerImpl,它的addView又会调用WindowManagerGlobal的addView方法,通过ViewRootImpl实例的setView()方法,将DecorView塞到window里面。

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

参考资料

  • 《Android进阶之光》
  • Android中Activity启动过程探究
  • View 体系详解:View 的工作流程

更多相关文章

  1. android 使用AsyncTask实现异步下载文件
  2. android通过更改hosts免优酷广告方法
  3. 如何优雅的避免android(安卓)运行时崩溃
  4. android 热修复之类加载机制
  5. Android之Weight属性源码解析
  6. [android] HttpURLConnection的初步学习
  7. Adapter
  8. Android布局优化(一),Android渲染机制
  9. Android(安卓)抽屉效果的导航菜单实现

随机推荐

  1. Android 为【apk】文件签名,增加修改系统
  2. Android 控件大全
  3. Android应用架构之MVP模式
  4. Android Service总结02 service介绍
  5. EditText android:editable is deprecate
  6. Android学习笔记进阶1之MediaPlayer
  7. Android关于drawable和drawable-v24,mipma
  8. Android平台OpenMax多媒体引擎介绍
  9. Android模块化和组件化开发简单理解(一)
  10. Android 如何实现手机震动