Android:DecorView

DecorView

DecorView是FrameLayout的子类,被认为是android视图树的根结点视图。
DecorView作为顶层View,内部包含一个竖直方向的LinearLayout,在这个LinearLayout里面有上中下三部分,分别为ViewStub,标题栏,内容栏。

大致示意布局如下:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:fitsSystemWindows="true"
    android:orientation="vertical">
    <!-- Popout bar for action modes -->
    <ViewStub
        android:id="@+id/action_mode_bar_stub"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:inflatedId="@+id/action_mode_bar"
        android:layout="@layout/action_mode_bar"
        android:theme="?attr/actionBarTheme" />
 
    <FrameLayout
        style="?android:attr/windowTitleBackgroundStyle"
        android:layout_width="match_parent"
        android:layout_height="?android:attr/windowTitleSize">
 
        <TextView
            android:id="@android:id/title"
            style="?android:attr/windowTitleStyle"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:background="@null"
            android:fadingEdge="horizontal"
            android:gravity="center_vertical" />
    </FrameLayout>
 
    <FrameLayout
        android:id="@android:id/content"
        android:layout_width="match_parent"
        android:layout_height="0dip"
        android:layout_weight="1"
        android:foreground="?android:attr/windowContentOverlay"
        android:foregroundGravity="fill_horizontal|top" />
</LinearLayout>

在activity中我们经常使用到setContentView,每次传入的布局文件,其实就是加入到内容栏中,成为其唯一子View。

View Root

ViewRoot对应ViewRootImpl类,是连接WindowManagerService和DecorView的纽带,View的三大流程:测量(measure),布局(layout),绘制(draw)均通过ViewRoot来完成。
所以View的绘制以及事件分发等交互都通过它来执行或传递的。

ViewRoot并不属于View树的一份子。
ViewRoot非View的子类,也非其父类,但实现了ViewParent接口,这让它可以作为View的名义上的父视图。
ViewRoot继承了Handler类,可接收事件并分发,android所有的触屏时间、按键事件、界面刷新等事件都是通过ViewRoot进行分发。
在这里插入图片描述

DecorView的创建

由window对象完成视图装载

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

attach用于创建DecorView的实例,并将view传入其中。

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) {
		..................................................................
        mWindow = new PhoneWindow(this, window);//创建一个Window对象
        mWindow.setWindowControllerCallback(this);
        mWindow.setCallback(this);//设置回调,向Activity分发点击或状态改变等事件
        mWindow.setOnWindowDismissedCallback(this);
        .................................................................
        mWindow.setWindowManager(
        	(WindowManager)context.getSystemService(Context.WINDOW_SERVICE),
        	mToken, mComponent.flattenToString(),
                (info.flags & ActivityInfo.FLAG_HARDWARE_ACCELERATED) != 0);//给Window设置WindowManager对象
        ....................................................................
}
//mContentParent为内容栏的framelayout
public void setContentView(int layoutResID) {
    if (mContentParent == null) {//mContentParent为空,创建一个DecroView
    	installDecor();
    } else {
        mContentParent.removeAllViews();//mContentParent不为空,删除其中的View
    }
    mLayoutInflater.inflate(layoutResID, mContentParent);//为mContentParent添加子View,即Activity中设置的布局文件
    final Callback cb = getCallback();
    if (cb != null && !isDestroyed()) {
        cb.onContentChanged();//回调通知,内容改变
    }
}

由attach方法生成了PhoneWindow实例,在setContentView方法中为window对象设置Decorwindow。

先在PhoneWinodw中创建一个DecroView,在创建过程中根据Theme不同,加载不同的布局格式(title和actionbar有无等),然后再向mContentParent中加入子View,即activity中设置的布局,这里就完成了嵌套(window里面嵌套DecroView,DecroView中又嵌套LinearLayout…)

创建DecroView

private void installDecor() {
    if (mDecor == null) {
        mDecor = generateDecor(); //生成DecorView
        mDecor.setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);
        mDecor.setIsRootNamespace(true);
        if (!mInvalidatePanelMenuPosted && mInvalidatePanelMenuFeatures != 0) {
            mDecor.postOnAnimation(mInvalidatePanelMenuRunnable);
        }
    }
    if (mContentParent == null) {
        mContentParent = generateLayout(mDecor); // 为DecorView设置布局格式,并返回mContentParent
        ...
        } 
    }
}

创建DecorView对象

protected DecorView generateDecor() {
    return new DecorView(getContext(), -1);
}

获取样式信息,加载布局,并将其作为子view加入DecorView,返回mContentParent

protected ViewGroup generateLayout(DecorView decor) {
    // 从主题文件中获取样式信息
    TypedArray a = getWindowStyle();
 
    ...................
 
    if (a.getBoolean(R.styleable.Window_windowNoTitle, false)) {
        requestFeature(FEATURE_NO_TITLE);
    } else if (a.getBoolean(R.styleable.Window_windowActionBar, false)) {
        // Don't allow an action bar if there is no title.
        requestFeature(FEATURE_ACTION_BAR);
    }
 
    ................
 
    // 根据主题样式,加载窗口布局
    int layoutResource;
    int features = getLocalFeatures();
    // System.out.println("Features: 0x" + Integer.toHexString(features));
    if ((features & (1 << FEATURE_SWIPE_TO_DISMISS)) != 0) {
        layoutResource = R.layout.screen_swipe_dismiss;
    } else if(...){
        ...
    }
 
    View in = mLayoutInflater.inflate(layoutResource, null);//加载layoutResource
 
    //往DecorView中添加子View,即文章开头介绍DecorView时提到的布局格式,那只是一个例子,根据主题样式不同,加载不同的布局。
    decor.addView(in, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT)); 
    mContentRoot = (ViewGroup) in;
 
    ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);// 这里获取的就是mContentParent
    if (contentParent == null) {
        throw new RuntimeException("Window couldn't find content container view");
    }
 
    if ((features & (1 << FEATURE_INDETERMINATE_PROGRESS)) != 0) {
        ProgressBar progress = getCircularProgressBar(false);
        if (progress != null) {
            progress.setIndeterminate(true);
        }
    }
 
    if ((features & (1 << FEATURE_SWIPE_TO_DISMISS)) != 0) {
        registerSwipeCallbacks();
    }
 
    // Remaining setup -- of background and title -- that only applies
    // to top-level windows.
    ...
 
    return contentParent;
}

其实前面的内容归结起来就是:调用setContentView之后,创建DecorView实例,并传入View和主题的属性。

DecorView的显示

通过setContentView设置的页面,在onResume后才对用户可见,因为此时decor仅完成初始化,layout还未完成绘制。

private void handleLaunchActivity(ActivityClientRecord r, Intent customIntent) {
 
    //就是在这里调用了Activity.attach()呀,接着调用了Activity.onCreate()和Activity.onStart()生命周期,
    //但是由于只是初始化了mDecor,添加了布局文件,
    //还没有把mDecor添加到负责UI显示的PhoneWindow中,所以这时候对用户来说,是不可见的
    Activity a = performLaunchActivity(r, customIntent);
 
    ......
 
    if (a != null) {
    //这里面执行了Activity.onResume()
    handleResumeActivity(r.token, false, r.isForward,
                        !r.activity.mFinished && !r.startsNotResumed);
 
    if (!r.activity.mFinished && r.startsNotResumed) {
        try {
                r.activity.mCalled = false;
                //执行Activity.onPause()
                mInstrumentation.callActivityOnPause(r.activity);
                }
        }
    }
}

重点看下handleResumeActivity(),在这其中,DecorView将会显示出来,同时重要的一个角色:ViewRoot也将登场。

final void handleResumeActivity(IBinder token, boolean clearHide, 
                                boolean isForward, boolean reallyResume) {

    //这个时候,Activity.onResume()已经调用了,但是现在界面还是不可见的
    ActivityClientRecord r = performResumeActivity(token, clearHide);

    if (r != null) {
        final Activity a = r.activity;
        if (r.window == null && !a.mFinished && willBeVisible) {
            r.window = r.activity.getWindow();
            View decor = r.window.getDecorView();
            //decor对用户不可见
            decor.setVisibility(View.INVISIBLE);
            ViewManager wm = a.getWindowManager();
            WindowManager.LayoutParams l = r.window.getAttributes();
            a.mDecor = decor;

            l.type = WindowManager.LayoutParams.TYPE_BASE_APPLICATION;

            if (a.mVisibleFromClient) {
                a.mWindowAdded = true;
                //被添加进WindowManager了,但是这个时候,还是不可见的
                wm.addView(decor, l);
            }

            if (!r.activity.mFinished && willBeVisible
                    && r.activity.mDecor != null && !r.hideForNow) {
                //在这里,执行了重要的操作,使得DecorView可见
                if (r.activity.mVisibleFromClient) {
                    r.activity.makeVisible();
                }
            }
        }
    }
}

这里才实现了DecorView的可视。
wm.addView(mDecor, getWindow().getAttributes());起到了重要的作用,因为其内部创建了一个ViewRootImpl对象,负责绘制显示各个子View。

void makeVisible() {
   if (!mWindowAdded) {
        ViewManager wm = getWindowManager();
        wm.addView(mDecor, getWindow().getAttributes());//将DecorView添加到WindowManager
        mWindowAdded = true;
    }
    mDecor.setVisibility(View.VISIBLE);//DecorView可见
}

具体来看addView()方法,因为WindowManager是个接口,具体是交给WindowManagerImpl来实现的。接着WindowManagerImpl再交给WindowManagerGlobal 的addView()方法去实现

public final class WindowManagerImpl implements WindowManager {    
    private final WindowManagerGlobal mGlobal = WindowManagerGlobal.getInstance();
    ...
    @Override
    public void addView(View view, ViewGroup.LayoutParams params) {
        mGlobal.addView(view, params, mDisplay, mParentWindow);
    }
}



public void addView(View view, ViewGroup.LayoutParams params,
                    Display display, Window parentWindow) {
 
    final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams) params;
 
    ......
 
    synchronized (mLock) {
 
        ViewRootImpl root;
        //实例化一个ViewRootImpl对象
        root = new ViewRootImpl(view.getContext(), display);
        view.setLayoutParams(wparams);
 
        mViews.add(view);
        mRoots.add(root);
        mParams.add(wparams);
    }
 
    ......
 
    try {
        //将DecorView交给ViewRootImpl
        root.setView(view, wparams, panelParentView);
    } catch (RuntimeException e) {
 
    }
}

看到其中实例化了ViewRootImpl对象,然后调用其setView()方法。其中setView()方法经过一系列折腾,最终调用了performTraversals()方法,然后依照下图流程层层调用,完成绘制,最终界面才显示出来。
在这里插入图片描述
要知道,当用户点击屏幕产生一个触摸行为,这个触摸行为则是通过底层硬件来传递捕获,然后交给ViewRootImpl,接着将事件传递给DecorView,而DecorView再交给PhoneWindow,PhoneWindow再交给Activity,然后接下来就是我们常见的View事件分发了。硬件 -> ViewRootImpl -> DecorView -> PhoneWindow -> Activity。
由此可见ViewRootImpl的重要性,是个连接器,负责WindowManagerService与DecorView之间的通信。

DecorView的职责

以上通过源码形式介绍了Window、Activity、DecorView以及ViewRoot之间的错综关系,以及如何创建并显示DecorView。
通过以上了解可以知道,Activity就像个控制器,不负责视图部分。Window像个承载器,装着内部视图。DecorView就是个顶层视图,是所有View的最外层布局。ViewRoot像个连接器,负责沟通,通过硬件的感知来通知视图,进行用户之间的交互。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值