Android自定义View

为什么要自定义View
自定义View的基本方法
自定义View的最基本的三个方法分别是: onMeasure()、onLayout()、onDraw(); View在Activity中显示出来,要经
历测量、布局和绘制三个步骤,分别对应三个动作:measure、layout和draw。

  • 测量:onMeasure()决定View的大小;
  • 布局:onLayout()决定View在ViewGroup中的位置;
  • 绘制:onDraw()决定绘制这个View。

自定义控件分类

  • 自定义View: 只需要重写onMeasure()和onDraw()
  • 自定义ViewGroup: 则只需要重写onMeasure()和onLayout()

自定义View基础

View的分类
视图View主要分为两类

类别解释 特点
视图组 即多个View组成的ViewGroup,如LinearLayout 包含子View
单一视图 即一个View,如TextView 不包含子View

 

View类简介

  • View类是Android中各种组件的基类,如View是ViewGroup基类
  • View表现为显示在屏幕上的各种视图

Android中的UI组件都由View、ViewGroup组成。

  • View的构造函数:共有4个

// 如果View是在Java代码里面new的,则调用第一个构造函数
public CarsonView(Context context) {
    super(context);
 }

// 如果View是在.xml里声明的,则调用第二个构造函数
// 自定义属性是从AttributeSet参数传进来的
  public  CarsonView(Context context, AttributeSet attrs) {
    super(context, attrs);
 }
// 不会自动调用
// 一般是在第二个构造函数里主动调用
// 如View有style属性时
  public  CarsonView(Context context, AttributeSet attrs, int defStyleAttr) {
    super(context, attrs, defStyleAttr);
 }
  //API21之后才使用
  // 不会自动调用
  // 一般是在第二个构造函数里主动调用
  // 如View有style属性时
  public  CarsonView(Context context, AttributeSet attrs, int defStyleAttr, int
defStyleRes) {
    super(context, attrs, defStyleAttr, defStyleRes);
 }

AttributeSet与自定义属性


系统自带的View可以在xml中配置属性,对于写的好的自定义View同样可以在xml中配置属性,为了使自定义的
View的属性可以在xml中配置,需要以下4个步骤:

1. 通过 <declare-styleable> 为自定义View添加属性
2. 在xml中为相应的属性声明属性值
3. 在运行时(一般为构造函数)获取属性值
4. 将获取到的属性值应用到View

View视图结构

1. PhoneWindow是Android系统中最基本的窗口系统,继承自Windows类,负责管理界面显示以及事件响应。它
是Activity与View系统交互的接口
2. DecorView是PhoneWindow中的起始节点View,继承于View类,作为整个视图容器来使用。用于设置窗口属
性。它本质上是一个FrameLayout
3. ViewRoot在Activtiy启动时创建,负责管理、布局、渲染窗口UI等等

对于多View的视图,结构是树形结构:最顶层是ViewGroup,ViewGroup下可能有多个ViewGroup或View,如下图:

一定要记住:无论是measure过程、layout过程还是draw过程,永远都是从View树的根节点开始测量或计算(即从树的顶端开始),一层一层、

一个分支一个分支地进行(即树形递归),最终计算整个View树中各个View,最终确定整个View树的相关属性。

Android坐标系

Android的坐标系定义为:屏幕的左上角为坐标原点向右为x轴增大方向

View位置(坐标)描述

View的位置由4个顶点决定的 4个顶点的位置描述分别由4个值决定:请记住:View的位置是相对于父控件而言的)

  • Top:子View上边界到父view上边界的距离
  • Left:子View左边界到父view左边界的距离
  • Bottom:子View下边距到父View上边界的距离
  • Right:子View右边界到父view左边界的距离

 

位置获取方式

View的位置是通过view.getxxx()函数进行获取:(以Top为例)

Android中颜色相关内容

View树的绘制流程

View树的绘制流程是谁负责的?

view树的绘制流程是通过ViewRoot去负责绘制的,ViewRoot这个类的命名有点坑,最初看到这个名字,翻译过来是view的根节点,但是事实完全不是这样,

ViewRoot其实不是View的根节点,它连view节点都算不上,它的主要作用是View树的管理者,负责将DecorView和PhoneWindow“组合”起来,

而View树的根节点严格意义上来说只有DecorView;每个DecorView都有一个ViewRoot与之关联,这种关联关系是由WindowManager去进行管理的;

view的添加

View的绘制流程

measure

1. 系统为什么要有measure过程?

2. measure过程都干了点什么事?

3. 对于自适应的尺寸机制,如何合理的测量一颗View树?

4. 那么ViewGroup是如何向子View传递限制信息的?

5. ScrollView嵌套ListView问题?

layout

1. 系统为什么要有layout过程?

2. layout过程都干了点什么事?

draw

1. 系统为什么要有draw过程?

2. draw过程都干了点什么事?

LayoutParams
LayoutParams 翻译过来就是布局参数,子 View 通过 LayoutParams 告诉父容器( ViewGroup )应该如何放置自己。
从这个定义中也可以看出来 LayoutParams ViewGroup 是息息相关的,因此脱离 ViewGroup LayoutParams 是没
有意义的。
事实上,每个 ViewGroup 的子类都有自己对应的 LayoutParams 类,典型的如 LinearLayout.LayoutParams
FrameLayout.LayoutParams 等,可以看出来 LayoutParams 都是对应 ViewGroup 子类的内部类
MarginLayoutParams
MarginLayoutParams 是和外间距有关的。事实也确实如此,和 LayoutParams 相比, MarginLayoutParams 只是增
加了对上下左右外间距的支持。实际上大部分 LayoutParams 的实现类都是继承自 MarginLayoutParams ,因为基本
所有的父容器都是支持子 View 设置外间距的
属性优先级问题 MarginLayoutParams 主要就是增加了上下左右 4 种外间距。在构造方法中,先是获取了
margin 属性;如果该值不合法,就获取 horizontalMargin ;如果该值不合法,再去获取 leftMargin
rightMargin 属性( verticalMargin topMargin bottomMargin 同理)。我们可以据此总结出这几种属性的优
先级
margin > horizontalMargin verticalMargin > leftMargin RightMargin topMargin bottomMargin
属性覆盖问题 优先级更高的属性会覆盖掉优先级较低的属性。此外,还要注意一下这几种属性上的注释
Call {@link ViewGroup#setLayoutParams(LayoutParams)} after reassigning a new value
 
LayoutParams View 如何建立联系
XML 中定义 View
Java 代码中直接生成 View 对应的实例对象
 
addView
自定义 LayoutParams
1. 创建自定义属性
2. 继承 MarginLayout
 
LayoutParams 常见的子类
在为 View 设置 LayoutParams 的时候需要根据它的父容器选择对应的 LayoutParams ,否则结果可能与预期不一致,
这里简单罗列一些常见的 LayoutParams 子类:
  • ViewGroup.MarginLayoutParams
  • FrameLayout.LayoutParams
  • LinearLayout.LayoutParams
  • RelativeLayout.LayoutParams
  • RecyclerView.LayoutParams
  • GridLayoutManager.LayoutParams
  • StaggeredGridLayoutManager.LayoutParams
  • ViewPager.LayoutParams
  • WindowManager.LayoutParams

MeasureSpec

定义

测量规格 , 封装了父容器对 view 的布局上的限制,内部提供了宽高的信息( SpecMode SpecSize ), SpecSize 是指
在某种 SpecMode 下的参考尺寸,其中 SpecMode 有如下三种:
 
  • UNSPECIFIED 父控件不对你有任何限制,你想要多大给你多大,想上天就上天。这种情况一般用于系统内部, 表示一种测量状态。(这个模式主要用于系统内部多次Measure的情形,并不是真的说你想要多大最后就真有 多大)
  • EXACTLY 父控件已经知道你所需的精确大小,你的最终大小应该就是这么大。
  • AT_MOST 你的大小不能大于父控件给你指定的size,但具体是多少,得看你自己的实现。 measurespec1

 

MeasureSpecs 的意义
通过将 SpecMode SpecSize 打包成一个 int 值可以避免过多的对象内存分配,为了方便操作,其提供了打包 /
包方法
MeasureSpec 值的确定
MeasureSpec 值到底是如何计算得来的呢
 
View MeasureSpec 值是根据子 View 的布局参数( LayoutParams )和父容器的 MeasureSpec 值计算得来的,具
体计算逻辑封装在 getChildMeasureSpec()
 
针对上表,这里再做一下具体的说明
对于应用层 View ,其 MeasureSpec 由父容器的 MeasureSpec 和自身的 LayoutParams 来共同决定
对于不同的父容器和 view 本身不同的 LayoutParams view 就可以有多种 MeasureSpec 1. view 采用固定宽
高的时候,不管父容器的 MeasureSpec 是什么, view MeasureSpec 都是精确模式并且其大小遵循
Layoutparams 中的大小; 2. view 的宽高是 match_parent 时,这个时候如果父容器的模式是精准模式,那么
view 也是精准模式并且其大小是父容器的剩余空间,如果父容器是最大模式,那么 view 也是最大模式并且其大
小不会超过父容器的剩余空间; 3. view 的宽高是 wrap_content 时,不管父容器的模式是精准还是最大化,
view 的模式总是最大化并且大小不能超过父容器的剩余空间。 4. Unspecifified 模式,这个模式主要用于系统内
部多次 measure 的情况下,一般来说,我们不需要关注此模式 ( 这里注意自定义 View 放到 ScrollView 的情况 需要
处理 )
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值