一.初见View

  1. View的层级关系(Veiw到底分成几层,自定义view是从那一层开始绘制的)

    R:Veiw树的结构如下 ,自定义View是从DecorView开始的;DecorView是View树的最顶层View,

  2. View树的遍历过程

  3. 父布局对View绘制模式的影响 (P182表)

    4.为什么父view为wrap_parent时,继承的view布局为warp_parent行不通达不到预期效果?

    5.如果在activity启动时获取某个view的尺寸,怎么获取?

    onWindowsFocusChanges方法时获取

    6.getwidth()和getMeasurewidth()有什么区别?

  4. Touch事件分析 包含2个一个是 dispatchTouchEvent 和OnTouchEvent();

  5. 如何处理滑动冲突

二. MeasureSpec

1.由来:在View的测量过程中系统会将View的LayoutParams根据父容器所施加的规格转换成对应的MeasureSpec,然后偶再根据这个MeasureSpec来测量Veiw的宽高;

2.MeasureSpec是一个32位的int值,可以通过getMode和getSize来获取SpecMode和SpecSize;

3.SpecSize的有3种类型:

|无限制类型unspecified |精确类型exactly |至多型at_most |

| 无限制 |对应match_parent和精确数值 |对应于wrap_content |

2.1MeasureSpec和Layoutparams对应关系

4.顶层DecorView的MeasureSpec的产生,在ViewRootImpl中

  1. private static int getRootMeasureSpec(int windowSize, int rootDimension) {
  2. int measureSpec;
  3. switch (rootDimension) {
  4. case ViewGroup.LayoutParams.MATCH_PARENT:
  5. // Window can't resize. Force root view to be windowSize.
  6. measureSpec = MeasureSpec.makeMeasureSpec(windowSize, MeasureSpec.EXACTLY);
  7. break;
  8. case ViewGroup.LayoutParams.WRAP_CONTENT:
  9. // Window can resize. Set max size for root view.
  10. measureSpec = MeasureSpec.makeMeasureSpec(windowSize, MeasureSpec.AT_MOST);
  11. break;
  12. default:
  13. // Window wants to be an exact size. Force root view to be that size.
  14. measureSpec = MeasureSpec.makeMeasureSpec(rootDimension, MeasureSpec.EXACTLY);
  15. break;
  16. }
  17. return measureSpec;
  18. }

由上可以得出 :

Layoutparmas.match_parent wrap_parent 精确(50dp)
精确模式,大小就是窗口大小 最大模式 at_most 精确模式,大小就是制定大小

5.普通View的产生,由于View的measure是由ViewGrop传递而来

  1. /**
  2. * Does the hard part of measureChildren: figuring out the MeasureSpec to
  3. * pass to a particular child. This method figures out the right MeasureSpec
  4. * for one dimension (height or width) of one child view.
  5. *
  6. * The goal is to combine information from our MeasureSpec with the
  7. * LayoutParams of the child to get the best possible results. For example,
  8. * if the this view knows its size (because its MeasureSpec has a mode of
  9. * EXACTLY), and the child has indicated in its LayoutParams that it wants
  10. * to be the same size as the parent, the parent should ask the child to
  11. * layout given an exact size.
  12. *
  13. * @param spec The requirements for this view
  14. * @param padding The padding of this view for the current dimension and
  15. * margins, if applicable
  16. * @param childDimension How big the child wants to be in the current
  17. * dimension
  18. * @return a MeasureSpec integer for the child
  19. */
  20. public static int getChildMeasureSpec(int spec, int padding, int childDimension) {
  21. int specMode = MeasureSpec.getMode(spec); // 一般来说这是父View传进来的
  22. int specSize = MeasureSpec.getSize(spec); // 同上
  23. int size = Math.max(0, specSize - padding); //去掉padding得到内容的大小
  24. int resultSize = 0;
  25. int resultMode = 0;
  26. switch (specMode) {
  27. // Parent has imposed an exact size on us
  28. // childDimension是layout文件里设置的大小:可以是MATCH_PARENT, WRAP_CONTENT或者具体大小, 代码中分别对三种做不同的处理
  29. case MeasureSpec.EXACTLY:
  30. if (childDimension >= 0) {
  31. //如果childDimension是具体大小,那么就按照这个大小来, Mode为Exactly,这样子View就知道要按照填写的大小来draw
  32. resultSize = childDimension;
  33. resultMode = MeasureSpec.EXACTLY;
  34. } else if (childDimension == LayoutParams.MATCH_PARENT) {
  35. // Child wants to be our size. So be it.
  36. // 子View填的是Match_parent, 那么父View就给子view自己的size(去掉padding), 然后告诉子view这是Exactly的大小
  37. resultSize = size;
  38. resultMode = MeasureSpec.EXACTLY;
  39. } else if (childDimension == LayoutParams.WRAP_CONTENT) {
  40. // Child wants to determine its own size. It can't be
  41. // bigger than us.
  42. // 子View 填的是wrap_Content,那么父View就告诉子View自己的size(去掉padding), 然后告诉子View, 你最多有这么多, 你自己决定大小
  43. resultSize = size;
  44. resultMode = MeasureSpec.AT_MOST;
  45. }
  46. break;
  47. // Parent has imposed a maximum size on us
  48. case MeasureSpec.AT_MOST:
  49. if (childDimension >= 0) {
  50. // Child wants a specific size... so be it
  51. resultSize = childDimension;
  52. resultMode = MeasureSpec.EXACTLY;
  53. } else if (childDimension == LayoutParams.MATCH_PARENT) {
  54. // Child wants to be our size, but our size is not fixed.
  55. // Constrain child to not be bigger than us.
  56. // 如果子View是Match_parent,但是父View是告诉子View, 你最多有size这么多, 那么就告诉子View,你最多有父View的size
  57. resultSize = size;
  58. resultMode = MeasureSpec.AT_MOST;
  59. } else if (childDimension == LayoutParams.WRAP_CONTENT) {
  60. // Child wants to determine its own size. It can't be
  61. // bigger than us.
  62. // 基本和上一个情况一样
  63. resultSize = size;
  64. resultMode = MeasureSpec.AT_MOST;
  65. }
  66. break;
  67. // Parent asked to see how big we want to be
  68. case MeasureSpec.UNSPECIFIED:
  69. if (childDimension >= 0) {
  70. // Child wants a specific size... let him have it
  71. resultSize = childDimension;
  72. resultMode = MeasureSpec.EXACTLY;
  73. } else if (childDimension == LayoutParams.MATCH_PARENT) {
  74. // Child wants to be our size... find out how big it should
  75. // be
  76. resultSize = 0;
  77. resultMode = MeasureSpec.UNSPECIFIED;
  78. } else if (childDimension == LayoutParams.WRAP_CONTENT) {
  79. // Child wants to determine its own size.... find out how
  80. // big it should be
  81. resultSize = 0;
  82. resultMode = MeasureSpec.UNSPECIFIED;
  83. }
  84. break;
  85. }
  86. return MeasureSpec.makeMeasureSpec(resultSize, resultMode);
  87. }

从上面的方法不难理解,他的代码主要是阐述根据父容器的MeasureSpec同时结合View本身的Layoutparams来确定子元素的MeasureSpec,列表如下

三. View的工作流程

View的工作流程主要是指Measure layout draw三个流程,即测量布局和绘制

3.1 View的测量

1.测量代码主要如下

  1. protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
  2. setMeasuredDimension(getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec),
  3. getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec));
  4. }
  5. 这段代码主要是确定View的测量后的值
  6. /**
  7. * Utility to return a default size. Uses the supplied size if the
  8. * MeasureSpec imposed no constraints. Will get larger if allowed
  9. * by the MeasureSpec.
  10. *
  11. * @param size Default size for this view
  12. * @param measureSpec Constraints imposed by the parent
  13. * @return The size this view should be.
  14. */
  15. public static int getDefaultSize(int size, int measureSpec) {
  16. int result = size;
  17. int specMode = MeasureSpec.getMode(measureSpec);
  18. int specSize = MeasureSpec.getSize(measureSpec);
  19. switch (specMode) {
  20. case MeasureSpec.UNSPECIFIED:
  21. result = size;
  22. break;
  23. case MeasureSpec.AT_MOST:
  24. case MeasureSpec.EXACTLY:
  25. result = specSize;
  26. break;
  27. }
  28. return result;
  29. }

由上面的代码可知在at_most和exactly的模式下,返回的view的specsize就是测量后的大小,(测量后的大小!=view的最终大小,最终大小是在layout时确定的),而unspecified,这种情况下,由上面代码可知

result=size,即宽高分别为传入值,那么这个传入值的方法是什么呢,往下看

  1. /**
  2. * Returns the suggested minimum height that the view should use. This
  3. * returns the maximum of the view's minimum height
  4. * and the background's minimum height
  5. * ({@link android.graphics.drawable.Drawable#getMinimumHeight()}).
  6. * <p>
  7. * When being used in {@link #onMeasure(int, int)}, the caller should still
  8. * ensure the returned height is within the requirements of the parent.
  9. *
  10. * @return The suggested minimum height of the view.
  11. */
  12. protected int getSuggestedMinimumHeight() {
  13. return (mBackground == null) ? mMinHeight : max(mMinHeight, mBackground.getMinimumHeight());
  14. }
  15. /**
  16. * Returns the suggested minimum width that the view should use. This
  17. * returns the maximum of the view's minimum width
  18. * and the background's minimum width
  19. * ({@link android.graphics.drawable.Drawable#getMinimumWidth()}).
  20. * <p>
  21. * When being used in {@link #onMeasure(int, int)}, the caller should still
  22. * ensure the returned width is within the requirements of the parent.
  23. *
  24. * @return The suggested minimum width of the view.
  25. */
  26. protected int getSuggestedMinimumWidth() {
  27. return (mBackground == null) ? mMinWidth : max(mMinWidth, mBackground.getMinimumWidth());
  28. }

从上面代码可以看出 如果view没有设置背景,view宽度对应xml里面的Android:minwidth属性所对应的值,如果没有指定即为0;如果有设置背景则取二者的较大值,

综合上面2段代码来看,也就很好的解释了开头的问题4,直接继承属性为wrap_parent的view的自定义控件需要重写onMeasure并设置其自身大小,因为如果父view为wrap,那么他的Specmode就是at_most,其子view如果是wrap属性,那么子veiw大小就是父控件的大小,和子view使用match_parent无异;

2 ViewGroup的测量

其实一开始我以为ViewGroup和View的测量代码是一样的都是调用onMeasure去完成,后来发现虽然逻辑上它的确是遍历所有子元素Measure方法然后子元素递归执行这个过程,但是实际上有点区别..由于

Viewgroup是一个抽象类,里面没有Onmeasure方法,他把测量工作交给了各个子类去实现,原因也是因为不同的布局是有不同的计算方式

  1. /**
  2. * Ask all of the children of this view to measure themselves, taking into
  3. * account both the MeasureSpec requirements for this view and its padding.
  4. * We skip children that are in the GONE state The heavy lifting is done in
  5. * getChildMeasureSpec.
  6. *
  7. * @param widthMeasureSpec The width requirements for this view
  8. * @param heightMeasureSpec The height requirements for this view
  9. */
  10. protected void measureChildren(int widthMeasureSpec, int heightMeasureSpec) {
  11. final int size = mChildrenCount;
  12. final View[] children = mChildren;
  13. for (int i = 0; i < size; ++i) {
  14. final View child = children[i];
  15. if ((child.mViewFlags & VISIBILITY_MASK) != GONE) {
  16. measureChild(child, widthMeasureSpec, heightMeasureSpec);
  17. }
  18. }
  19. }
  20. /**
  21. * Ask one of the children of this view to measure itself, taking into
  22. * account both the MeasureSpec requirements for this view and its padding.
  23. * The heavy lifting is done in getChildMeasureSpec.
  24. *
  25. * @param child The child to measure
  26. * @param parentWidthMeasureSpec The width requirements for this view
  27. * @param parentHeightMeasureSpec The height requirements for this view
  28. */
  29. protected void measureChild(View child, int parentWidthMeasureSpec,
  30. int parentHeightMeasureSpec) {
  31. final LayoutParams lp = child.getLayoutParams();
  32. final int childWidthMeasureSpec = getChildMeasureSpec(parentWidthMeasureSpec,
  33. mPaddingLeft + mPaddingRight, lp.width);
  34. final int childHeightMeasureSpec = getChildMeasureSpec(parentHeightMeasureSpec,
  35. mPaddingTop + mPaddingBottom, lp.height);
  36. child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
  37. }

3.2view的布局过程

1.先普及下坐标概念

View获取自身宽高

getHeight():获取View自身高度

getWidth():获取View自身宽度

View自身坐标

通过如下方法可以获得View到其父控件(ViewGroup)的距离:

getTop():获取View自身顶边到其父布局顶边的距离

getLeft():获取View自身左边到其父布局左边的距离

getRight():获取View自身右边到其父布局左边的距离

getBottom():获取View自身底边到其父布局顶边的距离

MotionEvent提供的方法

我们看上图那个深蓝色的点,假设就是我们触摸的点,我们知道无论是View还是ViewGroup,最终的点击事件都会由onTouchEvent(MotionEvent event)方法来处理,MotionEvent也提供了各种获取焦点坐标的方法:

getX():获取点击事件距离控件左边的距离,即视图坐标

getY():获取点击事件距离控件顶边的距离,即视图坐标

getRawX():获取点击事件距离整个屏幕左边距离,即绝对坐标

getRawY():获取点击事件距离整个屏幕顶边的的距离,即绝对坐标

2.layout的作用是ViewGroup用来确定子元素的位置,当ViewGroup位置被确定后,他会遍历所有的子元素并调用其layout方法,layout确定veiw本身的位子,onLayout确定所有子元素的位子

  1. public void layout(int l, int t, int r, int b) {
  2. if ((mPrivateFlags3 & PFLAG3_MEASURE_NEEDED_BEFORE_LAYOUT) != 0) {
  3. onMeasure(mOldWidthMeasureSpec, mOldHeightMeasureSpec);
  4. mPrivateFlags3 &= ~PFLAG3_MEASURE_NEEDED_BEFORE_LAYOUT;
  5. }
  6. int oldL = mLeft;
  7. int oldT = mTop;
  8. int oldB = mBottom;
  9. int oldR = mRight;
  10. boolean changed = isLayoutModeOptical(mParent) ?
  11. setOpticalFrame(l, t, r, b) : setFrame(l, t, r, b);
  12. if (changed || (mPrivateFlags & PFLAG_LAYOUT_REQUIRED) == PFLAG_LAYOUT_REQUIRED) {
  13. onLayout(changed, l, t, r, b);
  14. if (shouldDrawRoundScrollbar()) {
  15. if(mRoundScrollbarRenderer == null) {
  16. mRoundScrollbarRenderer = new RoundScrollbarRenderer(this);
  17. }
  18. } else {
  19. mRoundScrollbarRenderer = null;
  20. }
  21. mPrivateFlags &= ~PFLAG_LAYOUT_REQUIRED;
  22. ListenerInfo li = mListenerInfo;
  23. if (li != null && li.mOnLayoutChangeListeners != null) {
  24. ArrayList<OnLayoutChangeListener> listenersCopy =
  25. (ArrayList<OnLayoutChangeListener>)li.mOnLayoutChangeListeners.clone();
  26. int numListeners = listenersCopy.size();
  27. for (int i = 0; i < numListeners; ++i) {
  28. listenersCopy.get(i).onLayoutChange(this, l, t, r, b, oldL, oldT, oldR, oldB);
  29. }
  30. }
  31. }
  32. mPrivateFlags &= ~PFLAG_FORCE_LAYOUT;
  33. mPrivateFlags3 |= PFLAG3_IS_LAID_OUT;
  34. }

在第12行通过setFrame设定View的四个顶点位置然后在调用onlayout方法去确定子Veiw的在Veiw中的位子;

3.getwidth()和getMeasurewidth()有什么区别?

前者是最终的宽高,后者是测量的宽高,后者形成早于前者,如果一般默认情况下二者的确是相等的,但是如果我们自定义时人为更改,就会比测量的多出100

  1. @Override
  2. public void layout(@Px int l, @Px int t, @Px int r, @Px int b) {
  3. super.layout(l, t, r+100, b+100);
  4. }

3.3View的绘制过程

draw过程的作用是将view绘制在屏幕上面

1.draw的步骤遵循以下几步

  • 绘制背景 background.draw(canvas)
  • 绘制自己 ondraw
  • 绘制Children dispatchdraw
  • 绘制装饰 ondrawScrollbars

    源码里面其实写的也很清楚
  1. /**
  2. * Manually render this view (and all of its children) to the given Canvas.
  3. * The view must have already done a full layout before this function is
  4. * called. When implementing a view, implement
  5. * {@link #onDraw(android.graphics.Canvas)} instead of overriding this method.
  6. * If you do need to override this method, call the superclass version.
  7. *
  8. * @param canvas The Canvas to which the View is rendered.
  9. */
  10. @CallSuper
  11. public void draw(Canvas canvas) {
  12. final int privateFlags = mPrivateFlags;
  13. final boolean dirtyOpaque = (privateFlags & PFLAG_DIRTY_MASK) == PFLAG_DIRTY_OPAQUE &&
  14. (mAttachInfo == null || !mAttachInfo.mIgnoreDirtyState);
  15. mPrivateFlags = (privateFlags & ~PFLAG_DIRTY_MASK) | PFLAG_DRAWN;
  16. /*
  17. * Draw traversal performs several drawing steps which must be executed
  18. * in the appropriate order:
  19. *
  20. * 1. Draw the background
  21. * 2. If necessary, save the canvas' layers to prepare for fading
  22. * 3. Draw view's content
  23. * 4. Draw children
  24. * 5. If necessary, draw the fading edges and restore layers
  25. * 6. Draw decorations (scrollbars for instance)
  26. */
  27. // Step 1, draw the background, if needed
  28. int saveCount;
  29. if (!dirtyOpaque) {
  30. drawBackground(canvas);
  31. }
  32. // skip step 2 & 5 if possible (common case)
  33. final int viewFlags = mViewFlags;
  34. boolean horizontalEdges = (viewFlags & FADING_EDGE_HORIZONTAL) != 0;
  35. boolean verticalEdges = (viewFlags & FADING_EDGE_VERTICAL) != 0;
  36. if (!verticalEdges && !horizontalEdges) {
  37. // Step 3, draw the content
  38. if (!dirtyOpaque) onDraw(canvas);
  39. // Step 4, draw the children
  40. dispatchDraw(canvas);
  41. // Overlay is part of the content and draws beneath Foreground
  42. if (mOverlay != null && !mOverlay.isEmpty()) {
  43. mOverlay.getOverlayView().dispatchDraw(canvas);
  44. }
  45. // Step 6, draw decorations (foreground, scrollbars)
  46. onDrawForeground(canvas);
  47. // we're done...
  48. return;
  49. }

四.自定义Veiw的分类

1.继承View重新Ondraw()方法

2.继承viewGroup派生特殊的layout

3.继承特定的某个view

4.继承特定的viewGroup(LinearLayout)

Android艺术探索第四 view的自定义的更多相关文章

  1. Android自动化测试探索(四)uiautomator2简介和使用

    uiautomator2简介 项目Git地址: https://github.com/openatx/uiautomator2 安装 #1. 安装 uiautomator2 使用pip进行安装, 注意 ...

  2. 《android开发艺术探索》读书笔记(四)--View工作原理

    接上篇<android开发艺术探索>读书笔记(三) No1: View的三大流程:测量流程.布局流程.绘制流程 No2: ViewRoot对应于ViewRootImpl类,它是连接Wind ...

  3. Android艺术开发探索第四章——View的工作原理(下)

    Android艺术开发探索第四章--View的工作原理(下) 我们上篇BB了这么多,这篇就多多少少要来点实战了,上篇主席叫我多点自己的理解,那我就多点真诚,少点套路了,老司机,开车吧! 我们这一篇就扯 ...

  4. Android艺术开发探索第三章————View的事件体系(下)

    Android艺术开发探索第三章----View的事件体系(下) 在这里就能学习到很多,主要还是对View的事件分发做一个体系的了解 一.View的事件分发 上篇大致的说了一下View的基础知识和滑动 ...

  5. Android艺术开发探索第三章——View的事件体系(上)

    Android艺术开发探索第三章----View的事件体系(上) 我们继续来看这本书,因为有点长,所以又分了上下,你在本片中将学习到 View基础知识 什么是View View的位置参数 Motion ...

  6. Android开发艺术探索笔记——View(二)

    Android开发艺术探索笔记--View(二) View的事件分发机制 学习资料: 1.Understanding Android Input Touch Events System Framewo ...

  7. Android开发艺术探索笔记—— View(一)

    Android开发艺术探索笔记 --View(一) View的基础知识 什么是View View是Android中所有控件的基类.是一种界面层控件的抽象. View的位置参数 参数名 获取方式 含义 ...

  8. 《Android开发艺术探索》读书笔记 (3) 第3章 View的事件体系

    本节和<Android群英传>中的第五章Scroll分析有关系,建议先阅读该章的总结 第3章 View的事件体系 3.1 View基本知识 (1)view的层次结构:ViewGroup也是 ...

  9. 《android开发艺术探索》读书笔记(十四)--JNI和NDK编程

    接上篇<android开发艺术探索>读书笔记(十三)--综合技术 No1: Java JNI--Java Native Interface(java本地接口),它是为了方便java调用C. ...

随机推荐

  1. tensorflow Sigmoid 应用

    1.函数 函数:f(z) = 1 / (1 + exp( − z)) 导数:f(z)' = f(z)(1 − f(z)) 2.tensorflow实现 #!/usr/bin/env python # ...

  2. 存储结构比较vector,list,dequeue,stack(转)

        vector适用:对象数量变化少,简单对象,随机访问元素频繁list适用:对象数量变化大,对象复杂,插入和删除频繁最大的区别是,list是双向的,而vector是单向的.因此在实际使用时,如何 ...

  3. HTML随笔1

    1.编号列表: <ol type="A" start="1">    //type中有"A","1",&qu ...

  4. raft协议安全性保证

    分布式系统中主要的问题就是如何保持节点状态的一致性,不论发生任何failure,只要集群中大部分的节点可以正常工作,则这些节点具有相同的状态,保持一致,在client看来相当于一台机器. 一致性问题本 ...

  5. java反射机制(1)

      百度百科: java 反射机制:JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法:对于任意一个对象,都能够调用它的任意方法和属性:这种动态获取信息以及动态调用对象方 ...

  6. 看完给跪了:技术大牛总结的Github与华为软件开发云完整对比

    华为软件开发云配置管理 服务和Github是国内外比较有代表性的代码托管平台,它们以git作为版本管理工具,使项目中身处各地的人员可以协同工作,主要操作涉及仓库.分支.提交.pull request等 ...

  7. 一步一步学多线程-Timer

    在执行定时任务的时候可以用Timer来实现,现在小编对学到的进行一次总结,先来看一个demo 1 public class TimerTest { 2 3 public static void mai ...

  8. Maven注意事项

    Maven好处: 1.依赖管理:对jar包统一管理 2.项目构建:项目编码完成后,对项目进行编译.测试.打包.部署,实现项目一键发布1.配置环境变量    注意:maven工具必须有jdk环境.并且: ...

  9. 完美结合 Redux 与 React-router (react-router不切换页面)

    本文可以解答以下问题: 链接 redux 后 react-router 不切换页面 react-router 路由改变页面却不改变 redux 与 react-router 结合使用的方案 简单的问题 ...

  10. Oracle联机日志损坏解决办法

    关于此问题,在网上找到一篇不错的文章. 大家都清楚,联机日志分为当前联机日志和非当前联机日志. ---------------------------------------------------- ...