Android基础之View的绘制原理

View与ViewGroup绘制原理解析(一): 绘制流程 Android组件View绘制流程原理分析 android视图构成 如上图,Activity的window组成,Activity内部有个Window成员,它的实例为PhoneWindow,PhoneWindow有个内部类是DecorView,这个DecorView就是存放布局文件的,里面有TitleActionBar和我们setContentView传入进去的layout布局文件 Window类时一个抽象类,提供绘制窗口的API PhoneWindow是继承Window的一个具体的类,该类内部包含了一个 阅读详情

在这里插入图片描述

View是什么?

View是屏幕上的一块矩形区域,它负责用来显示一个区域,并且响应这个区域内的事件。可以说,手机屏幕上的任意一部分看的见得地方都是View,它很常见,比如 TextView 、ImageView 、Button以及LinearLayout、RelativeLayout都是继承子View的。
对于Activity来说,我们通过setContentView(view)添加的布局到Activity上,实际上都是添加到了Activity 内部的DecorView上面,这个DecorView,其实就是一个FrameLayout,因此实际上,我们的布局实际上添加到了FrameLayout里面。

绘制原理

1.测量

简单流程就是:

  1. 确定view是树结构
  2. 递归遍历每个子节点,即子view,进行测量。

image

下面具体说一下:

首先view是树结构。
也就是说子view是父view的孩子节点。
根节点是就是DecorView。

了解树的都知道,树的遍历都是递归遍历。
那么测量view的过程,其实就是遍历树的过程。

测量什么呢,怎么做一个记录呢。
就有了MesureSpec。是一个封装的int类型。
高2位表示mode,其余表示size。

mode即类型有哪些呢。
通常我们在定义一个view的宽高时有三种写法。

一是直接写多少多少dp
一个是match_parent
一个是wrap_content

所以mode也有三种,分别是:
EXACTLY:对应固定数值的写法,有确切的size。
AT_MOST:父view给定一个size,子view不超过这个size即可。
UNSPECIFIED:似乎没有用到。

所以不同类型的view它的测量方式是不同的。
比如LinearLayout和FrameLayout具体的测量方式是不同的。

测量的逻辑(可以先不要看代码,看一下具体逻辑,思考一下怎么实现)

  1. 测量一个view需要知道父view的MeasureSpec。因为需要父view的mode和size来确定子view的mode和size

  2. 根节点没有父view,那么MeasureSpec从哪里获取
    最外层的根节点DecorView的MeasureSpec只由自己的LayoutParams决定
    如果是match_parent和固定数值对应的就是EXACTLY
    如果是wrap_content对应的就是AT_MOST

对应的方法是

private static int getRootMeasureSpec(int windowSize, int rootDimension) {
    int measureSpec;
    switch (rootDimension) {
    //如果是MATCH_PARENT,那么就是EXACTLY
    case ViewGroup.LayoutParams.MATCH_PARENT:
        // Window can't resize. Force root view to be windowSize.
        measureSpec = MeasureSpec.makeMeasureSpec(windowSize, MeasureSpec.EXACTLY);
        break;
    //如果是WRAP_CONTENT,就是AT_MOST
    case ViewGroup.LayoutParams.WRAP_CONTENT:
        // Window can resize. Set max size for root view.
        measureSpec = MeasureSpec.makeMeasureSpec(windowSize, MeasureSpec.AT_MOST);
        break;
    default:
        //如果是固定的值,也是EXACTLY
        // Window wants to be an exact size. Force root view to be that size.
        measureSpec = MeasureSpec.makeMeasureSpec(rootDimension, MeasureSpec.EXACTLY);
        break;
    }
    return measureSpec;
}

  1. 从根节点开始,遍历子view
    整体流程是其实就是层次遍历。先计算子view的count,然后for循环,获取每个子view,计算其宽高的MeasureSpce,然后子view也是view吧。再去递归调用view的measure方法实现递归调用。

这里子view其实分为view和ViewGroup。
view的话后面不要递归遍历,直接计算就行了。
如果是ViewGroup还是要层次遍历,确保ViewGroup的子view都被调用到。

FrameLayout对应的 onMeasure 方法如下:

@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    int count = getChildCount();

    for (int i = 0; i < count; i++) {
        final View child = getChildAt(i);
        if (mMeasureAllChildren || child.getVisibility() != GONE) {
            //分析1 : 遍历所有子控件,测量每个子控件的大小
                //参数1:View控件
                //参数2:宽MeasureSpec
                //参数3:父容器在宽度上已经用了多少了,因为FrameLayout的规则是:前面已经放置的View并不会影响后面放置View的宽高,是直接覆盖到上一个View上的.所以这里传0
                //参数4:高MeasureSpec
                //参数5:父容器在高度上已经用了多少了
            measureChildWithMargins(child, widthMeasureSpec, 0, heightMeasureSpec, 0);
        }
    }
    ......

    //分析2 : 测量完所有的子控件的大小之后,才知道自己的大小  这很符合FrameLayout的规则嘛
    setMeasuredDimension(resolveSizeAndState(maxWidth, widthMeasureSpec, childState),
            resolveSizeAndState(maxHeight, heightMeasureSpec,
                    childState << MEASURED_HEIGHT_STATE_SHIFT));
    ......
}

ViewGroup类中 measureChildWithMargins 方法实现:

    /**
     * Ask one of the children of this view to measure itself, taking into
     * account both the MeasureSpec requirements for this view and its padding
     * and margins. The child must have MarginLayoutParams The heavy lifting is
     * done in getChildMeasureSpec.
     *
     * @param child The child to measure
     * @param parentWidthMeasureSpec The width requirements for this view
     * @param widthUsed Extra space that has been used up by the parent
     *        horizontally (possibly by other children of the parent)
     * @param parentHeightMeasureSpec The height requirements for this view
     * @param heightUsed Extra space that has been used up by the parent
     *        vertically (possibly by other children of the parent)
     */
    protected void measureChildWithMargins(View child,
            int parentWidthMeasureSpec, int widthUsed,
            int parentHeightMeasureSpec, int heightUsed) {
        final MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams();

        final int childWidthMeasureSpec = getChildMeasureSpec(parentWidthMeasureSpec,
                mPaddingLeft + mPaddingRight + lp.leftMargin + lp.rightMargin
                        + widthUsed, lp.width);
        final int childHeightMeasureSpec = getChildMeasureSpec(parentHeightMeasureSpec,
                mPaddingTop + mPaddingBottom + lp.topMargin + lp.bottomMargin
                        + heightUsed, lp.height);

        child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
    }

View类中的setMeasuredDimension方法实现:

    /**
     * <p>This method must be called by {@link #onMeasure(int, int)} to store the
     * measured width and measured height. Failing to do so will trigger an
     * exception at measurement time.</p>
     *
     * @param measuredWidth The measured width of this view.  May be a complex
     * bit mask as defined by {@link #MEASURED_SIZE_MASK} and
     * {@link #MEASURED_STATE_TOO_SMALL}.
     * @param measuredHeight The measured height of this view.  May be a complex
     * bit mask as defined by {@link #MEASURED_SIZE_MASK} and
     * {@link #MEASURED_STATE_TOO_SMALL}.
     */
    protected final void setMeasuredDimension(int measuredWidth, int measuredHeight) {
        boolean optical = isLayoutModeOptical(this);
        if (optical != isLayoutModeOptical(mParent)) {
            Insets insets = getOpticalInsets();
            int opticalWidth  = insets.left + insets.right;
            int opticalHeight = insets.top  + insets.bottom;

            measuredWidth  += optical ? opticalWidth  : -opticalWidth;
            measuredHeight += optical ? opticalHeight : -opticalHeight;
        }
        setMeasuredDimensionRaw(measuredWidth, measuredHeight);
    }

4.具体计算子view的宽高。

就是根据父view的MesureMode和子view写的是固定值还是wrap_content或者match_parent来确定子view的mode和size。
具体方法是:

//这里来自ViewGroup的getChildMeasureSpec方法,无删减
public static int getChildMeasureSpec(int spec, int padding, int childDimension) {
    //根据父容器的MeasureSpec获取父容器的SpecMode和SpecSize
    int specMode = MeasureSpec.getMode(spec);
    int specSize = MeasureSpec.getSize(spec);
    
    //剩下的size
    int size = Math.max(0, specSize - padding);

    //最终的size和mode
    int resultSize = 0;
    int resultMode = 0;

    switch (specMode) {
    // Parent has imposed an exact size on us
    //父容器有一个确定的大小
    case MeasureSpec.EXACTLY:
        if (childDimension >= 0) {
            //子控件也是确定的大小,那么最终的大小就是子控件设置的大小,SpecMode为EXACTLY
            resultSize = childDimension;
            resultMode = MeasureSpec.EXACTLY;
        } else if (childDimension == LayoutParams.MATCH_PARENT) {
            // Child wants to be our size. So be it.
            // 子控件想要占满剩余的空间,那么就给它吧.
            resultSize = size;
            resultMode = MeasureSpec.EXACTLY;
        } else if (childDimension == LayoutParams.WRAP_CONTENT) {
            // Child wants to determine its own size. It can't be
            // bigger than us.
            //子控件想要自己定义大小,但是不能超过剩余空间 size
            resultSize = size;
            resultMode = MeasureSpec.AT_MOST;
        }
        break;

    // Parent has imposed a maximum size on us
    case MeasureSpec.AT_MOST:
        if (childDimension >= 0) {
            // Child wants a specific size... so be it
            resultSize = childDimension;
            resultMode = MeasureSpec.EXACTLY;
        } else if (childDimension == LayoutParams.MATCH_PARENT) {
            // Child wants to be our size, but our size is not fixed.
            // Constrain child to not be bigger than us.
            resultSize = size;
            resultMode = MeasureSpec.AT_MOST;
        } else if (childDimension == LayoutParams.WRAP_CONTENT) {
            // Child wants to determine its own size. It can't be
            // bigger than us.
            resultSize = size;
            resultMode = MeasureSpec.AT_MOST;
        }
        break;

    // Parent asked to see how big we want to be
    case MeasureSpec.UNSPECIFIED:
        if (childDimension >= 0) {
            // Child wants a specific size... let him have it
            resultSize = childDimension;
            resultMode = MeasureSpec.EXACTLY;
        } else if (childDimension == LayoutParams.MATCH_PARENT) {
            // Child wants to be our size... find out how big it should
            // be
            resultSize = View.sUseZeroUnspecifiedMeasureSpec ? 0 : size;
            resultMode = MeasureSpec.UNSPECIFIED;
        } else if (childDimension == LayoutParams.WRAP_CONTENT) {
            // Child wants to determine its own size.... find out how
            // big it should be
            resultSize = View.sUseZeroUnspecifiedMeasureSpec ? 0 : size;
            resultMode = MeasureSpec.UNSPECIFIED;
        }
        break;
    }
    //noinspection ResourceType
    return MeasureSpec.makeMeasureSpec(resultSize, resultMode);
}

MeasureSpec

一个view的MeasureMode怎么确定

  1. 如果该view的宽高是固定值,那么其mode是EXACTLY
  2. 如果view的宽高是MATCH_PARENT,那就继承父view的mode。
    即父view的mode是EXCETLY,该view就是EXACTLY;
    父View是AT_MOST,该view就是AT_MOST;
    父View是UNSPECIFIED,该view也是 UNSPECIFIED。
  3. 如果view的宽高是WRAP_CONTENT。那就不论父view是什么mode,该view都是AT_MOST除了UNSPECIFIED。

Measure流程图

image

2.布局

思考一个问题,影响布局有哪些因素:

  1. 首先能想到的是上个步骤中测量的宽高
  2. 其次就是Gravity,不同的ViewGroup不一样。比如RelativeLayout和LinearLayout差别就很大。需要具体分析。layout的时候也要考虑。
  • Gravity.BOTTOM
  • Gravity.TOP
  • Gravity.CENTER_VERTICAL
  • Gravity.CENTER_HORIZONTAL
  • Gravity.RIGHT
  • Gravity.LEFT
  1. 就是layout时自定义的几个参数left,top,right,bottom

布局首先是确定四个参数:
left,top,right,bottom
什么意思呢:
这四个参数位置都是相对于父容器而言的

那么具体是怎么操作呢:

  1. 首先是从根节点也就是DecorView开始。
    根节点的四个参数如何确定,很简单,如下所示。
//这里的host其实是根视图(DecorView)
//参数:left,top,right,bottom  这些位置都是相对于父容器而言的
host.layout(0, 0, host.getMeasuredWidth(), host.getMeasuredHeight());

  1. 根节点之后就开始层次遍历。
    这里子view其实分为view和ViewGroup。view的话后面不要递归遍历,直接计算就行了。如果是ViewGroup还是要层次遍历,确保ViewGroup的子view都被调用到。

拿到当前view的子view的count,for循环拿到每个子view。根据子view的测量出来的宽高,以及Gravity等参数。去计算子view的四个参数。
就是该子view相对于父view的布局是什么。
然后再去递归调用子view的布局方法即layout方法。实现整个递归遍历。

3.绘制

绘制又分为两种:
如果开启并支持硬件绘制加速(从 Android 4.X 开始谷歌已经默认开启硬件加速),则走 GPU 硬件绘制的流程,否则走 CPU 软件绘制的流程。

先看一下官方给的注释:

/*
        注意了这是官方给的注释,谷歌工程师还真是贴心,把draw步骤写的详详细细,给力,点赞
     * 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)
     */

翻译一下就是:

  1. 绘制背景
  2. 绘制控件自己本身的内容
  3. 绘制子控件
  4. 绘制装饰(比如滚动条)和前景

整体流程比较简单:
还是从ViewRootImpl的performTraversals方法开始分析

private void performTraversals() {
    //开始绘画流程
    performDraw();
}

private void performDraw() {
    ......
    draw(fullRedrawNeeded);
    ......
}

private void draw(boolean fullRedrawNeeded){
    .....
    drawSoftware(surface, mAttachInfo, xOffset, yOffset, scalingRequired, dirty);
    .....
}

private boolean drawSoftware(Surface surface, AttachInfo attachInfo, int xoff, int yoff,
        boolean scalingRequired, Rect dirty) {
    ......
    mView.draw(canvas);
    ......
}


随着方法的调用深入,发现来到了View的draw方法

public void draw(Canvas canvas) {
    .....

    /*
        注意了这是官方给的注释,谷歌工程师还真是贴心,把draw步骤写的详详细细,给力,点赞
     * 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
    //1. 绘制背景
    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
        //3. 绘制自己的内容
        if (!dirtyOpaque) onDraw(canvas);

        // Step 4, draw the children
        //4. 绘制子控件  如果是View的话这个方法是空实现,如果是ViewGroup则绘制子控件
        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)
        //6. 绘制装饰和前景
        onDrawForeground(canvas);

        // Step 7, draw the default focus highlight
        //7. 绘制默认焦点高亮显示
        drawDefaultFocusHighlight(canvas);

        if (debugDraw()) {
            debugDrawFocus(canvas);
        }

        // we're done...
        return;
    }
    .....
}

两个比较容易混淆的方法

invalidate方法:

如果这时候视图大小没有发生变化,不会调用layout放置过程

requestLayout 方法:

当布局发生变化的时候,比如方向、尺寸变化;
比如在某些情况下需要重新测量大小,需要手动调用这个方法,而调用完这个方法后,就会去触发它的mesure和layout过程,但是不会调用draw方法。

总结

绘制完毕之后干嘛呢。会交个RenderThread处理,然后交给SurfaceFlinger,最后显示到屏幕上。

更多Android基础资料可以扫码免费领取!

Android 技术栈从入门到熟悉

![img](https://img-blog.csdnimg.cn/img_convert/c86e774b7bde17c3e71cd864f9db47aa.png)

Android Jetpack 全家桶全面学习

img

学习Java期间使用的一般都是Eclipse。

但对于Android来说,建议使用 Android Studio 而不是Eclipse ADT。

对于新手来说可能安装Android Studio存在一定困难你可以看着以下视频,一步步的跟着学习安装运行

Android Studio 安装教程

img

有了Java阶段的学习,这一阶段建议以视频学习为主辅以图书查漏补缺。如果以图书为主,可以根据图书讲解敲代码,辅以教学视频查漏补缺。遇到问题可以去百度,入门的问题一般会有很多人遇到,并且给出比较好的解答。

需要掌握基本知识点,比如四大组件如何使用、如何创建Service、如何进行布局、简单的自定义View、动画、网络通信等常见技术。

全套零基础教程已经为你们准备好了,需要的可以添加下方二维码免费领取

Android中的View绘制流程 Android中的View绘制流程可以概括为三个主要阶段:测量(Measure)、布局(Layout)和绘制(Draw)。这三个阶段依次进行,共同决定了View的最终呈现效果。测量(Measure)测量阶段的主要任务是确定View的大小。Android系统会遍历View树,对每个View进行测量,以确定其宽度和高度。这个过程是通过调用View的measure()方法实现的。在测量过程中,View会根据其布局参数(如等)和父容器的约束条件来计算自己的测量宽度和高度。 阅读详情

相关推荐

View和ViewGroup,View绘制流程解析

View是绘制在屏幕上的用户能与之交互的一个对象,它代表了用户界面组件的一块可绘制的空间块,每一个空间块在屏幕上占据一个长方 形区域。View 的绘制流程是从 ViewRootImpl 的 performTraversals() 方法开始的,它经过测量(measure)、放置(layout)和绘制(draw)三个过程才能把一个 View 绘制出来,measure() 方法用于测量 View 的宽高,layout() 用于确定 View 在父容器中的放置位置,draw() 负责做具体的绘制操作。

qq_45730283的博客 1404

深入理解Android View的绘制原理

布局是绘制的前提,因为只有确定了View的位置,才能进行后续的绘制操作。这个过程主要是通过layout()方法实现的,该方法确定了View的四个边(左、上、右、下)距离父View的距离。在安卓开发中,View的绘制过程是一个非常重要的概念,它涉及到屏幕渲染和用户界面的展示。因此,在布局阶段,系统会考虑父容器的约束条件来确定每个子View的布局。父View会根据子View的测量结果和自身的布局参数,来确定子View的位置。因此,在设计布局时,应尽量减少不必要的嵌套层级,使用简单的布局结构,以提高布局性能。

weixin_42487687的博客 2036

四、界面编程(一) View的基础知识及架构详解

View虽然不属于四大组件,但是它的作用堪比四大组件,甚至比Receiver和Provider的重要性都更大。本文将介绍View的基础知识、常用概念、测量绘制简介等内容,通过这些内容的理解,可以更深入的了解View,也为更复杂的内容做下铺垫。

KevinWXT的博客 2901

View的绘制原理

1.初识ViewRoot和DecorViewViewRoot对应于ViewRootImpl类,它是连接WindowManager和DecorView的纽带,View的三大流程均是通过ViewRoot来完成的,在ActivityThread中,当ActivityThread中,当Activity对象被创建完毕后,会将DecorView添加到Window中,同时会创建ViewRootImpl对象,并将V

betterAndroider的博客 7946

Android自定义View开发(一):基础绘制原理

init();@Override// 绘制背景圆环// 绘制进度实现特殊的UI效果提供更好的交互体验优化性能提供可复用的组件View的工作原理Canvas的使用方法Paint的详细特性实战案例分析性能优化技巧调试方法通过本文的学习,你应该能够掌握自定义View开发的基础知识,并能够开发简单的自定义View组件。在下一篇文章中,我们将深入探讨事件分发机制。如果你对本文有任何疑问或建议,欢迎在评论区留言交流。

Ya-Jun 的博客 115

手把手教你用Python分析OpenFAST仿真结果:pyDatView处理.out文件与数据可视化技巧

本文详细介绍了如何使用Python中的pyDatView工具分析OpenFAST仿真结果,包括数据加载、质量检查、时域与频域分析、多工况对比及自动化报告生成。通过优化技巧和可视化方法,帮助工程师高效处理.out文件,提升风机性能分析的准确性和效率。

weixin_29311017的博客 183

Android基础-View测量、布局及绘制原理

一、View绘制的流程框架 View的绘制是从上往下一层层迭代下来的。DecorView-->ViewGroup(--->ViewGroup)-->View ,按照这个流程从上往下,依次measure(测量),layout(布局),draw(绘制) 二、Measure流程 顾名思义,就是测量每个控件的大小。 调用measure()方法,进行一...

weixin_30800807的博客 134

Intel网卡固件烧录实战:eeupdate与bootutil命令完全解析

固件作为硬件设备底层运行的软件,其稳定性直接决定硬件功能的完整性。网卡固件一旦损坏,常表现为MAC地址丢失、PXE启动失效、NVM数据异常导致降速或掉线。维护这类问题,需借助Intel官方工具eeupdate与bootutil在UEFI Shell或DOS环境下执行底层烧录操作。eeupdate负责网卡的身份信息与NVM存储管理,可重置MAC、恢复固件镜像;bootutil则专管启动行为,支持PXE配置与Boot Agent升级。从环境准备、命令参数到故障恢复,掌握这两款工具能让运维人员在修复存量网卡、应对

weixin_34269583的博客 312

一 UI 绘制流程及原理(Android UI )

Android UI View视图结构 View是如何被添加到屏幕窗口上? 创建顶层布局DecorView 在顶层布局中加载加载基础布局ViewGroup 将我们设置的setContentView(R.id.layoutId)中R.id.layoutId布局添加到基础布局ViewGroup当中 ...

初心一点的专栏 592

嵌入式存储入门:Emmc控制器与设备工作原理全解析(附Synopsys/Arasan架构图)

本文深入解析了嵌入式系统中Emmc控制器与设备的工作原理,重点剖析了其作为CPU与存储设备桥梁的核心架构。通过对比Synopsys和Arasan两家主流IP厂商的控制器设计,详细阐述了DMA引擎与FIFO缓冲模块如何协同工作以提升数据传输效率,并提供了从硬件PCB布局到驱动调试的实战指南,帮助硬件工程师与开发者优化系统性能。

weixin_29248653的博客 146

【openGauss实战1】openGauss基于CentOS8的部署

基于openGauss数据库在CentOS8上的安装实践。

IT邦德 3050

2017挖财安卓校招笔试复盘:从Java基础到Binder与View绘制

移动开发校招笔试早已从概念背诵转向原理考察,而Java基础与Android核心机制正是两道分水岭。理解集合框架的底层差异、并发控制的可见性与原子性,是排查线上异常的前提;掌握Activity生命周期组合场景、事件分发链路则能应对复杂UI交互。更深一层,Binder的一次拷贝原理决定了跨进程通信效率,View绘制中的MeasureSpec传递则直接影响自定义控件表现。这些技术价值在实际工程中体现为卡顿优化、内存泄漏治理与滑动冲突解决。无论是准备Android面试,还是复盘真题,围绕这些高频考点构建知识体系,都

weixin_34293911的博客 373

openpi 入门教程(一)安装、训练、微调与推理

OpenPI是由物理智能团队发布的机器人开源模型和软件包,包含两种主要模型:基于流的扩散视觉-语言-动作模型(VLA)的π₀模型和基于FAST动作标记器的自回归VLA的π₀-FAST模型。这些模型已在超过10,000小时的机器人数据上进行了预训练,并提供了基础模型检查点,支持开箱即用或根据用户数据集进行微调。OpenPI的实验性质意味着它可能不适用于所有平台,但鼓励研究人员和从业者进行创造性实验。运行这些模型需要至少具备NVIDIA GPU,推荐使用RTX 4090或更高规格的显卡。OpenPI支持在Ubu

2506_90492529的博客 5107

小米安卓开发笔试题B卷解析:Java基础、Handler与性能优化

在Android应用开发中,掌握系统底层原理与Java并发机制是构建稳定应用的基础。文章围绕Android核心组件生命周期、Handler消息循环、View绘制与内存优化等关键技术,解析小米2019秋招安卓开发笔试题B卷的考点与答题思路。通过分析Activity启动模式、线程通信、HTTP缓存等常见面试问题,展示如何将原理知识落地到实际工程排查中。同时强调手写单例、图片加载库设计等编程题中的工程边界意识,帮助开发者建立系统化的复习框架。适用于校招备考与社招基础巩固。

weixin_34323858的博客 414

导数与微分在 FPS 射击游戏中的实际应用:把“手感”算出来

摘要:射击游戏开发中,单线程适用于简单小游戏(如单机打靶),所有任务由主线程顺序处理,代码简单但效率较低;多线程则适合复杂场景(如多人在线3D大作),通过渲染、逻辑、网络、音效等线程分工协作,确保画面流畅、响应迅速和实时同步。选择依据取决于游戏复杂度、联网需求和性能要求,小型游戏用单线程,大型高要求项目必须采用多线程架构。(149字)

本博客聚焦游戏开发技巧、创业实战心得及大学热门课程干货。这里既有技术深度,也有思维广度,无论你是开发者、创业者还是高校学子,都能在这里找到适合自己的成长之路! 3278

WinCC实现定时存储数据至SQLServer

环境 Win10 WinCC V7.5 SQL Server 2016 运行结果 流程 WinCC变量 全局VB脚本 (我设置的是每2秒存一次,因此周期性触发器设置1秒)

skr? 3988

揭开Android View的神秘面纱:深入探索工作原理

在 Android 开发中,自定义 View 是实现个性化界面和独特交互效果的重要手段。根据不同的需求和场景,自定义 View 主要分为以下几种常见类型,每种类型都有其独特的实现要点和挑战。继承 View 类是最基础的自定义 View 方式,适用于需要实现完全自定义的绘制效果,无法通过现有控件组合来实现的场景。在这种方式下,开发者需要重写 onDraw 方法,使用 Canvas 和 Paint 来绘制各种图形、文本和图像,以实现所需的视觉效果。

顾林海 3126

【专栏必读】王道考研408数据结构+算法设计与分析万字笔记使用说明及章节导航

各位老铁思维导图在我整理完笔记后会放上去的,我需要根据王道的思维导图重新再做一遍 文章目录一:有关注意事项二:关于专栏三:学习建议四:各专栏导航(各位老铁别催,质量优先)(1)第一章:绪论(2)第二章:线性表(3)第三章:栈和队列第四章:串第五章:二叉树第六章:图 首先感谢王道大大(手动比心),很用心在做了,大家可以看其中两节是否符合胃口 (王道408考研数据结构)第五章树-第三节1:二叉树遍历(先序、中序和后序) (王道408考研数据结构)第五章树-第四节2:平衡二叉树(AVL)及其旋转 一:有.

快乐江湖的博客 5万+

关于Android绘制这一遍就够了

在Android中,如果你想要进行自定义绘制,你需要继承View类,并重写onDraw()方法。在这个方法中,你可以获取Canvas对象,并通过这个对象调用Canvas的绘制方法来进行绘制。自定义View在Android中,如果你需要进行自定义绘制,你可以通过继承View类并重写onDraw()方法来实现。在onDraw()方法中,你可以获取Canvas对象,并通过这个对象调用Canvas的绘制方法来进行绘制。此外,你还可以通过或。

VitorLiu的博客 1399

Camera2Demo

在Camera2 的基础上进行封装,使得Camera2一堆初始化逻辑与Activity分离,降低耦合度,完成预览拍照并保存的操作

SAP B1中文培訓教材_SDK

公司内部培训资料,对开发工具 ,DIAPI, UIAPI,Add-ON有详细解说

了解view的绘制流程和基础概念

本文大纲: android中view的加载绘制流程 自定义view中的基础概念 父View和子View的联系 1.android中view的加载绘制流程 1.1 andriod视图结构: 说明: 上图给出了android一个Activity中的视图结构,从外向内依次是 phoneWindow-->DecorView-->ActionBar+Conten...

qq_23992393的博客 281

Devexpress16.2.6破解工具

Devexpress16.2.6破解工具Devexpress16.2.6破解工具Devexpress16.2.6破解工具Devexpress16.2.6破解工具

小米2019秋招安卓开发笔试题全解析:从基础到实战

Android开发不仅要求掌握四大组件、Handler消息机制等核心原理,还需要理解并发编程、自定义View与性能优化等工程实践。这些基础能力直接决定了应用的质量与稳定性,也是大厂校招笔试中筛选人才的关键依据。从HashMap底层实现到Synchronized并发控制,从Activity生命周期到屏幕适配方案,扎实的基本功往往比项目经验更能体现候选人的技术深度。在实际开发中,无论是处理复杂的UI层级,还是解决地图遮挡等适配问题,都离不开对这些底层机制的理解。以小米2019秋招安卓开发笔试题为蓝本,系统复盘了

weixin_34356555的博客 372

假人有限元模型

假人模型,车辆碰撞假人有限元模型,适合做乘员安全的研究者利用

View的学习记录(一)

View的学习记录本篇目的View的调用View视图树Activity中视图的结构View的测量onMeasure()MeasurSpec类重写onMeasure()View的绘制ViewGrouop的测量和绘制测量放置绘制 本篇目的 探讨View的绘制过程以及分析View在绘制过程中我们可以参与改造的地方,为自定义View做基础 View的调用 View视图树 ViewParentViewGro...

RungBy的博客 535

Android UI绘制流程解析

Android UI绘制流程解析

江湖人陆仁甲 794

自定义View

自定义View基础篇安卓自定义View基础 - 坐标系安卓自定义View基础 - 角度弧度安卓自定义View基础 - 颜色进阶篇安卓自定义View进阶 - 分类和流程安卓自定义View进阶 - 绘制基本图形安卓自定义View进阶 - 画布操作安卓自定义View进阶 - 图片文字安卓自定义View进阶 - Path基本操作安卓自定义View进阶 - 贝塞尔曲线安卓自定义View进阶 - Path完结...

baidu_32472003的博客 1336

android View绘制流程

view的绘制 是学习自定义View的第一步,一直感觉挺难的,其实确实挺难的 哈哈哈哈,打算将自己学习到的写下来,记录一下,以后也好查看和回忆 ,虽说是原创,但是自己也是通过别人的博客或者书籍学习到的,所以有冒犯的地方,可以不留情面的告诉我,View的绘制呢分三步 Measure(测量view的宽高)Layout(确定View的位置) Draw(绘制View)还有一点,android的view分为...

jiaxiangdong1029的博客 243

揭秘 Android View 的 TranslationY 位移原理:源码深度剖析

在 Android 系统中,View是所有可视化组件的基类。它是屏幕上的一个矩形区域,负责绘制自己的内容并处理用户的交互事件。View可以是简单的文本框、按钮,也可以是复杂的自定义视图。通过组合和嵌套不同的View,可以构建出各种各样的用户界面。属性是View类中的一个浮点型属性,用于表示View在垂直方向上相对于其原始位置的位移量。默认情况下,的值为 0,表示View没有发生垂直位移。通过改变的值,可以让View在垂直方向上向上或向下移动。// 获取 View 的 TranslationY 属性值。

Android开发领域创作者 1140
上一篇: 程序员就业难,为什么还有这么多人前赴后继
下一篇: Android 图像显示系统 - 开篇
网络安全-XG
博客等级 码龄5年 1万+粉丝 2752原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值