自定义控件

153 阅读4分钟

ARM2022.7.25

自定义View(该部分仅通过View部分讲解一点知识)

  1. Android系统内置的view无法满足我们的需求
  2. onMeasure()对当前view的尺寸进行测量、onDraw()绘制、构造函数
  3. xml布局中:wrap_content包住内容;match_parent填充父布局给予的全部空间;具体尺寸:dp、sp
  4. onMeasure()函数原型 protected void onMeasure(int widthMeasureSpec, int hightMeasureSpec) 传入的两个参数分别代表宽、高,但是int有32位,前两位代表对应的测量模式、后30位代表尺寸大小。比如widthMeasureSpec,前两位代表宽的测量模式,后30位是宽的大小。 测量模式:区别不同的布局模式。

d0019616f8281312fec2ed249d5c7fdd(1.jpg

  1. 从int数据提取测量模式与尺寸? 用Android内置的MeasureSpec测量规格可获得 int widthMode = MeasureSpec.get(widthMeasureSpec); int widthSize = MeasureSpec.getSize(widthMeasureSpec);
  2. 说明(了解) 对于View,MeasureSpec由父容器的MeasureSpec和自身LayoutParams。
  3. onDraw函数 在画板Canvas对象上进行绘制
  4. 自定义布局属性 需要在根标签(LinearLayout)重设置命名空间 命名空间:
  5. 名称随便取
  6. 命名空间后面取值是固定的:schemas.android.com/apk/res-aut… 在自定义View构造函数中有一个AttributeSet属性,提取布局中的属性

Android自定义View

58a8542bc5a9406f59c83da3476e3a1.jpg

ps:View主要实现onMeasure()和onDraw();ViewGroup主要实现onMeasure()和onLayout()

View

单一视图,即一个View View类是Android各个组件的基类,View表现为显示在屏幕上的各种视图

package com.example.zihuiview1;

import android.content.Context;
import android.util.AttributeSet;

public class CarsonView {
    Context context;
    AttributeSet attributeSet;
    int defStyleAttr;
    int defStyleRes;

    //第一个构造函数
    public CarsonView(Context context) {
        this.context = context;
    }

    //如果View是在xml布局中声明的话,调用第二个构造函数
    //自定义属性是从AttributeSet参数传进来
    //这个参数一般是必须重写,因为在LayoutInfaltor中CreateView,系统会通过反射调用该构造函数
    //不重写会报错
    public CarsonView(Context context, AttributeSet attributeSet) {
        this.context = context;
        this.attributeSet = attributeSet;
    }

    //不会自动调用
    //一般是在第二个构造函数中调用第三个构造函数
    //View有style属性时
    public CarsonView(Context context, AttributeSet attributeSet, int defStyleAttr) {
        this.context = context;
        this.attributeSet = attributeSet;
        this.defStyleAttr = defStyleAttr;
    }

    //不会自动调用
    //一般是在第二个构造函数中主动调用
    //API21后使用
    //View有style属性时
    public CarsonView(Context context, AttributeSet attributeSet, int defStyleAttr, int defStyleRes) {
        this.context = context;
        this.attributeSet = attributeSet;
        this.defStyleAttr = defStyleAttr;
        this.defStyleRes = defStyleRes;
    }
}

View视图结构

92093cf6c316dbb57cf42af205aaceb.jpg

  1. PhoneWindow是安卓系统中最基本的窗口系统,继承于Window类,负责管理系统显示以及事物响应。它是Activity与View系统交互的接口
  2. DecorView是PhoneWindow中的起始节点View,继承于View类,是整个视图的容易,用于设置窗口属性。
  3. ViewRoot是在Activity启动时创建,负责管理、布局、渲染窗口UI等。

b3ca62edb6f519f8cefd297d0681fcb.jpg

  • view树的绘制过程 答:由viewroot负责绘制,主要作用是View树的管理者,负责将DecorView和PhoneWindow"组合"起来。而View树的根节点严格来说只有DecorView,每一个DecorView都有ViewRoot与之关联,这种关联被WindowManager管理。
  • View的添加

7c25554b1eccd94975fc54613837461.jpg

  • View的绘制流程

41127d3b4c7722c379c0766709b8d10.jpg

5e213a3c1d6a6cc2a1af2225f0b698b.jpg

Android坐标系 往右为x轴正方向,往下为y轴正方向 View位置描述 四个顶点决定,View的位置是相对于父控件而言的!! Top:子View上边界到达父View上边界的距离 Left:左到左 Bottom:下到上 Right:右到左

位置的获取方式

getLeft();//获取View左上角据父View左侧距离
//在MotionEvent中get()和getRaw()的区别
event.getX();
event.getRawX();

d0019616f8281312fec2ed249d5c7fd.jpg

getMeasureWidth()和getWidth()区别(重点:面试会被问到getMeasureWidth()的底层逻辑及相关) getMeasureWidth()

  1. 在measure()过程结束后获得对应的值
  2. 通过setMeasuredDimension()方法进行设置 getWidth()
  3. 在layout()过程结束后获得对应的值;
  4. 通过视图右边的坐标减去左边的坐标计算出对应的值;

LayoutParams布局参数

  1. 子View通过该参数告诉父容器ViewGroup如何放置自己
  2. 每一个ViewGroup的子类都有自己对应的LayoutParams类 eg.LinearLayout.LayoutParams

MarginLayoutParams

  1. 与外边距有关,比LayoutParams增加了上下左右边距的支持
  2. margin>horizontalMargin和verticalMargin>leftMargin和RightMargin、topMargin和bottomMargin 构造函数,不合法就顺移

ViewGroup

视图组,即多个View组成的ViewFroup,eg.LinearLayout

自定义View的三种方法及自定义属性使用

三种方法:组合控件、继承控件、自绘控件

组合控件(相对简单)

讲系统原有控件进行组合,构成一个新空间

继承控件

适用:在原有系统控件基础上做一些修饰性的修改,而不是大幅度的改变 View、ViewGroup

自绘控件

复杂,所有的绘制逻辑和流程需要自己完成。 若最终为叶子控件,那么直接继承View,若最终为容器控件,那么直接继承ViewGroup。

自定义View中使用自定义属性

在values中编写需要的属性(res/values/下新建资源文件 attrs.xml)

  1. name :名字
  2. format: 属性格式 ps:自定义属性集合中包含多个自定义属性
 1     <!--1.reference:参考某一资源ID-->
 2     <ImageView android:background = "@drawable/图片ID"/>
 3     <!--2. color:颜色值-->
 4     <TextView android:textColor = "#00FF00"/>
 5     <!--3.boolean:布尔值-->
 6     <Button android:focusable = "true"/>
 7     <!--4.dimension:尺寸值-->
 8     <Button android:layout_width = "42dp"/>
 9     <!--5. float:浮点值-->
10     <alpha android:fromAlpha = "1.0"/>
11     <!--6.integer:整型值-->
12     <TextView android:lines="1"/>
13     <!--7.string:字符串-->
14     <TextView android:text = "我是文本"/>
15     <!--8.fraction:百分数-->
16     <rotate android:pivotX = "200%"/>
17     <!--9.enum:枚举值-->
18     <LinearLayout
19         android:orientation = "vertical">
20     </LinearLayout>
21     <!--10.flag:位或运算-->
22     <TextView android:gravity="bottom|left"/>
23     <!--11.混合类型:属性定义时可以指定多种类型值-->
24     <ImageView android:background = "@drawable/图片ID" />
25     <!--或者-->
26     <ImageView android:background = "#00FF00" />