Android DecorView源码解读

目录

1、几个概念

1)Window

2)PhoneWindow

 3)DecorView

 2、创建DecorView

1)在Activity中调用setContentView()

2)mDecor、mContentParent的创建之PhoneWindow的 installDecor()

3、ViewRoot完成对DectorView的绘制


最近疯狂的喜欢看Android的源码,看着看着就到了DecorView,那就进到源码中去看下这里面的一些东西。

1、几个概念

1)Window

抽象类。是屏幕上绘制各种UI以及响应用户的输入事件。主要包括三部分内容

  • mWindowAttributes :WindowManager.LayoutParams 
  • Callback
  • ViewTree
public abstract class Window {
//。。。。省略代码

    // The current window attributes.
    private final WindowManager.LayoutParams mWindowAttributes =
        new WindowManager.LayoutParams();
    
    private Window mContainer;
    private Window mActiveChild;
//。。。。省略代码

    public interface Callback {
        //。。。。省略代码
    }
//。。。。省略代码

}

2)PhoneWindow

继承于Window,用来提供绘制窗口。含有窗口的顶级DectorView。

public class PhoneWindow extends Window implements MenuBuilder.Callback {
    //顶级view,包括statusbar actionbar 以及content
    private DecorView mDecor;
    //对应的content。对应com.android.internal.R.id.content
    ViewGroup mContentParent;
    //标题栏的一些属性设置
    private TextView mTitleView;
    //R.id.left_icon
    private ImageView mLeftIconView;
//R.id.right_icon
    private ImageView mRightIconView;  
 
}

mDecor:为顶级的DectorView,包括statusbar actionbar 以及content;

mContentParent:对应的窗口栏。对应的id为com.android.internal.R.id.content;

其它的几个控件对应的就是标题栏里面的几个控件的设置。
 

 3)DecorView

是包含在标题栏、Activity设置的contentView,底部导航栏大小的View、状态栏大小的View。

其中底部导航栏大小的View、状态栏大小的View这两个view在运行时添加到底部导航栏和状态栏区域的下面。

public class DecorView extends FrameLayout implements RootViewSurfaceTaker, WindowCallbacks {
}

从API21(Android 5.x)之后,可以通过getWindow().setStatusBarColor()/getWindow().setNavigationBarColor()来设置状态栏和底部导航栏的颜色。

 2、创建DecorView

1)在Activity中调用setContentView()

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }

跟进Activity.java的源代码

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

可以看到调用的是PhoneWinow.java 中的setContentView()

@Override
    public void setContentView(int layoutResID) {
       //如果mContentParent为null,则调用installDecor()进行生成
        if (mContentParent == null) {
            installDecor();
        } else if (!hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
        //如果不为null,需要将里面的view移除
            mContentParent.removeAllViews();
        }
        if (hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
        //。。。省略代码
        } else {
        //将该layout插入到mContentParent
            mLayoutInflater.inflate(layoutResID, mContentParent);
        }
        mContentParent.requestApplyInsets();
       // 。。。省略代码,回调Activity
    }

这个流程就是如果 mContentParent还没有创建出来,则去创建mContentParent;如果已经创建过,则移除上面所有的view,然后通过inflate添加到mContentParent中。

也就是如果调用一次setContentView(),mContentParent则创建出来了,以后在调用的时候,就直接移除view,在执行inflater就可以了。

mContentParent创建在第一次调用setContentView()

我们跟进到 installDecor()来看下这个mContentParent是怎么创建出来的。

2)mDecor、mContentParent的创建之PhoneWindow的 installDecor()

private void installDecor() {
        mForceDecorInstall = false;
        if (mDecor == null) {
//生成mDecor
            mDecor = generateDecor(-1);
     //。。。。省略代码
        } else {
            mDecor.setWindow(this);
        }
        if (mContentParent == null) {
    //生成mContentParent
            mContentParent = generateLayout(mDecor);
          //。。。。省略代码
            final DecorContentParent decorContentParent = (DecorContentParent) mDecor.findViewById(
                    R.id.decor_content_parent);
            if (decorContentParent != null) {
                mDecorContentParent = decorContentParent;
//设置mDecorContentParent的一些属性setWindowCallback setIcon/setLogo
                mDecorContentParent.setUiOptions(mUiOptions);
                //。。。。省略代码              
            } else {
                mTitleView = findViewById(R.id.title);
                if (mTitleView != null) {
//如果FEATURE_NO_TITLE,则隐藏mTitleView,否则设置mTitleView
         //。。。。省略代码
                }
            }
//FEATURE_ACTIVITY_TRANSITIONS
            if (hasFeature(FEATURE_ACTIVITY_TRANSITIONS)) {
 //。。。。省略代码
            }
        }
    }

该方法中主要就是得到mDecor、mContentParent。看下是怎么生成的

  • 得到mDecor ->generateDecor()
 protected DecorView generateDecor(int featureId) {
      //通过不同的情况获取context
        Context context;
       //。。。。省略代码
        return new DecorView(context, featureId, this, getAttributes());
    }

根据不同情况获取Context,最后new DecorView 

  • 得到mContentParent->generateLayout()
 protected ViewGroup generateLayout(DecorView decor) {
        // Apply data from current theme.
        TypedArray a = getWindowStyle();
       //根据TypedArray来获取设置的Window_windowIsFloating、Window_windowNoTitle、Window_windowActionBarOverlay、Window_statusBarColor、Window_navigationBarColor等style从而设置decor的各个属性
//。。。。省略代码
       
        // Inflate the window decor.
        int layoutResource;
        int features = getLocalFeatures();
        // 根据设置的不同样式来决定decor对应的布局文件
        // 具体可以参照下面表格
    //。。。。省略代码
     //这个将该布局文件设置到mDecor中
        mDecor.onResourcesLoaded(mLayoutInflater, layoutResource);

        ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);
        //。。。。省略代码 设置decor的属性
        return contentParent;
    }

主要是根据设置的style对mDecor设置各个属性,并且根据不同的情况设置mDecor的布局文件。

该表格就是根据设置的不同theme来匹配mDecor的不同的布局文件:

样式

mIsFloating:true

false

FEATURE_SWIPE_TO_DISMISS

R.layout.screen_swipe_dismiss

 

FEATURE_LEFT_ICON

FEATURE_RIGHT_ICON

 TypedValue res = new TypedValue();

getContext().getTheme().resolveAttribute( R.attr.dialogTitleIconsDecorLayout, res, true);

layoutResource = res.resourceId;

R.layout.screen_title_icons

FEATURE_PROGRESS

FEATURE_INDETERMINATE_PROGRESS

API 21之后废弃
FEATURE_CUSTOM_TITLER.attr.dialogCustomTitleDecorLayoutR.layout.screen_custom_title
FEATURE_NO_TITLE/无标题栏,最常用的

true:R.attr.dialogTitleDecorLayout

FEATURE_ACTION_BAR:

layoutResource = a.getResourceId( R.styleable.Window_windowActionBarFullscreenDecorLayout, R.layout.screen_action_bar)

R.layout.screen_title
FEATURE_ACTION_MODE_OVERLAYR.layout.screen_simple_overlay_action_mode
其它/全屏R.layout.screen_simple

上面得到对应的layoutResource,然后通过mDecor.onResourcesLoaded(mLayoutInflater, layoutResource),将layoutResource设置到mDecor中。

 theme就是我们通过下面两种方式设置的:

a)在Activity中调用requestWindowFeature(),

b)在AndroidManifest.xml文件中设置Activity或者Application的android:theme。

所以我们在调用requestWindowFeature()的时候要在setContentView()之前,因为这个创建mDecor的时候要去选择对应的布局文件。

我们重点看下标记的两个常用的布局文件R.layout.screen_title和R.layout.screen_simple。这两个文件位于framework/base/core/res/layout/

(1)R.layout.screen_simple

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:fitsSystemWindows="true"
    android:orientation="vertical">
    <ViewStub android:id="@+id/action_mode_bar_stub"
              android:inflatedId="@+id/action_mode_bar"
              android:layout="@layout/action_mode_bar"
              android:layout_width="match_parent"
              android:layout_height="wrap_content" />
    <FrameLayout
         android:id="@android:id/content"
         android:layout_width="match_parent"
         android:layout_height="match_parent"
         android:foregroundInsidePadding="false"
         android:foregroundGravity="fill_horizontal|top"
         android:foreground="?android:attr/windowContentOverlay" />
</LinearLayout>

(2) R.layout.screen_title

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

从generateLayout()里面的代码中可以看出来 mContentParent就是取得这些布局文件中的@android:id/content下对应的这个FrameLayout。

下面这个图是DectorView上的ViewTree结构:

 

下面这个图是Window、PhoneWindow、DectorView之间的关系,以及DectorView上面的各部分的UI是怎么添加到DectorView

上的。

3、ViewRoot完成对DectorView的绘制

每一个DectorView对应一个ViewRoot,ViewRoot完成对DectorView的measure、layout、draw。

当Acitivity执行到onResume(),用户可见窗口的时候,在ActivityThread.handleResumeActivity()中将ViewRootImpl和decorView进行关联起来

    final void handleResumeActivity(IBinder token,
            boolean clearHide, boolean isForward, boolean reallyResume, int seq, String reason) {
  //。。。。。省略代码
 
      if (r.window == null && !a.mFinished && willBeVisible) {
                r.window = r.activity.getWindow();
                View decor = r.window.getDecorView();
                decor.setVisibility(View.INVISIBLE);
              //。。。。。省略代码
                if (r.mPreserveWindow) {
                    a.mWindowAdded = true;
                    r.mPreserveWindow = false;
        //每一个DecorView都有一个对应的ViewRoot
                    ViewRootImpl impl = decor.getViewRootImpl();
                    if (impl != null) {
                        impl.notifyChildRebuilt();
                    }
                }
    //。。。。。省略代码
     }
    //。。。。。省略代码
 }

通过调用impl.notifyChildRebuilt()来完成DectorView的measure、layout、draw

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值