当前位置: 首页 > 编程日记 > 正文

Android-----View绘制流程以及invalidate()等相关方法分析 .

引自:http://blog.csdn.net/qinjuning/article/details/7110211

前言: 本文是我读《Android内核剖析》第13章----View工作原理总结而成的,在此膜拜下作者 。同时真挚地向渴望了解

     Android 框架层网友,推荐这本书,希望你们能够在Android开发里学到更多的知识 。 

 

         

            整个View树的绘图流程是在ViewRoot.java类的performTraversals()函数展开的,该函数做的执行过程可简单概况为

 根之前状态,判断是否需要重新计算视图大小(measure)、是否重新需要安置视图的位置(layout)、以及是否需要重绘

 (draw),其框架过程如下:

                                                                                                   步骤其实为host.layout() 

           

 

 

      接下来温习一下整个View树的结构,对每个具体View对象的操作,其实就是个递归的实现。

 

   关于这个 DecorView 根视图的说明,可以参考我的这篇博客:

         《Android中将布局文件/View添加至窗口过程分析 ---- 从setContentView()谈起》

  流程一:      mesarue()过程

 

        主要作用:为整个View树计算实际的大小,即设置实际的高(对应属性:mMeasuredHeight)和宽(对应属性:

  mMeasureWidth),每个View的控件的实际宽高都是由父视图和本身视图决定的。

 

     具体的调用链如下:

          ViewRoot根对象地属性mView(其类型一般为ViewGroup类型)调用measure()方法去计算View树的大小,回调

View/ViewGroup对象的onMeasure()方法,该方法实现的功能如下:    

         1、设置本View视图的最终大小,该功能的实现通过调用setMeasuredDimension()方法去设置实际的高(对应属性:  

                mMeasuredHeight)和宽(对应属性:mMeasureWidth)   ;

         2 、如果该View对象是个ViewGroup类型,需要重写该onMeasure()方法,对其子视图进行遍历的measure()过程。

              

               2.1  对每个子视图的measure()过程,是通过调用父类ViewGroup.java类里的measureChildWithMargins()方法去

          实现,该方法内部只是简单地调用了View对象的measure()方法。(由于measureChildWithMargins()方法只是一个过渡

          层更简单的做法是直接调用View对象的measure()方法)。

              

     整个measure调用流程就是个树形的递归过程

 

   measure函数原型为 View.java 该函数不能被重载

    public final void measure(int widthMeasureSpec, int heightMeasureSpec) {//....//回调onMeasure()方法  
        onMeasure(widthMeasureSpec, heightMeasureSpec);//more}

为了大家更好的理解,采用“二B程序员”的方式利用伪代码描述该measure流程

   //回调View视图里的onMeasure过程private void onMeasure(int height , int width){//设置该view的实际宽(mMeasuredWidth)高(mMeasuredHeight)//1、该方法必须在onMeasure调用,否者报异常。
       setMeasuredDimension(h , l) ;//2、如果该View是ViewGroup类型,则对它的每个子View进行measure()过程int childCount = getChildCount() ;for(int i=0 ;i<childCount ;i++){//2.1、获得每个子View对象引用View child = getChildAt(i) ;//整个measure()过程就是个递归过程//该方法只是一个过滤器,最后会调用measure()过程 ;或者 measureChild(child , h, i)方法都
           measureChildWithMargins(child , h, i) ; //其实,对于我们自己写的应用来说,最好的办法是去掉框架里的该方法,直接调用view.measure(),如下://child.measure(h, l)
       }}//该方法具体实现在ViewGroup.java里 。protected  void measureChildWithMargins(View v, int height , int width){v.measure(h,l)   }

流程二、 layout布局过程:

 

     主要作用 :为将整个根据子视图的大小以及布局参数将View树放到合适的位置上。

 

     具体的调用链如下:

       host.layout()开始View树的布局,继而回调给View/ViewGroup类中的layout()方法。具体流程如下

  

        1 、layout方法会设置该View视图位于父视图的坐标轴,即mLeft,mTop,mLeft,mBottom(调用setFrame()函数去实现)

  接下来回调onLayout()方法(如果该View是ViewGroup对象,需要实现该方法,对每个子视图进行布局) ;

       

       2、如果该View是个ViewGroup类型,需要遍历每个子视图chiildView,调用该子视图的layout()方法去设置它的坐标值。

layout函数原型为 ,位于View.java

   /* final 标识符 , 不能被重载 , 参数为每个视图位于父视图的坐标轴* @param l Left position, relative to parent* @param t Top position, relative to parent* @param r Right position, relative to parent* @param b Bottom position, relative to parent*/public final void layout(int l, int t, int r, int b) {boolean changed = setFrame(l, t, r, b); //设置每个视图位于父视图的坐标轴if (changed || (mPrivateFlags & LAYOUT_REQUIRED) == LAYOUT_REQUIRED) {if (ViewDebug.TRACE_HIERARCHY) {ViewDebug.trace(this, ViewDebug.HierarchyTraceType.ON_LAYOUT);}onLayout(changed, l, t, r, b);//回调onLayout函数 ,设置每个子视图的布局mPrivateFlags &= ~LAYOUT_REQUIRED;}mPrivateFlags &= ~FORCE_LAYOUT;}

 同样地, 将上面layout调用流程,用伪代码描述如下: 

   // layout()过程  ViewRoot.java// 发起layout()的"发号者"在ViewRoot.java里的performTraversals()方法, mView.layout()private void  performTraversals(){//...
       View mView  ;mView.layout(left,top,right,bottom) ;//....
   }//回调View视图里的onLayout过程 ,该方法只由ViewGroup类型实现private void onLayout(int left , int top , right , bottom){//如果该View不是ViewGroup类型//调用setFrame()方法设置该控件的在父视图上的坐标轴
       setFrame(l ,t , r ,b) ;//--------------------------//如果该View是ViewGroup类型,则对它的每个子View进行layout()过程int childCount = getChildCount() ;for(int i=0 ;i<childCount ;i++){//2.1、获得每个子View对象引用View child = getChildAt(i) ;//整个layout()过程就是个递归过程
           child.layout(l, t, r, b) ;}}

流程三、 draw()绘图过程

     由ViewRoot对象的performTraversals()方法调用draw()方法发起绘制该View树,值得注意的是每次发起绘图时,并不

  会重新绘制每个View树的视图,而只会重新绘制那些“需要重绘”的视图,View类内部变量包含了一个标志位DRAWN,当该

视图需要重绘时,就会为该View添加该标志位。

 

   调用流程 :

     mView.draw()开始绘制,draw()方法实现的功能如下:

          1 、绘制该View的背景

          2 、为显示渐变框做一些准备操作(见5,大多数情况下,不需要改渐变框)          

          3、调用onDraw()方法绘制视图本身   (每个View都需要重载该方法,ViewGroup不需要实现该方法)

          4、调用dispatchDraw ()方法绘制子视图(如果该View类型不为ViewGroup,即不包含子视图,不需要重载该方法)

值得说明的是,ViewGroup类已经为我们重写了dispatchDraw ()的功能实现,应用程序一般不需要重写该方法,但可以重载父类

  函数实现具体的功能。

 

            4.1 dispatchDraw()方法内部会遍历每个子视图,调用drawChild()去重新回调每个子视图的draw()方法(注意,这个 

地方“需要重绘”的视图才会调用draw()方法)。值得说明的是,ViewGroup类已经为我们重写了dispatchDraw()的功能

实现,应用程序一般不需要重写该方法,但可以重载父类函数实现具体的功能。

    

     5、绘制滚动条

 

  于是,整个调用链就这样递归下去了。

    

     同样地,使用伪代码描述如下:

   // draw()过程     ViewRoot.java// 发起draw()的"发号者"在ViewRoot.java里的performTraversals()方法, 该方法会继续调用draw()方法开始绘图private void  draw(){//...
       View mView  ;mView.draw(canvas) ;  //....
   }//回调View视图里的onLayout过程 ,该方法只由ViewGroup类型实现private void draw(Canvas canvas){//该方法会做如下事情//1 、绘制该View的背景//2、为绘制渐变框做一些准备操作//3、调用onDraw()方法绘制视图本身//4、调用dispatchDraw()方法绘制每个子视图,dispatchDraw()已经在Android框架中实现了,在ViewGroup方法中。// 应用程序程序一般不需要重写该方法,但可以捕获该方法的发生,做一些特别的事情。//5、绘制渐变框    
   }//ViewGroup.java中的dispatchDraw()方法,应用程序一般不需要重写该方法
   @Overrideprotected void dispatchDraw(Canvas canvas) {// //其实现方法类似如下:int childCount = getChildCount() ;for(int i=0 ;i<childCount ;i++){View child = getChildAt(i) ;//调用drawChild完成
           drawChild(child,canvas) ;}       }//ViewGroup.java中的dispatchDraw()方法,应用程序一般不需要重写该方法protected void drawChild(View child,Canvas canvas) {// ....//简单的回调View对象的draw()方法,递归就这么产生了。
       child.draw(canvas) ;//.........}

关于绘制背景图片详细的过程,请参考我的另外的博客:

           

              <<Android中View(视图)绘制不同状态背景图片原理深入分析以及StateListDrawable使用详解>>

 

    强调一点的就是,在这三个流程中,Google已经帮我们把draw()过程框架已经写好了,自定义的ViewGroup只需要实现

 measure()过程和layout()过程即可 。

 

     这三种情况,最终会直接或间接调用到三个函数,分别为invalidate(),requsetLaytout()以及requestFocus() ,接着

这三个函数最终会调用到ViewRoot中的schedulTraversale()方法,该函数然后发起一个异步消息,消息处理中调用

performTraverser()方法对整个View进行遍历。

 

 

    invalidate()方法 :

 

   说明:请求重绘View树,即draw()过程,假如视图发生大小没有变化就不会调用layout()过程,并且只绘制那些“需要重绘的”

视图,即谁(View的话,只绘制该View ;ViewGroup,则绘制整个ViewGroup)请求invalidate()方法,就绘制该视图。

 

     一般引起invalidate()操作的函数如下:

            1、直接调用invalidate()方法,请求重新draw(),但只会绘制调用者本身。

            2、setSelection()方法 :请求重新draw(),但只会绘制调用者本身。

            3、setVisibility()方法 : 当View可视状态在INVISIBLE转换VISIBLE时,会间接调用invalidate()方法,

                     继而绘制该View。

            4 、setEnabled()方法 : 请求重新draw(),但不会重新绘制任何视图包括该调用者本身。

 

    requestLayout()方法 :会导致调用measure()过程 和 layout()过程 。

 

           说明:只是对View树重新布局layout过程包括measure()和layout()过程,不会调用draw()过程,但不会重新绘制

任何视图包括该调用者本身。

 

    一般引起invalidate()操作的函数如下:

         1、setVisibility()方法:

             当View的可视状态在INVISIBLE/ VISIBLE 转换为GONE状态时,会间接调用requestLayout() 和invalidate方法。

    同时,由于整个个View树大小发生了变化,会请求measure()过程以及draw()过程,同样地,只绘制需要“重新绘制”的视图。

 

    requestFocus()函数说明:

 

          说明:请求View树的draw()过程,但只绘制“需要重绘”的视图。

 

 

    下面写个简单的小Demo吧,主要目的是给大家演示绘图的过程以及每个流程里该做的一些功能。截图如下:

 

                                                

 

 

1、    MyViewGroup.java  自定义ViewGroup类型

   

    /*** @author http://http://blog.csdn.net/qinjuning*///自定义ViewGroup 对象public class MyViewGroup  extends ViewGroup{private static String TAG = "MyViewGroup" ;private Context mContext ;public MyViewGroup(Context context) {super(context);mContext = context ;init() ;}//xml定义的属性,需要该构造函数public MyViewGroup(Context context , AttributeSet attrs){super(context,attrs) ;mContext = context ;init() ;}//为MyViewGroup添加三个子Viewprivate void init(){//调用ViewGroup父类addView()方法添加子View//child 对象一 : ButtonButton btn= new Button(mContext) ;btn.setText("I am Button") ;this.addView(btn) ;//child 对象二 : ImageView ImageView img = new ImageView(mContext) ;img.setBackgroundResource(R.drawable.icon) ;this.addView(img) ;//child 对象三 : TextViewTextView txt = new TextView(mContext) ;txt.setText("Only Text") ;this.addView(txt) ; //child 对象四 : 自定义ViewMyView myView = new MyView(mContext) ;this.addView(myView) ; }@Override//对每个子View进行measure():设置每子View的大小,即实际宽和高protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec){//通过init()方法,我们为该ViewGroup对象添加了三个视图 , Button、 ImageView、TextViewint childCount = getChildCount() ;Log.i(TAG, "the size of this ViewGroup is ----> " + childCount) ;Log.i(TAG, "**** onMeasure start *****") ;//获取该ViewGroup的实际长和宽  涉及到MeasureSpec类的使用int specSize_Widht = MeasureSpec.getSize(widthMeasureSpec) ;int specSize_Heigth = MeasureSpec.getSize(heightMeasureSpec) ;Log.i(TAG, "**** specSize_Widht " + specSize_Widht+ " * specSize_Heigth   *****" + specSize_Heigth) ;//设置本ViewGroup的宽高
            setMeasuredDimension(specSize_Widht , specSize_Heigth) ;for(int i=0 ;i<childCount ; i++){View child = getChildAt(i) ;   //获得每个对象的引用child.measure(50, 50) ;   //简单的设置每个子View对象的宽高为 50px , 50px  //或者可以调用ViewGroup父类方法measureChild()或者measureChildWithMargins()方法//this.measureChild(child, widthMeasureSpec, heightMeasureSpec) ;
            }}@Override//对每个子View视图进行布局protected void onLayout(boolean changed, int l, int t, int r, int b) {// TODO Auto-generated method stub//通过init()方法,我们为该ViewGroup对象添加了三个视图 , Button、 ImageView、TextViewint childCount = getChildCount() ;int startLeft = 0 ;//设置每个子View的起始横坐标 int startTop = 10 ; //每个子View距离父视图的位置 , 简单设置为10px吧 。 可以理解为 android:margin=10px ;
            Log.i(TAG, "**** onLayout start ****") ;for(int i=0 ;i<childCount ; i++){View child = getChildAt(i) ;   //获得每个对象的引用child.layout(startLeft, startTop, startLeft+child.getMeasuredWidth(), startTop+child.getMeasuredHeight()) ;startLeft =startLeft+child.getMeasuredWidth() + 10;  //校准startLeft值,View之间的间距设为10px ;Log.i(TAG, "**** onLayout startLeft ****" +startLeft) ;}              }//绘图过程Android已经为我们封装好了 ,这儿只为了观察方法调用程protected void dispatchDraw(Canvas canvas){Log.i(TAG, "**** dispatchDraw start ****") ;super.dispatchDraw(canvas) ;}protected boolean drawChild(Canvas canvas , View child, long drawingTime){Log.i(TAG, "**** drawChild start ****") ;return super.drawChild(canvas, child, drawingTime) ;}}

2、MyView.java 自定义View类型,重写onDraw()方法 ,

//自定义View对象public class MyView extends View{private Paint paint  = new Paint() ;public MyView(Context context) {super(context);// TODO Auto-generated constructor stub
        }public MyView(Context context , AttributeSet attrs){super(context,attrs);}protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec){//设置该View大小为 80 80setMeasuredDimension(50 , 50) ;}//存在canvas对象,即存在默认的显示区域
        @Overridepublic void onDraw(Canvas canvas) {// TODO Auto-generated method stub
            super.onDraw(canvas);Log.i("MyViewGroup", "MyView is onDraw ") ;//加粗
            paint.setTypeface(Typeface.defaultFromStyle(Typeface.BOLD));paint.setColor(Color.RED);canvas.drawColor(Color.BLUE) ;canvas.drawRect(0, 0, 30, 30, paint);canvas.drawText("MyView", 10, 40, paint);}}

主Activity只是显示了该xml文件,在此也不罗嗦了。 大家可以查看该ViewGroup的Log仔细分析下View的绘制流程以及

相关方法的使用。第一次启动后捕获的Log如下,网上找了些资料,第一次View树绘制过程会走几遍,具体原因可能是某些

View 发生了改变,请求重新绘制,但这根本不影响我们的界面显示效果 。

 

        总的来说: 整个绘制过程还是十分十分复杂地,每个具体方法的实现都是我辈难以立即的,感到悲剧啊。对Android提

 供的一些ViewGroup对象,比如LinearLayout、RelativeLayout布局对象的实现也很有压力。 本文重在介绍整个View树的绘制

流程,希望大家在此基础上,多接触源代码进行更深入地扩展。

 

 

      

       示例DEMO下载地址:http://download.csdn.net/detail/qinjuning/3982468

 

//==========================================================

// 本次更新于 2012-05-20 晚

//==========================================================

 

 Al Last,关于UI绘制的这块,我博客里零零散散的叙说了一些知识,建议大家都能够去看看:

 

   1、  详解measure过程以及如何设置View宽高的,建议看我的另外两篇博客:

         <<Android中measure过程、WRAP_CONTENT详解以及xml布局文件解析流程浅析(上)>>

         <<Android中measure过程、WRAP_CONTENT详解以及xml布局文件解析流程浅析(下)>>

 

   2、详解DecorView以及Activity窗口对应布局地说明

      <<Android中将布局文件/View添加至窗口过程分析 ---- 从setContentView()谈起>>

 

   3、详解View绘制过程中如何绘制背景图片:

       <<Android中View(视图)绘制不同状态背景图片原理深入分析以及StateListDrawable使用详解>>

 

相关文章:

实现ASP.NET MVC3 HtmlHelper 的 RadioButtonList 与CheckBoxList 扩展

ASP.NET MVC3也出来有一段时间了&#xff0c;对于没有RadioButtonList 与CheckBoxList的问题&#xff0c;网上也已经有很多解决方案了&#xff0c;可以for循环拼接出来&#xff0c;也可以引用ASP.NET MVC Toolkit&#xff0c;等等方法。其实本没有必要写出来的&#xff0c;不过…

Blender从头到尾创建一个低多边形场景学习教程

Low Poly Landscapes – Blender Bite Sized Course 流派:电子学习| MP4 |视频:h264&#xff0c;1280720 |音频:AAC&#xff0c;48.0 KHz 语言&#xff1a;英语中英文字幕&#xff08;根据原英文字幕机译更准确&#xff09;|大小解压后:3.9 GB |时长:6h 0m 从头到尾创造一个低聚…

SQL基础学习总结:6(INSERT语句的相关使用方法)

数据的插入(INSERT语句的使用方法) 我们之前在表的创建部分简单地介绍了一下INSERT语句的功能和使用方法&#xff0c;现在我们再详细讲一下它的用法。 INSERT语句的基本语法 其语法结构如下&#xff1a; INSERT INTO <表名> (列名1,列名2,列名3...)-> VALUES (数据…

JVM安全退出(如何优雅的关闭java服务)

为了保障应用重启过程中异步操作的执行,避免强制退出JVM可能产生的各种问题,我们可以采用关闭钩子、自定义信号的方式,主动的通知JVM退出,并在JVM关闭前,执行应用程序的一些扫尾工作,进一步保证应用程序可以安全的退出。

常用的CSS(收集)

1. 防padding属性改变盒子模型宽度 Css代码 * { -webkit-box-sizing: border-box; -moz-box-sizing: border-box; box-sizing: border-box; } 当前css将固定盒子模型宽度总是以(显示)指定的宽度(width:300px)为准, 并不会受到padding而强制改变和模型宽…

2022-2028年中国EMI膜产业发展态势及市场发展策略报告

【报告类型】产业研究 【报告价格】4500起 【出版时间】即时更新&#xff08;交付时间约3个工作日&#xff09; 【发布机构】智研瞻产业研究院 【报告格式】PDF版 本报告介绍了中国EMI膜行业市场行业相关概述、中国EMI膜行业市场行业运行环境、分析了中国EMI膜行业市场行业…

JS报“Uncaught TypeError: undefined is not a function ”异常

最终的原因是&#xff1a;js引用的位置太靠后&#xff0c;导致使用js方法时&#xff0c;js库还没加载。报此异常的可能原因有&#xff1a; 1、函数名写错了&#xff1b; 2、没有引用js文件&#xff1b; 3、定义晚了转载于:https://www.cnblogs.com/finalstar/p/4254632.html

密码学摘要算法之MD5

摘要算法 摘要算法是一种能产生特殊输出格式的算法&#xff0c;这种算法的特点是:无论用户输入什么长度的原始数据&#xff0c;经过计算后输出的密文都是固定长度的&#xff0c;这种算法的原理是根据一定的运算规则对原数据进行某种形式的提取&#xff0c;这种提取就是摘要&…

Revit:从入门到精通学习教程

流派:电子学习| MP4 |视频:h264&#xff0c;1280720 |音频:AAC&#xff0c;48.0 KHz 语言&#xff1a;英语中英文字幕&#xff08;根据原英文字幕机译更准确&#xff09; |大小:8.07 GB |时长:12h 16m Revit:从开始到专业掌握Revit 你会学到什么 Revit的最新功能 在Revit中创建…

JDBC编程:1(使用JDBC连接数据库)

使用JDBC连接数据库 下载连接MySQL数据库的驱动 这个jar包可以在官网上对照着你的MySQL版本来下载&#xff0c;这里我下载的是最新的8.0.20版本&#xff0c; 这里是8.0.20版本的驱动包&#xff1a;mysql-connector-java-8.0.20.zip 因为CSDN的积分不能设置永久免费&#xff0…

VS2010 编译 QT4.8.7 x64

1 下载qt4.8.7源代码&#xff0c;解压到合适位置(如本文为d:\qt\4.8.7) 2 设置环境变量&#xff1a; set QMAKESPECwin32-msvc2010set QTDIRd:\qt\4.8.7 3 修改配置文件&#xff1a; 修改\mkspecs\win32-msvc2010\qmake.conf将QMAKE_CFLAGS_RELEASE的O2改为O1 或 安装补丁http…

2022-2028年中国EBA树脂(乙烯丙烯酸丁酯)产业竞争现状及发展前景规划报告

【报告类型】产业研究 【报告价格】4500起 【出版时间】即时更新&#xff08;交付时间约3个工作日&#xff09; 【发布机构】智研瞻产业研究院 【报告格式】PDF版 本报告介绍了中国EBA树脂&#xff08;乙烯丙烯酸丁酯&#xff09;行业市场行业相关概述、中国EBA树脂&#…

JAX_WS 2.2 规范的webservices客户端实现(Axis2,Cxf)

为了对接之前老版本的接口,折腾了好几个小时. 主要是目前我的程序采用的是axis2的jax_rpc方式发布webservices服务,用这种服务的客户端,去调用老版本的jax_ws 2.2的接口,会报Runtime空指针。 于是采用cxf&#xff0c;使用了cxf3.0.3&#xff08;当前版本&#xff09; 的wsdl2ja…

idea mybatis plugin插件,免费mybatis插件

idea的mybatis插件。一直想下一个&#xff0c;在大批量修改一些问题时候 mapper和.xml文件查看会方便许多。 直接在idea的插件market里看经常会卡住&#xff0c;直接去网站看。 于是去官网查查看&#xff0c;网站巨慢 https://plugins.jetbrains.com/ 曾经试过mybatis plugin的…

从头开始学习Unity着色器

MP4 |视频:h264&#xff0c;1280720 |音频:AAC&#xff0c;44.1 KHz&#xff0c;2 Ch 语言&#xff1a;英语中英文字幕&#xff08;根据原英文字幕机译更准确&#xff09;|时长:56节课(4h 26m) |大小解压后:2.89 GB Unity Shaders和HLSL阴影语言的完整指南 你会学到: 编写Uni…

JDBC编程:2(数据库的基本操作)

数据库的基本操作 查询数据 在开始前先简单地介绍一下什么是静态SQL和动态SQL&#xff1a; 静态SQL&#xff0c;在编译阶段就可以确定数据库要做什么事情。在某种高级语言中&#xff0c;如果嵌入了SQL语句&#xff0c;而这个SQL语句的主体结构已经明确&#xff0c;例如在Jav…

[Asp.net 5] Options-配置文件(2)

很久之前写过一篇介绍Options的文章&#xff0c;2016年再打开发现很多变化。增加了新类&#xff0c;增加OptionMonitor相关的类。今天就对于这个现在所谓的新版本进行介绍。 老版本的传送门&#xff08;[Asp.net 5] Options-配置文件之后昂的配置&#xff09;。 首先上一个图&a…

android-sdk-windows版本号下载

Android SDK 4.0.3 开发环境配置及执行 近期又装了一次最新版本号的ADK环境 眼下最新版是Android SDK 4.0.3 本文的插图和文本尽管是Android2.2的 步骤都是一样的&#xff0c;假设安装的过程中遇到什么问题&#xff0c;能够留言&#xff0c;我会尽快回复&#xff01; 系统环境的…

vs code搭建Django环境

在网上找了很多博客&#xff0c;看了vs code的官方文档&#xff0c;最终拼凑起来&#xff0c;终于搭建起来了djangode开发虚拟环境&#xff08;win10下&#xff09; 一、新建项目文件夹 F:\Python\temp\django_demo&#xff08;例子&#xff09; 二、在项目文件夹创建虚拟pytho…

Marvelous Designer衣袖设计教程

大小解压后&#xff1a;2.96G 持续时间3h 28m 包含项目文件 1280X720 MP4 语言&#xff1a;英语中英文字幕&#xff08;根据原英文字幕机译更准确&#xff09; 标题:技能分享——卓越设计师大师班(袖子) 信息: 在我的课程中&#xff0c;精彩设计师大师班(袖子)。在本课程中&…

Mybatis入门:1(Mybatis框架的环境搭建)

Mybatis框架的环境搭建 一.创建maven工程并导入坐标 导入坐标&#xff1a; <dependencies><dependency><groupId>org.mybatis</groupId><artifactId>mybatis</artifactId><version>3.5.4</version></dependency><d…

html表单的创建和css的构成

产品参数 <h2>LIFAair LA500参数表</h2><table><tr><td>测试数目</td><td>单位</td><td>数据</td></tr><tr><td>颗粒物CADR</td><td>m<sup>3</sup>/h</td><t…

Gartner2014年魔力象限(商业智能和分析平台)

转载于:https://www.cnblogs.com/crsn/p/4271377.html

@SupperssWarnings注解

SupperssWarnings注解&#xff1a;压制、控制警告 SuppressWarnings(“rawtypes”) 是什么含义 SuppressWarnings压制警告&#xff0c;即去除警告 rawtypes是说传参时也要传递带泛型的参数 SuppressWarnings(“unchecked”) unchecked 执行了未检查的转换时的警告 SuppressWarn…

虚幻引擎C++编程游戏开发基础

流派:电子学习| MP4 |视频:h264&#xff0c;1280720 |音频:AAC&#xff0c;44.1 KHz 语言&#xff1a;英语中英文字幕&#xff08;根据原英文字幕机译更准确&#xff09;|大小解压后:23.8 GB |时长:44h 59m 学习C编程和游戏开发基础和虚幻引擎 你会学到什么 通过简单的例子和插…

Mybatis入门:3(动态sql)

动态sql语句 if标签 基本使用 一.在ProductDao接口中创建一个查询方法findByType import com.domain.Product;import java.util.List;public interface ProductDao {/*** 根据类型来查找* param product* return*/Product findByType(Product product); }二.在映射配置文件…

javaweb学习总结(二十三)——jsp自定义标签开发入门

一、自定义标签的作用 自定义标签主要用于移除Jsp页面中的java代码。 二、自定义标签开发和使用 2.1、自定义标签开发步骤 1、编写一个实现Tag接口的Java类(标签处理器类) 1 package me.gacl.web.tag;2 3 import java.io.IOException;4 5 import javax.servlet.http.HttpServle…

Java数组的三种声明方式

具体的细节大家可以不用先去了解,这涉及到很多知识,只要记住输出的时候,先导包,然后再利用Arrays.toString(arr)输出就行了。如:先定义好一个长度为4的新数组,此时数组为空,使用arr[ ]数组下标来进行逐个赋值。那我们定义好数组之后,就理所应当的对声明好的数组进行赋值。那么对于未涉及过编程的小伙伴,看到这可能会蒙了。原因就是我们sout(arr)时,输出的是这个数据的内存地址,而不是真实的数据。使用数组: 只需要一个变量,然后数组中存很多的数据, 其实可以把数组想成 一个容器。

Math: Math.atan() 与 Math.atan2() 计算两点间连线的夹角

Math.atan2()函数返回点(x,y)和原点(0,0)之间直线的倾斜角.那么如何计算任意两点间直线的倾斜角呢?只需要将两点x,y坐标分别相减得到一个新的点(x2-x1,y2-y1).然后利用他求出角度就可以了.使用下面的一个转换可以实现计算出两点间连线的夹角.然而,Math.atan()只能返回一个角度值,因此确定他的角度非常的复杂,而且,90度和270度的正切是无穷大,因为除数为零,我们也是比较难以处理的~!angel为一个角度的弧度值,slope为直线的斜率,是一个数字,这个数字可以是负的。

@RequiredArgsConstructor详解&@AllArgsConstructor和@RequiredArgsConstructor区别

RequiredArgsConstructor是Lombok的一个注解,简化了我们对@Autowired书写,我们在写Controller层或者Service层的时候,总是需要注入很多mapper接口或者service接口,如果每个接口都写上@Autowired,这样看起来就会很繁琐,@RequiredArgsConstructor注解可以代替@Autowired注解。