21-1-21:Android开发艺术探索读书笔记:View工作原理

ViewRoot DecorView

ViewRoot是ViewRootImpl类 用于连接WindowManager DecorView
三大流程都是通过ViewRoot来完成的。ActivityThread中Activity对象创建完毕以后会把DecorView添加到Window中同时创建ViewRootImpl对象 并把ViewRootImpl和DecorView相关联

View 绘制 从ViewRoot的performTraversal开始 经过measure(测量) layout (位置)draw(绘制)绘制出一个view
在这里插入图片描述
View树的概念:一次onMeasure会调用到所有的子元素 以此循环 类似的 从最顶级的节点循环到所有的view中 另外两个方法是类似的。当然只有最后draw方法完成了view的内容才会呈现出来

DecorView是FramLayout的子类(可以从源码中看到)。DecorView作为顶级View一般情况下包括一个竖直方向的线性布局 上方是标题栏 下方是内容栏。通过基础设置的布局就是在内容栏里的。获取方法

ViewGroup viewGroup = findViewById(android.R.id.content);

设置View

viewGroup.getChildAt(0);

MeasureSpec

MeasureSpec很大程度上决定了一个View的尺寸规格,受父容器影响。测量过程中系统把View的LayoutParams根据父容器的绘制规则转换成对应的MeasureSpec,再根据这个MeasureSpec来测量view长宽高(不一定等于view最终样式????)

MeasureSpec代表一个32位的int,高2位代表specMode测量模式 低30位代表SpecSize规格大小
在这里插入图片描述
在这里插入图片描述在这里插入图片描述
在这里插入图片描述

工作流程

measure

ViewGroup没有定义具体测量过程,具体方法需要子类自己实现。因为不同ViewGroup子类具有不同布局特性导致测量细节不一样

所有的view测量完成后通过getMeasuredWidth/Height就可以正确的得到View的测量数值。但是一个比较好的习惯是在onLayout方法中再去获得这些数值。

onCreate Start Resume 不能得到View准确信息 因为measure和Activity周期不是同步执行的 无法保证执行了方法时某个View已经测量完毕 (此时获得的长度就是0了)

准确获得长度方法
①onWindowFocusChanged
View已经初始化完毕 长度已经准备好了 得到 失去焦点的时候都会被调用
②view.post(runnable)
等待Looper调用此Runnable时View已经初始化好了
③ViewTreeObserver
使用ViewTreeObserver的众多回调可以完成这个功能 当View树状态发生改变 方法回调

    protected void onStart() {
        super.onStart();
        ViewTreeObserver observer = this.getCurrentFocus().getViewTreeObserver();
        observer.addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
            @Override
            public void onGlobalLayout() {
                getCurrentFocus().getViewTreeObserver().removeOnGlobalLayoutListener(this);
                int x = getCurrentFocus().getMeasuredWidth();
                int y = getCurrentFocus().getMeasuredHeight();
            }
        });
    }

④view.measure()
手动测量 太麻烦了 不写了

Layout过程

确定子元素位置 。ViewGroup位置确定之后会再onLayout中遍历所有子元素并且调用layout方法 在layout方法中onLayout方法又被调用 遍历tree

流程 :setFrame方法初始化四个顶点 (确定子View在父容器中位置)onLayout 方法确定子元素位置 这个方法具体实现和具体布局有关 类似于onMeasure

View默认实现中测量长度和最终长度是相等的,不过测量长度形成于measure 最终长度形成于layout 。日常开发中可以认为这两者相等

Draw过程

把View绘制到屏幕上
绘制背景 canvas
绘制自己 ondraw
绘制children dispatchDraw
(事件一层层传递)
绘制装饰 onDrawScrollBars

一个view不需要绘制任何内容 setWillNotDraw标记位会被设置 用于优化 默认ViewGroup启用 View不启用

自定义View

①自定义view分类

继承View重写onDraw

实现一些不规则效果
需要自己支持wrap_content padding 也要自己支持

实例代码:

public class MyView extends View {
    public MyView(Context context) {
        super(context);
        paint.setColor(Color.RED);
    }
    Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        final  int paddingLeft = getPaddingLeft();
        final  int paddingRight = getPaddingRight();
        final  int paddiingTop = getPaddingTop();
        final  int paddingBottom = getPaddingBottom();
        int width = getWidth()-paddingLeft-paddingRight;
        int height = getHeight() - paddiingTop - paddingBottom;
        int radius = Math.min(width,height)/2   ;
        canvas.drawCircle(paddingLeft+width/2,paddiingTop+height/2,radius,paint);
    }
}

如上所示 padding没有生效 wrap_content的效果也会是和matchparents一样

在xml中自定义属性
步骤1:创建xml文件

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <declare-styleable name="MyView"><--自定义属性集合 可以有很多属性-->
        <attr name="circle_color" format="color"/>
    </declare-styleable>
</resources>

步骤2:
在View构造方法中解析自定义属性的数值并且做好相应处理

    public MyView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        TypedArray array = context.obtainStyledAttributes(attrs,R.styleable.MyView);
        mColor = array.getColor(R.styleable.MyView_circle_color,Color.RED);
        array.recycle();
        paint.setColor(mColor);
    }

步骤3:

对应的布局文件中就可以使用这个属性了

注意事项:

自定义属性前缀

xmlns:app="http://schemas.android.com/apk/res-auto"

继承ViewGroup派生的特殊Layout
实现自定义布局

继承特定View
扩展某种已有View的功能

继承特定ViewGroup
方法2更接近底层

②自定义View事项

让view支持wrap_content

不做特殊处理就无法达到预期效果

如果有必要 让view支持padding

直接继承View控件 不在draw方法中处理padding 那么padding将无法起作用
继承ViewGroup控件需要在onMeasure和onLayout中考虑padding和子元素margin造成的影响

尽量不在View中使用handler

内部提供了post系列方法

有线程或者动画 需要停止 使用 onDetachedFromWindow

有这种需求时 onDetachedFromWindow是一个很好的时机 包含此View的Activity退出或者当前View被remove时 方法调用。View不可见的时候应该停止线程和动画 否则导致内存泄漏

处理好嵌套滑动冲突

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值