• 首页 首页 icon
  • 工具库 工具库 icon
    • IP查询 IP查询 icon
  • 内容库 内容库 icon
    • 快讯库 快讯库 icon
    • 精品库 精品库 icon
    • 问答库 问答库 icon
  • 更多 更多 icon
    • 服务条款 服务条款 icon

Android开发:自定义View

武飞扬头像
小树熊
帮助2

目录

一、View的简介

1.1 View的构造函数

1.2 View的绘制流程图

二、自定义View

2.1 onMeasure()方法

2.2 OnDraw()方法


一、View的简介

View类是Android中各种组件的基类,如View是ViewGroup基类,表现为显示在屏幕上的各种视图。Android中的UI组件都是由View和ViewGroup组成。

1.1 View的构造函数

  1.  
    //如果View在Java代码中是new出来的,就会调用第一个构造函数
  2.  
    public View(Context context) {
  3.  
    throw new RuntimeException("Stub!");
  4.  
    }
  5.  
     
  6.  
    //如果View是在.xml里面声明的就会调用第二个构造函数
  7.  
    public View(Context context, @Nullable AttributeSet attrs) {
  8.  
    throw new RuntimeException("Stub!");
  9.  
    }
  10.  
     
  11.  
     
  12.  
    public View(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
  13.  
    throw new RuntimeException("Stub!");
  14.  
    }
  15.  
     
  16.  
    public View(Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) {
  17.  
    throw new RuntimeException("Stub!");
  18.  
    }
学新通

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

  1. 通过<declare-styleable>为自定义View添加属性
  2. 在xml中为相应的属性生命属性值
  3. 在运行时获取属性值
  4. 将获取的属性值应用到View

1.2 View的绘制流程图

学新通

二、自定义View

自定义View的最基本的方法是:

onMeasure():测量,决定View的大小;

onLayout():布局,决定View在ViewGroup中的位置

onDraw():绘制,决定绘制这个View;

2.1 onMeasure()方法

  1.  
    @Override
  2.  
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
  3.  
    ...
  4.  
    }

首先看一下onMeasure函数的两个参数,widthMeasureSpec和heightMeasureSpec。这两个int型的数据包含了测量模式和尺寸。

  1.  
    //获取测量模式
  2.  
    int modeWidth = MeasureSpec.getMode(widthMeasureSpec);
  3.  
    int modeHeight = MeasureSpec.getMode(heightMeasureSpec);
  4.  
    //获取尺寸
  5.  
    int sizeWidth = MeasureSpec.getSize(widthMeasureSpec);
  6.  
    int sizeHeight = MeasureSpec.getSize(heightMeasureSpec);

什么是测量模式呢?测量模式有三种,分别是UNSPECIFIED/ EXCATLY/ AT_MOST。

测量模式 表示意思
UNSPECIFIED 父容器没有对当前View有任何限制,当前View可以任意取尺寸
EXACTLY 当前的尺寸就是当前View应该取的尺寸
AT_MOST 当前尺寸是当前View能取的最大尺寸

现在重写一个onMeasure函数,实现一个自定义的正方形View。

  1.  
    //继承View
  2.  
    public class SquareView extends View {
  3.  
     
  4.  
    //1.只有Context的构造函数
  5.  
    public SquareView(Context context) {
  6.  
    super(context);
  7.  
    }
  8.  
     
  9.  
    //2.含有Context和AttributeSet的构造函数
  10.  
    public SquareView(Context context, @Nullable AttributeSet attrs) {
  11.  
    super(context, attrs);
  12.  
    }
  13.  
     
  14.  
    //3.重写onMesure方法
  15.  
    @Override
  16.  
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
  17.  
    super.onMeasure(widthMeasureSpec, heightMeasureSpec);
  18.  
    int width = getMySize(100,widthMeasureSpec);
  19.  
    int height = getMySize(100,heightMeasureSpec);
  20.  
     
  21.  
    if (width<height){
  22.  
    height = width;
  23.  
    }else {
  24.  
    width = height;
  25.  
    }
  26.  
    setMeasuredDimension(width,height);
  27.  
    }
  28.  
     
  29.  
    //根据测量模式
  30.  
    private int getMySize(int defaultSize, int measureSpec) {
  31.  
    int mSize = defaultSize;
  32.  
    int mode = MeasureSpec.getMode(measureSpec);
  33.  
    int size = MeasureSpec.getSize(measureSpec);
  34.  
    switch (mode){
  35.  
    //父容器没有对当前View有任何限制,当前View可以任意取尺寸
  36.  
    case MeasureSpec.UNSPECIFIED:
  37.  
    mSize = defaultSize;
  38.  
    break;
  39.  
    //View能取得的最大尺寸
  40.  
    case MeasureSpec.AT_MOST:
  41.  
    mSize = size;
  42.  
    break;
  43.  
    //当前的尺寸就是当前View应该取的尺寸
  44.  
    case MeasureSpec.EXACTLY:
  45.  
    mSize = size;
  46.  
    break;
  47.  
    }
  48.  
    return mSize;
  49.  
    }
  50.  
    }
学新通
  1.  
    <?xml version="1.0" encoding="utf-8"?>
  2.  
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
  3.  
    xmlns:tools="http://schemas.android.com/tools"
  4.  
    android:layout_width="match_parent"
  5.  
    android:layout_height="match_parent"
  6.  
    android:orientation="vertical"
  7.  
    tools:context=".MainActivity">
  8.  
     
  9.  
    <TextView
  10.  
    android:layout_width="wrap_content"
  11.  
    android:layout_height="wrap_content"
  12.  
    android:layout_margin="15sp"
  13.  
    android:text="自定义View_SquareView"
  14.  
    android:textSize="30sp" />
  15.  
     
  16.  
    <com.example.appb.SquareView
  17.  
    android:layout_width="match_parent"
  18.  
    android:layout_height="100dp"
  19.  
    android:layout_margin="20dp"
  20.  
    android:background="@color/purple_200" />
  21.  
     
  22.  
    <com.example.appb.SquareView
  23.  
    android:layout_width="wrap_content"
  24.  
    android:layout_height="wrap_content"
  25.  
    android:layout_margin="20dp"
  26.  
    android:background="@color/teal_200" />
  27.  
     
  28.  
    </LinearLayout>
学新通

运行效果:

学新通

        Tips:在自定义View的时候,需要两个构造函数。否则在编译的时候会报异常:Binary XML file line Error inflating class. 原因是:Android在根据xml文件夹创建View对象的时候会调用View的双参构造方法,即public SquareView(Context context, AttributeSetattrs),所以如果没有写全的话就会报错。

2.2 OnDraw()方法

在onMeasure方法中实现了自定义尺寸大小,在onDraw方法中实现了自定义的绘制View。接下来做一个自定义的圆形View。

  1.  
    @Override
  2.  
    protected void onDraw(Canvas canvas) {
  3.  
    //调用父类的onDraw函数,因为View这个类实现了一些基本的绘制功能,比如绘制背景颜色和背景图片
  4.  
    super.onDraw(canvas);
  5.  
    //半径
  6.  
    int r = getMeasuredWidth()/2;
  7.  
    //以圆心的横坐标为当前View的左起始位置 半径
  8.  
    int centerX = getLeft() r;
  9.  
    //以圆心的横坐标为当前View的顶部起始位置 半径
  10.  
    int centerY = getTop() r;
  11.  
    Paint paint = new Paint();
  12.  
    paint.setColor(Color.YELLOW);
  13.  
    canvas.drawCircle(centerX,centerY,r,paint);
  14.  
    }
  1.  
    <com.example.appb.CycloView
  2.  
    android:layout_width="100dp"
  3.  
    android:layout_height="wrap_content"
  4.  
    />

运行效果:

学新通

 2.3 自定义布局属性

首先需要在res/values/styles.xml文件声明一个自定义属性:

  1.  
    <resources>
  2.  
    <!--声明属性集合的名称-->
  3.  
    <declare-styleable name="CycloView">
  4.  
    <!--声明属性名称,和取值类型-->
  5.  
    <attr name="default_size" format="dimension"/>
  6.  
    </declare-styleable>
  7.  
    </resources>

布局文件:

  1.  
    <?xml version="1.0" encoding="utf-8"?>
  2.  
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
  3.  
    xmlns:hc="http://schemas.android.com/apk/res-auto"
  4.  
    android:layout_width="match_parent"
  5.  
    android:layout_height="match_parent"
  6.  
    android:orientation="vertical">
  7.  
     
  8.  
    <com.example.appb.CycloView
  9.  
    android:layout_width="100dp"
  10.  
    android:layout_height="wrap_content"
  11.  
    hc:default_size="100dp"
  12.  
    />
  13.  
     
  14.  
    </LinearLayout>

Tips: 使用自定义属性需要在根标签添加命名空间,命名空间的取值是

"http://schemas.android.com/apk/res-auto"

修改构造函数,读取自定义属性的值:

  1.  
    import android.content.Context;
  2.  
    import android.content.res.TypedArray;
  3.  
    import android.graphics.Canvas;
  4.  
    import android.graphics.Color;
  5.  
    import android.graphics.Paint;
  6.  
    import android.util.AttributeSet;
  7.  
    import android.view.View;
  8.  
     
  9.  
    import androidx.annotation.Nullable;
  10.  
     
  11.  
    public class CycloView extends View {
  12.  
     
  13.  
    private int defaultSize;
  14.  
    public CycloView(Context context) {
  15.  
    super(context);
  16.  
    }
  17.  
     
  18.  
    public CycloView(Context context, @Nullable AttributeSet attrs) {
  19.  
    super(context, attrs);
  20.  
    //获取属性集合
  21.  
    TypedArray typedArray = context.obtainStyledAttributes(attrs,R.styleable.CycloView);
  22.  
    //获取default_size属性
  23.  
    defaultSize = typedArray.getDimensionPixelSize(R.styleable.CycloView_default_size,100);
  24.  
    //将TypedArray回收
  25.  
    typedArray.recycle();
  26.  
    }
  27.  
    @Override
  28.  
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
  29.  
    super.onMeasure(widthMeasureSpec, heightMeasureSpec);
  30.  
    int width = getMySize(defaultSize,widthMeasureSpec);
  31.  
    int height = getMySize(defaultSize,heightMeasureSpec);
  32.  
     
  33.  
    if (width<height){
  34.  
    height = width;
  35.  
    }else {
  36.  
    width = height;
  37.  
    }
  38.  
    setMeasuredDimension(width,height);
  39.  
    }
  40.  
     
  41.  
    private int getMySize(int defaultSize, int measureSpec) {
  42.  
    int mSize = defaultSize;
  43.  
    int mode = MeasureSpec.getMode(measureSpec);
  44.  
    int size = MeasureSpec.getSize(measureSpec);
  45.  
    switch (mode){
  46.  
    case MeasureSpec.UNSPECIFIED:
  47.  
    mSize = defaultSize;
  48.  
    break;
  49.  
    case MeasureSpec.AT_MOST:
  50.  
    mSize = size;
  51.  
    break;
  52.  
    case MeasureSpec.EXACTLY:
  53.  
    mSize = size;
  54.  
    break;
  55.  
    }
  56.  
    return mSize;
  57.  
    }
  58.  
     
  59.  
    @Override
  60.  
    protected void onDraw(Canvas canvas) {
  61.  
    //调用父类的onDraw函数,因为View这个类实现了一些基本的绘制功能,比如绘制背景颜色和背景图片
  62.  
    super.onDraw(canvas);
  63.  
    //半径
  64.  
    int r = getMeasuredWidth()/2;
  65.  
    //以圆心的横坐标为当前View的左起始位置 半径
  66.  
    int centerX = getLeft() r;
  67.  
    //以圆心的横坐标为当前View的顶部起始位置 半径
  68.  
    int centerY = getTop() r;
  69.  
    Paint paint = new Paint();
  70.  
    paint.setColor(Color.YELLOW);
  71.  
    canvas.drawCircle(centerX,centerY,r,paint);
  72.  
    }
  73.  
    }
学新通

参考文档:

自定义View(一) - 简书 (jianshu.com)

这篇好文章是转载于:学新通技术网

  • 版权申明: 本站部分内容来自互联网,仅供学习及演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,请提供相关证据及您的身份证明,我们将在收到邮件后48小时内删除。
  • 本站站名: 学新通技术网
  • 本文地址: /boutique/detail/tanhgcaeec
系列文章
更多 icon
同类精品
更多 icon
继续加载