多次调用onMeasure onLayout

先看代码

public class MyView extends View {private static final String TAG = "MyView";public MyView(Context context) {super(context);init();}public MyView(Context context, AttributeSet attrs) {super(context, attrs);init();}public MyView(Context context, AttributeSet attrs, int defStyleAttr) {super(context, attrs, defStyleAttr);init();
}private void init() {
}@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {super.onMeasure(widthMeasureSpec, heightMeasureSpec);Log.d(TAG, "View onMeasure");
}@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {super.onLayout(changed, left, top, right, bottom);Log.d(TAG, "View onLayout");
}@Override
protected void onDraw(Canvas canvas) {super.onDraw(canvas);Log.d(TAG, "View onDraw");
}@Override
public void computeScroll() {super.computeScroll();Log.d(TAG, "view computeScroll");
}@Override
public void draw(Canvas canvas) {super.draw(canvas);
}}

布局文件

<?xml version="1.0" encoding="utf-8"?>
<com.example.jinxiong.blog.MyViewxmlns:android="http://schemas.android.com/apk/res/android"xmlns:tools="http://schemas.android.com/tools"android:id="@+id/activity_main"android:layout_width="match_parent"android:layout_height="match_parent"tools:context="com.example.jinxiong.blog.MainActivity">
</com.example.jinxiong.blog.MyView>

Log打印

09-30 10:01:29.454 27838-27838/com.example.jinxiong.blog D/MyView: View onMeasure
09-30 10:01:29.538 27838-27838/com.example.jinxiong.blog D/MyView: View onLayout
09-30 10:01:29.754 27838-27838/com.example.jinxiong.blog D/MyView: View onMeasure
09-30 10:01:29.754 27838-27838/com.example.jinxiong.blog D/MyView: View onLayout
09-30 10:01:29.754 27838-27838/com.example.jinxiong.blog D/MyView: view computeScroll
09-30 10:01:29.754 27838-27838/com.example.jinxiong.blog D/MyView: View onDraw

为什么会调用了两次的onMeasure onLayout方法尼?
官方文档:
A parent View may call measure() more than once on its children. For example, the parent may measure each child once with unspecified dimensions to find out how big they want to be, then call measure() on them again with actual numbers if the sum of all the children’s unconstrained sizes is too big or too small (that is, if the children don’t agree among themselves as to how much space they each get, the parent will intervene and set the rules on the second pass).
一个父View或许会多次调用子View的measure()方法。举个例子,父View会使用不明确的尺寸去丈量看看子View到底需要多大,当子View总的尺寸太大或者太小的时候会再次使用实际的尺寸去调用onmeasure()
网上也有很多这个问题的答案想法
http://stackoverflow.com/questions/21178959/why-android-onmeasure-be-called-twice-in-my-custom-view

https://github.com/android-cn/android-discuss/issues/386

关于自定义ViewGroup 没有调用onDraw

public class MyViewGroup extends LinearLayout {
private static final String TAG = "MyView";
public MyViewGroup(Context context) {super(context);
}public MyViewGroup(Context context, AttributeSet attrs) {super(context, attrs);
}public MyViewGroup(Context context, AttributeSet attrs, int defStyleAttr) {super(context, attrs, defStyleAttr);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {super.onMeasure(widthMeasureSpec, heightMeasureSpec);Log.d(TAG, "ViewGroup onMeasure");
}@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {super.onLayout(changed, left, top, right, bottom);Log.d(TAG, "ViewGroup onLayout");
}@Override
protected void onDraw(Canvas canvas) {super.onDraw(canvas);Log.d(TAG, "ViewGroup onDraw");
}@Override
protected void dispatchDraw(Canvas canvas) {super.dispatchDraw(canvas);Log.d(TAG, "viewGroup dispatchDraw");}
}

布局

<?xml version="1.0" encoding="utf-8"?>
<com.example.jinxiong.blog.MyViewGroupxmlns:android="http://schemas.android.com/apk/res/android"xmlns:tools="http://schemas.android.com/tools"android:id="@+id/activity_main"android:layout_width="match_parent"android:layout_height="match_parent"tools:context="com.example.jinxiong.blog.MainActivity">
</com.example.jinxiong.blog.MyViewGroup>

Log

09-30 10:16:45.474 9522-9522/com.example.jinxiong.blog D/MyView: ViewGroup onMeasure
09-30 10:16:45.538 9522-9522/com.example.jinxiong.blog D/MyView: ViewGroup onLayout
09-30 10:16:45.554 9522-9522/com.example.jinxiong.blog D/MyView: ViewGroup onMeasure
09-30 10:16:45.554 9522-9522/com.example.jinxiong.blog D/MyView: ViewGroup onLayout
09-30 10:16:45.554 9522-9522/com.example.jinxiong.blog D/MyView: viewGroup dispatchDraw

但是当我布局文件中使用①中的MyViewGroup的时候他就会调用onDraw,为什么?
其实在View中有这么一个方法

 /*** If this view doesn't do any drawing on its own, set this flag to* allow further optimizations. By default, this flag is not set on* View, but could be set on some View subclasses such as ViewGroup.** Typically, if you override {@link #onDraw(android.graphics.Canvas)}* you should clear this flag.** @param willNotDraw whether or not this View draw on its own*/public void setWillNotDraw(boolean willNotDraw) {setFlags(willNotDraw ? WILL_NOT_DRAW : 0, DRAW_MASK);
}

当我们继承自ViewGroup(这里LinearLayout)的时候,这个标志位一直是打开的,而对于View默认是false的,所以当我们在MyViewGroup中重新设置:

setWillNotDraw(false);

那么它的onDraw就会调用。至于为什么这样,是因为android优化,既然你这个ViewGroup没有什么东西draw,那么我为什么要调用这个方法尼,(我的布局文件没有设置background,也设置其他需要draw)。当然我们也可以在我们的MyView中手动设置setWillNotDraw(true); 那么当我们没有放置任何东西给这个view draw ,那么它的onDraw也不会被调用

View视图是没有边界的


可以看到我们可以在这个坐标系中画一条跨越234象限的直线,但是在手机的屏幕上我们只能看到第4象限的某一部分,手机屏幕的大小我们这里是A*B ,对于我们想要看到其余的地方,那么我们需要通过scrollTo/By方法来实现

<?xml version="1.0" encoding="utf-8"?>
<com.example.jinxiong.blog.MyViewxmlns:android="http://schemas.android.com/apk/res/android"xmlns:tools="http://schemas.android.com/tools"android:id="@+id/activity_main"android:layout_width="match_parent"android:layout_height="match_parent"tools:context="com.example.jinxiong.blog.MainActivity">
</com.example.jinxiong.blog.MyView>

MyView中onDraw方法

@Override
public void onDraw(Canvas canvas) {Paint paint = new Paint();paint.setColor(Color.GREEN);canvas.drawLine(-200, -200, 0, 0, paint);paint.setColor(Color.RED);canvas.drawLine(0, 0, 200, 200, paint);paint.setColor(Color.BLUE);canvas.drawLine(200, 200, 400, 400, paint);super.draw(canvas);
}

Activity

@Override
protected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);final View view = this.findViewById(R.id.activity_main);view.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {view.scrollBy(-200,-200);}});
}


点击之后

相当于这个样子:

    /**方法注释* Move the scrolled position of your view. This will cause a call to* {@link #onScrollChanged(int, int, int, int)} and the view will be* invalidated.* @param x the amount of pixels to scroll by horizontally* @param y the amount of pixels to scroll by vertically*/

其实这个方法就是使这个调用scrollBy/To的View 的内容滑动,其实怎么解释它的正负比较好理解,我的想法就是:

假设我们想看到“我想看到你”这个字符串,我们怎么滑动,我们把手指放在屏幕上,然后慢慢向上移动,就可以看到,那么我们可以记录下你down事件时候的坐标,然后在记录你move事件的坐标,用最后down坐标减去move坐标,那么是不是得到Y坐标是一个正数,就是view.scrollBy(0,Y)。然后我们想看到“手机的宽度为A”假设我们画了这个文字在canvas上,那么我们也是可以像上面那样,用上面的方法可以得知Y是个负数。你可以想象成这个view是可以上下滑动的,想listView那样。

手机硬件加速问题

MyViewGroup

public class MyViewGroup extends LinearLayout {
private static final String TAG = "MyView";
public MyViewGroup(Context context) {super(context);init();
}public MyViewGroup(Context context, AttributeSet attrs) {super(context, attrs);init();}public MyViewGroup(Context context, AttributeSet attrs, int defStyleAttr) {super(context, attrs, defStyleAttr);init();
}private void init() {setWillNotDraw(false);}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {super.onMeasure(widthMeasureSpec, heightMeasureSpec);Log.d(TAG, "ViewGroup onMeasure");
}@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {super.onLayout(changed, left, top, right, bottom);Log.d(TAG, "ViewGroup onLayout");
}@Override
protected void onDraw(Canvas canvas) {super.onDraw(canvas);Log.d(TAG, "ViewGroup onDraw");
}@Override
protected void dispatchDraw(Canvas canvas) {super.dispatchDraw(canvas);Log.d(TAG, "viewGroup dispatchDraw");
}
}

MyView

public class MyView extends View{private static final String TAG = "MyView";
public MyView(Context context) {super(context);init();}public MyView(Context context, AttributeSet attrs) {super(context, attrs);init();}public MyView(Context context, AttributeSet attrs, int defStyleAttr) {super(context, attrs, defStyleAttr);init();
}
private void init() {
//        setWillNotDraw(true);}@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {super.onMeasure(widthMeasureSpec, heightMeasureSpec);Log.d(TAG, "View onMeasure");
}@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {super.onLayout(changed, left, top, right, bottom);Log.d(TAG, "View onLayout");
}@Override
protected void onDraw(Canvas canvas) {super.onDraw(canvas);Log.d(TAG, "View onDraw");
}@Override
public void computeScroll() {super.computeScroll();Log.d(TAG, "view computeScroll");
}}

布局:

<?xml version="1.0" encoding="utf-8"?>
<com.example.jinxiong.blog.MyViewGroup
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/activity_main"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@android:color/holo_blue_bright"
tools:context="com.example.jinxiong.blog.MainActivity">
<com.example.jinxiong.blog.MyViewandroid:layout_width="match_parent"android:layout_height="match_parent"android:id="@+id/myView"android:background="@android:color/black"/></com.example.jinxiong.blog.MyViewGroup>

Activity

public class MainActivity extends AppCompatActivity {
private static final String TAG = "MyView";
@Override
protected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);final View viewGroup = this.findViewById(R.id.activity_main);final View view = this.findViewById(R.id.myView);viewGroup.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {viewGroup.scrollBy(0,100);if (viewGroup.getScrollY() > 1200) {viewGroup.scrollTo(0, 0);}}});
}
}

原始样子:

点击之后:

09-30 23:50:07.390 1465-1465/com.example.jinxiong.blog D/MyView: ViewGroup onDraw
09-30 23:50:07.390 1465-1465/com.example.jinxiong.blog D/MyView: viewGroup dispatchDraw
09-30 23:50:07.854 1465-1465/com.example.jinxiong.blog D/MyView: ViewGroup onDraw
09-30 23:50:07.854 1465-1465/com.example.jinxiong.blog D/MyView: viewGroup dispatchDraw
09-30 23:50:16.822 1465-1465/com.example.jinxiong.blog D/MyView: ViewGroup onDraw
09-30 23:50:16.822 1465-1465/com.example.jinxiong.blog D/MyView: viewGroup dispatchDraw

为什么我的MyView没有重绘尼?当我点击多次之后viewGroup.scrollTo(0,0) myViewde onDraw方法还是没有调用,者就有点不科学了,为什么?后来查了很久才发现原来有个硬件加速的东西,当我在我的manifest中添加了

android:hardwareAccelerated="false"

再次运行,每一次的点击

09-30 23:57:26.078 7783-7783/com.example.jinxiong.blog D/MyView: ViewGroup onDraw
09-30 23:57:26.078 7783-7783/com.example.jinxiong.blog D/MyView: view computeScroll
09-30 23:57:26.078 7783-7783/com.example.jinxiong.blog D/MyView: View onDraw
09-30 23:57:26.078 7783-7783/com.example.jinxiong.blog D/MyView: viewGroup dispatchDraw

ok了这样就,摘录自一篇文章:

Android的绘制模型
开启硬件加速后,Android框架将采用新的绘制模型。基于软件的绘制模型和基于硬件的绘制模型有和不同呢?
基于软件的绘制模型 在软件绘制模型下,视图按照如下两个步骤绘制:
1. Invalidate the hierarchy(注:hierarchy怎么翻译?)
2. Draw the hierarchy
应用程序调用invalidate()更新UI的某一部分,失效(invalidation)消息将会在整个视图层中传递,计算每个需要重绘的区域(即脏区域)。然后Android系统将会重绘所有和脏区域有交集的view。很明显,这种绘图模式存在缺点:
1. 每个绘制操作中会执行不必要的代码。比如如果应用程序调用invalidate()重绘button,而button又位于另一个view之上,即使该view没有变化,也会进行重绘。
2. 可能会掩盖一些应用程序的bug。因为android系统会重绘与脏区域有交集的view,所以view的内容可能会在没有调用invalidate()的情况下重绘。这可能会导致一个view依赖于其它view的失效才得到正确的行为。
基于硬件的绘制模型
Android系统仍然使用invalidate()和draw()来绘制view,但在处理绘制上有所不同。Android系统记录绘制命令到显示列表,而不是立即执行绘制命令。另一个优化就是Android系统只需记录和更新标记为脏(通过invalidate())的view。新的绘制模型包含三个步骤:
1. Invalidate the hierarchy
2. 记录和更新显示列表
3. 绘制显示列表

就是说当我们开启了硬件加速(默认是开的3.0开始),那么当我们改变某一个view的时候只会更新该view的脏区,举个例子吧
开了硬件加速:

public class MainActivity extends AppCompatActivity {
private static final String TAG = "MyView";
private int color = 20;
@Override
protected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);final View viewGroup = this.findViewById(R.id.activity_main);
//        final View view = this.findViewById(R.id.myView);viewGroup.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {viewGroup.setBackgroundColor(Color.rgb(color + 20, 20, 20));color += 20;}});
}
}

其余代码不变

10-01 00:36:02.686 10350-10350/com.example.jinxiong.blog D/MyView: ViewGroup onDraw
10-01 00:36:02.686 10350-10350/com.example.jinxiong.blog D/MyView: viewGroup dispatchDraw
10-01 00:36:57.554 10350-10350/com.example.jinxiong.blog D/MyView: ViewGroup onDraw
10-01 00:36:57.554 10350-10350/com.example.jinxiong.blog D/MyView: viewGroup dispatchDraw

关闭硬件加速

10-01 00:38:06.606 12367-12367/com.example.jinxiong.blog D/MyView: ViewGroup onDraw
10-01 00:38:06.606 12367-12367/com.example.jinxiong.blog D/MyView: view computeScroll
10-01 00:38:06.610 12367-12367/com.example.jinxiong.blog D/MyView: View onDraw
10-01 00:38:06.610 12367-12367/com.example.jinxiong.blog D/MyView: viewGroup dispatchDraw

硬件加速相关链接
http://blog.sina.com.cn/s/blog_821e2bb10102vdij.html

关于scrollTo/By 和 setTranslation

其实一开始对这两个方法不是很好的理解,但是慢慢的还是明白了,但是后来很奇怪为什么么他两不会引起onMeasure 和 onLayout这个两个方法

无论对于MyView 还是MyViewGroup 他们之间的位置都是没有改变的,那么为什么还需要measure 和 layout 尼

/*** Sets the horizontal location of this view relative to its {@link #getLeft() left} position.* This effectively positions the object post-layout, in addition to wherever the object's* layout placed it.** @param translationX The horizontal position of this view relative to its left position,* in pixels.** @attr ref android.R.styleable#View_translationX*/

translationX/Y是view中的一个属性,只是在layout 之后再次移动的位置,并不会改变view的left ,top,right,bottom的值,也不会引起重新measure 和layout ,只有改变某个view 的padding 和margin属性值才会引起重新measure 和 layout(我暂时知道的/微笑)

View 的各种知识相关推荐

  1. Android自定义view之基础知识

    Android自定义view之基础知识 虽然Android已经自带了很多实用的view和layout,加以调教能实现很美观的界面,但是有一些情况下,需要实现特殊的界面效果,比如我们比较熟悉的各种播放器 ...

  2. View 绘制体系知识梳理(7) getMeasuredWidth 和 getWidth 的区别

    前言 前几天被问到了getMeasuredWidth和getWidth之间的区别,因此回来看了一下源码,又顺便复习了一遍measure/layout/draw的过程,有兴趣的同学可以看前面的几篇文章 ...

  3. Android View(一)——View的基础知识

    目录 一.View的基础知识 1.什么是View 2.View的位置参数 3.MotionEvent 4. TouchSlop 5. VelocityTracker 6. GestureDetecto ...

  4. Android 自定义 View 最少必要知识,互联网寒冬

    2.4 封装 有些控件可能在多个地方使用,如大多数 App 里面的底部 Tab,像这样的经常被用到的控件就可以通过自定义 View 将它们封装起来,以便在多个地方使用. 3. 如何自定义 View? ...

  5. 第一章 仿支付宝芝麻信用界面制作(需要自定义View的相关知识)

    之前一段时间帮朋友做一个类似芝麻信用分界面的效果的View,效果就是随着分数的增长,圆弧和指针的位置.角度也随之改变,先给大家分享下效果图 当时看到那个效果也是着实考虑了一番,考虑了很多熬死了许许多多 ...

  6. View 绘制体系知识梳理(4) 绘制过程之 Layout 详解

    一.布局的起点 - performTraversals 和前面分析测量过程类似,整个布局的起点也是在ViewRootImpl的performTraversals当中: private void per ...

  7. android minheight的作用,Android 自定义 View 最少必要知识

    1. 什么是自定义 View? 1.1 定义 在 Android 系统中,界面中所有能看到的元素都是 View.默认情况下,Android 系统为开发者提供了很多 View,比如用于展示文本信息的 T ...

  8. View的事件体系(上)(View基础知识,滑动,弹性滑动)

    View不是四大组件之一,但重要性堪比四大组件,本篇博文主要讲解View的事件体系,包括View的基础知识,滑动,弹性滑动,事件分发机制,滑动冲突的种类与解决方案. 一 View的基础知识 (1).V ...

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

    2019独角兽企业重金招聘Python工程师标准>>> 今天继续给大家分享下View的相关知识,重点有一下两点:   1.View的几种不同状态属性            2.如何根 ...

  10. 转载 干货 | 陪伴我学习NLP、知识图谱的那些资源(教程+书籍+网站+工具+论文...可以说很全面了)

    https://blog.csdn.net/guleileo/article/details/81140179 干货 | 陪伴我学习NLP.知识图谱的那些资源(教程+书籍+网站+工具+论文...可以说 ...

最新文章

  1. ICCV 2019接收论文提前看,旷视11篇入选文章放出 | 资源
  2. python入门之控制结构-循环结构_Python 入门之控制结构 - 循环结构(一)
  3. 新建linux 服务器初始化配置
  4. HangFire循环作业中作业因执行时间太长未完成新作业开启导致重复数据的问题...
  5. Balsamiq Mockups 实例3
  6. 适合初学者的sql_适用于初学者SQL多重连接示例
  7. 时间序列分析导论书摘:确定自回归过程的阶数
  8. 室内智能照明控制系统电路设计
  9. 通达信版弘历软件指标_弘历软件指标源码
  10. 雷顿学院《百万大咖》校园行
  11. windows 2012下安装.NET框架时出现组件的文件跟组件清单中的验证信息不匹配,无法安装
  12. html 取消settimeout,JavaScript中停止执行setInterval和setTimeout事件的方法
  13. Aria2高速下载利器 带你冲破百度网盘重重束缚
  14. mobilefacenet caffe2WK
  15. EBS 退货单无法退账,VALIDATE_RMA_LOT_QUANTITIES_ERROR
  16. 自己动手做一个局域网聊天工具(一)
  17. ]媒体记者报道新闻,抒情是诗人的事!
  18. mac使用allure_mac 上构建 Jenkins+allure 报告时,一直报错
  19. 技术人员分享的好处,why not?
  20. 无脑人:我们真的需要大脑吗?

热门文章

  1. Android中Style详解
  2. mysql mysqld multi_MySQL 使用mysqld_multi部署单机多实例详细过程 (转)
  3. #define 喵 int_【吃鸡大作战第三季】第12集 告白小雪喵
  4. 计算机英语趣味知识竞赛,(经典)精华版 英语趣味知识竞赛.ppt
  5. UE4 各种玻璃材质制作汇总
  6. java color 棕色,接近黑色的染发颜色 低调但是显气色的发色
  7. 使用jib-maven-plugin分层构建Docker镜像——避免直接使用FatJar
  8. 20【材料】相关专业调剂信息!一直更新!
  9. 李维看 .net 和 DELPHI 6 (含李维照片) (转)
  10. PM notifier