Android自定义View

自定义 view - 前置知识点

2018-07-14  本文已影响39人  前行的乌龟

ps :在系统的学习自定义 view 之前,搞懂本本篇的内容会让你学习的过程顺序,简单很多

view 的分类

view 其实就2种:


自定义 view 的3个核心方法

这3个方法就是自定义的核心了,自定义 view 不管我们怎么写,基本都是围绕这3个方法玩。view 没有 onLayout 方法,因为 view 不是容器里面放不了 view ,只有 ViewGroup 才有 onLayout 方法


自定义 view 的种类


view 的多个构造方法

view 的构造方法有4个,分别面对不同的使用情况,我们在自定义 view 时要知道在哪个构造的方法里做初始化,其实一般我们都是在这4个方法里面都写初始化方法的

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

// 如果View是在.xml里声明的,则调用第二个构造函数
// 自定义属性是从AttributeSet参数传进来的
    public  CarsonView(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

// 不会自动调用
    public  CarsonView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

    //API21之后才使用,不会自动调用
    public  CarsonView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
        super(context, attrs, defStyleAttr, defStyleRes);
    }

注意:即使你在View中使用了Style这个属性也不会调用三个参数的构造函数,所调用的依旧是两个参数的构造函数。


view 的视图层级

我们连带着把 Actvity 的 视图层级一起写一下吧,下面这张就是 Actvity 视图层级


2086682-5fda69bb0c776c22.png

我们在 xml 布局文件中声明的布局根节点并不是 Activity 的视图根节点,是上图中的 contentView 的位置,contentView 上面的都是 Activity 内部添加的,我们控制不了,但是我们需要了解,一些页面效果我们需要操作 DecorView

这张图是常见的 view 视图层级


944365-afb2be431e523baf.png

ViewGroup 里面还可以再放 ViewGroup,但是 view 里面就不能放任何view 了

无论是measure过程、layout过程还是draw过程,永远都是从View树的根节点开始测量或计算(即从树的顶端开始),一层一层、一个分支一个分支地进行(即树形递归),最终计算整个View树中各个View,最终确定整个View树的相关属性

上面图中有一层就表示视图有一个层级,视图层级越多就会加重 cpu 计算负荷,这个不是线性的关系,是几何层级的关系。ViewGroup 宽高采用 warp_content 时,会跑2次这个 ViewGroup 所属子 view 的 onMeasur 方法,会大大增加任务量。所以我们在写布局时,层级过多或是 warp_content 应用过多,都会造成页面加载计算大,页面卡顿,这是我们需要优化的一个点。


Android 坐标系

详细看下图:


944365-ee0cd39fd788e293.png

和数学坐标系的 Y 轴方向是不同的


view 的坐标

view 有3套描述坐标位置的方式:

1. Left,Top,Right,Bottom

这4个描述的是 view 的左右上下到 view 所在父控件左上角的位置

相关的 API :

getTop();       //获取子View左上角距父View顶部的距离
getLeft();      //获取子View左上角距父View左侧的距离
getBottom();    //获取子View右下角距父View顶部的距离
getRight();     //获取子View右下角距父View左侧的距离

详细看下图:


005Xtdi2gw1f1qzqwvkkbj308c0dwgm9.jpg

需要注意的是 right = left + view 的 width , bottom = top + view 的 height

2. rawX ,rawY

描述的是 view 左上角到屏幕左上角的距离

这个可以用 MotionEvent 中 get 和 getRaw 的区别来学习

相关 API :

event.getX();       //触摸点相对于其所在组件坐标系的坐标
event.getY();

event.getRawX();    //触摸点相对于屏幕默认坐标系的坐标
event.getRawY();

详细看下图:


005Xtdi2jw1f1r2bdlqhbj308c0dwwew.jpg
3. x,y,translationX、translationY

从android3.0开始,View增加了额外几个参数:x,y,translationX、translationY。其中x和y是View左上角的坐标,translationX和translationY是View左上角相对于父容器的偏移量,它们默认值是0。这些参数也是相对于View父容器。具体关系见下图:


5494434-0fbe681e48aaa4ea.png

x = left + translationX,y = top + translationY

x和left不同体现在:left是View的初始坐标,在绘制完毕后就不会再改变;而x是View偏移后的实时坐标,是实际坐标。y和top的区别同理。


如何获取 view 的宽高

获取 view 的宽高有2套 API:

他们的区别:


944365-6b27b9835d927e04.png

getMeasuredWidth() 方法可以在 view 的 onLayout 方法里使用,onLayout 在 onMeasure 之后跑,这时候 measuredWidth view 的宽是计算出来的,但是我们要考虑 view 申请的大小超过父控件最大值的问题。

我们可以考虑在 onSizeChange 方法内记录 view 的大小,这也是一种办法。这2套获取宽高的 API 最终的结果值都一样,区别在于产生数据的时机不同。

getWidth() / getHeight() 只有在 view 计算完并显示之后才能返回具体的值,其他时候返回的都是 0,所以 getWidth() / getHeight() 一般我们都是做延迟使用,等待 view 计算显示完毕

获取 view 宽高的时机不同,所依赖的方法也是不同的,具体的我就不写了,大家看这个:

另外还有一点要清楚:getMeasuredXXX() 有时并不 = getXXX() ,下面这段话足以解释

getMeasuredXXX() 与 getXXX() 的区别和联系所在。说得直白一点,measuredWidth 与 width 分别对应于视图绘制 的 measure 与 layout 阶段。很重要的一点是,我们要明白,View 的宽高是由 View 本身和 parent 容器共同决定的,要知道有这个 MeasureSpec 类的存在。

比如,View 通过自身 measure() 方法向 parent 请求 100x100 的宽高,那么这个宽高就是 measuredWidth 和 measuredHeight 值。但是,在 parent 的 onLayout() 阶段,通过 childview.layout() 方法只分配给 childview 50x50 的宽高。那么,这个 50x50 宽高就是 childview 实际绘制并显示到屏幕的宽高,也就是 width 和 height 值。

如果你对自定义 View 过程很熟练的话,理解这部分内容就比较轻松一些。事实上,开发过程中,getWidth() 和 getHeight() 方法用的更多一些。


Android 的角度 (angle) 与弧度 (radian)

android 的角度和弧度有其需要说上一说, android 里的角度和我们平时的习惯是嫌烦的,这点很坑爹

另外这块涉及到画布的相关操作(旋转)、正余弦函数计算等,即会涉及到角度(angle)与弧度(radian)的相关知识。

另外记住缩写:

角度,弧度的详细描述:


944365-7a81d3e1715eda0b.png

android 角度方向是顺时针的:


1785445-fbfc94447e590f0e.png

Android 中颜色部分

Android 支持一下几种颜色模式:


944365-43d2051c332e0f95.png

ARGB 表示4位颜色通道,RGB 表示3位颜色通道,RGB 相比 ARGB 少了透明的颜色通道,需要注意的是 ARGB8888 4位通道的图片若是转成 RGB565 3位通道的图片格式,是会造成图片色差的,A 透明颜色通道用的越多色差越严重

4位颜色通道含义:


944365-f63d3055739f08b2.png
//java中使用Color类定义颜色
 int color = Color.GRAY;     //灰色

  //Color类是使用ARGB值进行表示
  int color = Color.argb(127, 255, 0, 0);   //半透明红色
  int color = 0xaaff0000;                   //带有透明度的红色
<?xml version="1.0" encoding="utf-8"?>
<resources>

    //定义了红色(没有alpha(透明)通道)
    <color name="red">#ff0000</color>
    //定义了蓝色(没有alpha(透明)通道)
    <color name="green">#00ff00</color>
</resources>
//方法1
int color = getResources().getColor(R.color.mycolor);

//方法2(API 23及以上)
int color = getColor(R.color.myColor);    
<!--在style文件中引用-->
    <style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
        <item name="colorPrimary">@color/red</item>
    </style>

 <!--在layout文件中引用在/res/values/color.xml中定义的颜色-->
  android:background="@color/red"     

 <!--在layout文件中创建并使用颜色-->
  android:background="#ff0000"

上一篇下一篇

猜你喜欢

热点阅读