高级UI

作者: Lucky胡 | 来源:发表于2019-11-03 14:55 被阅读0次

    一、Android 布局添加流程

    二、View的绘制流程

    1、view绘制流程

    2、具体3大绘制过程

    1.测量过程performMeasure()

    2.布局过程performLayout()
    调用ViewRootImpl里的

    performLayout(WindowManager.LayoutParams lp, int desiredWindowWidth,
                int desiredWindowHeight)
    

    在这个方法里又调用了view的layout()方法

    view.layout(0, 0, host.getMeasuredWidth(), host.getMeasuredHeight());
    //在该方法中,计算了view的相对父布局的位置,即left/right/top/bottom
    //如果是viewgroup,则还需要实现onLayout(boolean changed, int left, int top, int right, int bottom),来计算其子view的位置,这样来算出每个view的位置
    

    比如FrameLayout作为一个ViewGroup,就实现了自己的onLayout()方法来计算子view的位置。

        protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
            layoutChildren(left, top, right, bottom, false /* no force left gravity */);
        }
    //在layoutChilden()里计算了所有子view的位置,然后子view调用自己的layout()方法将位置放入自身
    

    总结:
    ViewGroup
    确定自己的位置:layout(),调用onLayout()进行子view的布局。
    对于自定义的ViewGroup,需要重写onLayout()来计算子view的位置。

    View
    调用layout()确定自己的位置即可。

    3.绘制过程performDraw()
    里面调用了ViewRootImpl.draw()方法,然后调动drawSoftware(),里面调用了view的draw()方法

    public void draw(Canvas canvas) {
    
            /*
             * Draw traversal performs several drawing steps which must be executed
             * in the appropriate order:
             *
             *      1. Draw the background
             *      2. If necessary, save the canvas' layers to prepare for fading
             *      3. Draw view's content
             *      4. Draw children
             *      5. If necessary, draw the fading edges and restore layers
             *      6. Draw decorations (scrollbars for instance)
             */
    
            // Step 1, draw the background, if needed
            int saveCount;
    
            if (!dirtyOpaque) {
                drawBackground(canvas);
            }
    
            // skip step 2 & 5 if possible (common case)
            final int viewFlags = mViewFlags;
            boolean horizontalEdges = (viewFlags & FADING_EDGE_HORIZONTAL) != 0;
            boolean verticalEdges = (viewFlags & FADING_EDGE_VERTICAL) != 0;
            if (!verticalEdges && !horizontalEdges) {
                // Step 3, draw the content
                if (!dirtyOpaque) onDraw(canvas);
    
                // Step 4, draw the children
                dispatchDraw(canvas);
    
                drawAutofilledHighlight(canvas);
    
                // Overlay is part of the content and draws beneath Foreground
                if (mOverlay != null && !mOverlay.isEmpty()) {
                    mOverlay.getOverlayView().dispatchDraw(canvas);
                }
    
                // Step 6, draw decorations (foreground, scrollbars)
                onDrawForeground(canvas);
    
                // Step 7, draw the default focus highlight
                drawDefaultFocusHighlight(canvas);
    
                if (debugDraw()) {
                    debugDrawFocus(canvas);
                }
    
                // we're done...
                return;
    }
    

    如果View是ViewGroup,其重写了dispatchDraw()方法。
    在dispatchDraw()里遍历其子view,调用子view的draw()方法绘制自身。

    总结绘制过程:
    1.绘制背景drawBackground(canvas)
    2.绘制自己onDraw(canvas)
    3.绘制子view dispatchDraw(canvas) -->如果是ViewGroup,子view该方法为空
    4.绘制前景、滚动条等装饰onDrawForeground(canvas)

    额外知识、Android系统UI卡顿原理及VSync信号机制

    1、卡顿原因分析及常见解决方式

    1.过度绘制
    去除不必要的背景色;

    布局视图扁平化:避免过度嵌套布局,每次测量都需要测量父布局、子布局的所有位置,尽量采取扁平布局,例如ConstrainLayout,减少ReletiveLayout、LinearLayout嵌套布局。

    减少透明色的使用;

    2.UI线程里复杂运算

    3.频繁GC
    尽可能减少在for循环里new对象;减少在onDraw里new对象
    尽量不要在循环中大量使用局部变量。

    2、VSync机制

    1.概念

    1.屏幕刷新率
    2.帧率 FPS
    3.VSync
    屏幕产生的硬件VSync
    由SurfaceFlinger将其转换成软件VSync信号

    2.VSync的作用

    Vertical Synchronization 垂直同步的缩写
    主要是为了解决“Tearing”撕裂的现象
    同步UI绘制和动画,是的可以达到60fps的固定帧率。

    VSync工作原理示意图 双重缓存模型

    GPU计算结果放到Buffer里,屏幕从另一个Buffer里取数据。在VSync信号触发交换读写缓存。

    三重缓存模型

    3、Choreographer机制

    三、高级绘制:Paint/Canvas/Path详解

    1、Paint详解

    概念:画笔,保存了绘制几何图形、文本和位图的样式和颜色信息。
    (1)常用API:


    Paint常用API Paint.setStrokCap()效果 Paint.setStrokeJoin()效果 setFilterBitmap()设置双线性过滤 字体的度量
    //字体的度量,paint.getFontMetrics()
        public static class FontMetrics {
            /**
             * The maximum distance above the baseline for the tallest glyph in
             * the font at a given text size.
             */
            public float   top;
            /**
             * The recommended distance above the baseline for singled spaced text.
             */
            public float   ascent;
            /**
             * The recommended distance below the baseline for singled spaced text.
             */
            public float   descent;
            /**
             * The maximum distance below the baseline for the lowest glyph in
             * the font at a given text size.
             */
            public float   bottom;
            /**
             * The recommended additional space to add between lines of text.
             */
            public float   leading;
        }
    

    (2)颜色相关

    
        private void setColor(){
            mPaint = new Paint();
            mPaint.setColor(Color.RED);   //设置颜色,16进制数值,0xFFFF0000
            mPaint.setARGB(0,0,0,0); //分别表示透明度,RGB三原色,0~255数值
            Shader shader = new Shader();
            //一般由以下几种着色器
            shader = new LinearGradient(); 
            shader = new RadialGradient();
            shader = new SweepGradient();
            shader = new BitmapShader();
            shader = new ComposeShader();
            mPaint.setShader(shader);   //着色器
            //
        }
    

    1.线性着色器:

    //float x0, float y0, float x1, float y1, @NonNull @ColorInt int colors[],@Nullable float positions[], @NonNull TileMode tile
    //colors[]颜色数组
    // positions[]数值范围[0,1],指定某个位置的颜色值,个数应该和colors[]个数一样,否则报错。默认为null,线性渐变。
    mShader = new LinearGradient(0,0,500,500,new int[]{Color.RED,Color.BLUE},null, Shader.TileMode.CLAMP);
    
    线性着色器效果

    2.环形渲染

    
            //RadialGradient()构造方法:
            //float centerX, float centerY, float radius,
            //            @NonNull @ColorInt int colors[], @Nullable float stops[],
            //            @NonNull TileMode tileMode
    
            //float centerX, float centerY, float radius,
            //            @ColorInt int centerColor, @ColorInt int edgeColor, @NonNull TileMode tileMode
    
    
            mShader = new RadialGradient(250,250,250,new int[]{Color.GREEN,Color.YELLOW,Color.RED},null, Shader.TileMode.CLAMP);
            
    
    环形渲染

    3.扫描渲染

            //SweepGradient()构造方法:
            //float cx, float cy, @NonNull @ColorInt int colors[], @Nullable float positions[]
            //float cx, float cy, @ColorInt int color0, @ColorInt int color1
    
    mShader  = new SweepGradient(250,250,Color.RED,Color.GREEN);
    
    
    扫描渲染

    4.位图渲染

    
            mBitmap = BitmapFactory.decodeResource(getResources(),R.mipmap.liuyifei);
            mShader = new BitmapShader(mBitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);
    
    ...
    
        @Override
        protected void onDraw(Canvas canvas) {
            super.onDraw(canvas);
            canvas.drawRect(0,0,mBitmap.getWidth(),mBitmap.getHeight(),mPaint);
        }
    
    
    
    位图渲染

    不同Shader.TileMode

    // draw的区域超过shader的大小时,如何绘制。有以下三种绘制方法:
        public enum TileMode {
            /**
             * replicate the edge color if the shader draws outside of its
             * original bounds
             */
            CLAMP   (0),
            /**
             * repeat the shader's image horizontally and vertically
             */
            REPEAT  (1),
            /**
             * repeat the shader's image horizontally and vertically, alternating
             * mirror images so that adjacent images always seam
             */
            MIRROR  (2);
        
            TileMode(int nativeInt) {
                this.nativeInt = nativeInt;
            }
            final int nativeInt;
        }
    
    
    
        @Override
        protected void onDraw(Canvas canvas) {
            super.onDraw(canvas);
            canvas.drawRect(0,0,mBitmap.getWidth()*1.5f,mBitmap.getHeight()*1.5f,mPaint);
        }
    
    TileMode.CLAMP TileMode.MIRROR TileMode.REPEAT

    5.组合渲染

    
    
             //ComposeShader()的构造方法:
            //@NonNull Shader shaderA, @NonNull Shader shaderB, @NonNull PorterDuff.Mode mode
            //@NonNull Shader shaderA, @NonNull Shader shaderB, @NonNull Xfermode mode
    
            mBitmap = BitmapFactory.decodeResource(getResources(),R.mipmap.liuyifei);
            BitmapShader bitmapShader = new BitmapShader(mBitmap, Shader.TileMode.REPEAT, Shader.TileMode.REPEAT);
            LinearGradient linearGradient = new LinearGradient(0,0,500,500,new int[]{Color.RED,Color.BLUE,Color.GREEN},null, Shader.TileMode.CLAMP);
    
            mShader = new ComposeShader(bitmapShader,linearGradient, PorterDuff.Mode.MULTIPLY);
    
    
    组合渲染

    其中PorterDuff.Mode是组合渲染中两个Shader进行混合的规则,PorterDuff.Mode.MULTIPLY是相乘。

    2、利用Paint绘制滤镜Xfermode

    PorterDuff.Mode图层混合模式
    一共有18种模式。

    //有三个地方用到图层混合
    //1、ComposeShader
    //2、mPaint.setXfermode
    //3、PorterDuffColorFilter
            mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
            mShader = new ComposeShader(bitmapShader,linearGradient, new PorterDuffXfermode(PorterDuff.Mode.MULTIPLY));
            mPaint.setShader(mShader);
    
            //由于某些图层混合在硬件加速下不能用
            //所以需要禁止硬件加速
            setLayerType(View.LAYER_TYPE_SOFTWARE,null);
    

    相关文章

      网友评论

          本文标题:高级UI

          本文链接:https://www.haomeiwen.com/subject/lwakbctx.html