DecorView

基础

        对于应用来说,Activity仅仅是一个载体,它本身并不负责任何界面的绘制,只是允许在其上创建界面,并提供一些API用于响应用户的操作,同时维护应用程序的生命周期等。所有的绘制都是交由Activity内部的Window(只有一个实现对象PhoneWindow)对象来实现的,而PhoneWindow内部在添加View之前,会首先创建一个DecorView,后继所有的View都是添加到DecorView中的。因此,DecorView是所有View的最深层节点

        可以通过getWindow().getDecorView()获取到当前Activity对应的DecorView对象。利用该对象可以实现截屏等功能。如下:

        View view = getWindow().getDecorView();
        view.setDrawingCacheEnabled(true);
        Bitmap bitmap = view.getDrawingCache();
        try {
            bitmap.compress(Bitmap.CompressFormat.JPEG,50,new FileOutputStream(new File(Environment.getExternalStorageDirectory(),System.currentTimeMillis()+".jpg")));
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }

层级分析

        在5.0以上和5.0以下是不同的。如下:

//----------------------------------4.4--------------------------------------------
|-name = DecorView,    [0,0][720,1280]
    |-name = LinearLayout,    [0,0][720,1280]
        |-name = ViewStub,    [0,0][0,0]
        |-name = FrameLayout,    [0,50][720,1280]
            |-name = ActionBarOverlayLayout,    [0,0][720,1230]
                |-name = ContentFrameLayout,    [0,112][720,1230]
                    |-name = LinearLayout,    [0,0][720,1118]
                        |-name = AppCompatEditText,    [0,0][720,38]
                        |-name = AppCompatButton,    [0,38][720,134]
                        |-name = AppCompatTextView,    [0,134][720,172]
                |-name = ActionBarContainer,    [0,0][720,112]
                    |-name = Toolbar,    [0,0][720,112]
                        |-name = TextView,    [32,29][160,83]
                        |-name = ActionMenuView,    [0,0][0,0]
                    |-name = ActionBarContextView,    [0,0][0,0]
//----------------------------------模拟器6.0--------------------------------------------
|-name = DecorView,    [0,0][480,854]                                              //最顶层的DecorView
    |-name = LinearLayout,    [0,0][480,854]						//整体空间,包含状态栏在内,但无法修改状态栏
        |-name = ViewStub,    [0,0][0,0]
        |-name = FrameLayout,    [0,24][480,854]									//除状态栏之后的剩余空间
            |-name = ActionBarOverlayLayout,    [0,0][480,830]                      //占满上面的FrameLayout
                |-name = ContentFrameLayout,    [0,56][480,830]						//除标题栏之外的剩余空间
                    |-name = LinearLayout,    [0,0][480,774]						//自己View的根结点
                        |-name = AppCompatEditText,    [0,0][480,28]
                        |-name = AppCompatButton,    [0,28][480,76]
                        |-name = AppCompatTextView,    [0,76][480,95]
                |-name = ActionBarContainer,    [0,0][480,56]                      //标题栏
                    |-name = Toolbar,    [0,0][480,56]
                        |-name = TextView,    [16,14][79,42]
                        |-name = ActionMenuView,    [0,0][0,0]
                    |-name = ActionBarContextView,    [0,0][0,0]
    |-name = View,    [0,0][480,24]												//状态栏
        对比上面可以发现:主要区别在于5.0以上DecorView有两个子View,而5.0以下的只有一个。多出的第二个子View就是状态栏。因此,可以通过下面代码直接修改状态栏的背景色,当然此种方法只能在5.0以上的系统运行,5.0以下的就会直接崩溃:
decorView.getChildAt(1).setBackgroundColor(Color.YELLOW);//将状态栏背景改为黄色
       ContentFrameLayout,它是布局所要填充到的View,其Id为android.R.id.content。其内部只有一个View,就是自己布局的根View。因此可以通过下面代码获取布局的根View。如下:
        ViewGroup content = (ViewGroup) decorView.findViewById(android.R.id.content);
        content.getChildAt(0);
       ActionBarContainer:与ContentFrameLayout平级,代表着标题栏。因此,可以通过下面代码修改标题栏。如下:
        //先找到内容区域,再获取内容区域的父View。因为标题栏区域与内容区域平级
        ViewGroup vp = (ViewGroup) decorView.findViewById(android.R.id.content).getParent();
        //所以使用上步获取到的父View.getChildAt(1)就可以拿到标题栏区域的根结点
        ViewGroup actionbar = (ViewGroup) vp.getChildAt(1);
      //  actionbar.setBackgroundColor(Color.YELLOW);//修改标题栏的背景色
        //重定义标题栏的布局
        actionbar.removeAllViews();
        TextView tv = new TextView(this);
        tv.setText("custom");
        tv.setGravity(Gravity.RIGHT);
        actionbar.addView(tv);

附件

        获取上面DecorView的层级结构的代码如下,直接使用showPos(decorView,0)即可。

    private void showPos(View view,int level){
        StringBuilder sb = new StringBuilder();
        int x;
        for(x = 0;x<level;x++){
            sb.append("    ");
        }
        sb.append("|-");
        String name = view.getClass().getSimpleName();
        sb.append("name = ");
        sb.append(name);
        sb.append(",    ");
        sb.append(String.format(Locale.CHINA,"[%d,%d][%d,%d]",view.getLeft(),view.getTop(),view.getRight(),view.getBottom()));
        Log.e(TAG,sb.toString());
        if(view instanceof  ViewGroup){
            ViewGroup v = (ViewGroup) view;
            for(x = 0;x<v.getChildCount();x++){
                showPos(v.getChildAt(x),level+1);
            }
        }
    }

构造函数

        Activity#setContentView()->PhoneWindow#setContentView()->installDecor()->generateDecor()->DecorView#构造函数。

    protected DecorView generateDecor() {//PhoneWindow#generateDecor()
        return new DecorView(getContext(), -1);
    }
        由Window与PhoneWindow可知,getContext()返回的就是Window所关联的Activity实例。因此,DecorView中的mContext指的也是Window所关联的Activity实例。




  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值