赞
踩
目录
View类是Android中各种组件的基类,如View是ViewGroup基类,表现为显示在屏幕上的各种视图。Android中的UI组件都是由View和ViewGroup组成。
- //如果View在Java代码中是new出来的,就会调用第一个构造函数
- public View(Context context) {
- throw new RuntimeException("Stub!");
- }
-
- //如果View是在.xml里面声明的就会调用第二个构造函数
- public View(Context context, @Nullable AttributeSet attrs) {
- throw new RuntimeException("Stub!");
- }
-
-
- public View(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
- throw new RuntimeException("Stub!");
- }
-
- public View(Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) {
- throw new RuntimeException("Stub!");
- }

AttributeSet与自定义属性:系统自带的View可以在xml中配置属性,对于已经写好的自定义的View同样可以在xml中配置属性,为了使自定义View的属性可以在xml中配置,需要一下四个步骤:
自定义View的最基本的方法是:
onMeasure():测量,决定View的大小;
onLayout():布局,决定View在ViewGroup中的位置
onDraw():绘制,决定绘制这个View;
- @Override
- protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
- ...
- }
首先看一下onMeasure函数的两个参数,widthMeasureSpec和heightMeasureSpec。这两个int型的数据包含了测量模式和尺寸。
- //获取测量模式
- int modeWidth = MeasureSpec.getMode(widthMeasureSpec);
- int modeHeight = MeasureSpec.getMode(heightMeasureSpec);
- //获取尺寸
- int sizeWidth = MeasureSpec.getSize(widthMeasureSpec);
- int sizeHeight = MeasureSpec.getSize(heightMeasureSpec);
什么是测量模式呢?测量模式有三种,分别是UNSPECIFIED/ EXCATLY/ AT_MOST。
测量模式 | 表示意思 |
---|---|
UNSPECIFIED | 父容器没有对当前View有任何限制,当前View可以任意取尺寸 |
EXACTLY | 当前的尺寸就是当前View应该取的尺寸 |
AT_MOST | 当前尺寸是当前View能取的最大尺寸 |
现在重写一个onMeasure函数,实现一个自定义的正方形View。
- //继承View
- public class SquareView extends View {
-
- //1.只有Context的构造函数
- public SquareView(Context context) {
- super(context);
- }
-
- //2.含有Context和AttributeSet的构造函数
- public SquareView(Context context, @Nullable AttributeSet attrs) {
- super(context, attrs);
- }
-
- //3.重写onMesure方法
- @Override
- protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
- super.onMeasure(widthMeasureSpec, heightMeasureSpec);
- int width = getMySize(100,widthMeasureSpec);
- int height = getMySize(100,heightMeasureSpec);
-
- if (width<height){
- height = width;
- }else {
- width = height;
- }
- setMeasuredDimension(width,height);
- }
-
- //根据测量模式
- private int getMySize(int defaultSize, int measureSpec) {
- int mSize = defaultSize;
- int mode = MeasureSpec.getMode(measureSpec);
- int size = MeasureSpec.getSize(measureSpec);
- switch (mode){
- //父容器没有对当前View有任何限制,当前View可以任意取尺寸
- case MeasureSpec.UNSPECIFIED:
- mSize = defaultSize;
- break;
- //View能取得的最大尺寸
- case MeasureSpec.AT_MOST:
- mSize = size;
- break;
- //当前的尺寸就是当前View应该取的尺寸
- case MeasureSpec.EXACTLY:
- mSize = size;
- break;
- }
- return mSize;
- }
- }

- <?xml version="1.0" encoding="utf-8"?>
- <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:tools="http://schemas.android.com/tools"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:orientation="vertical"
- tools:context=".MainActivity">
-
- <TextView
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_margin="15sp"
- android:text="自定义View_SquareView"
- android:textSize="30sp" />
-
- <com.example.appb.SquareView
- android:layout_width="match_parent"
- android:layout_height="100dp"
- android:layout_margin="20dp"
- android:background="@color/purple_200" />
-
- <com.example.appb.SquareView
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_margin="20dp"
- android:background="@color/teal_200" />
-
- </LinearLayout>

运行效果:
Tips:在自定义View的时候,需要两个构造函数。否则在编译的时候会报异常:Binary XML file line Error inflating class. 原因是:Android在根据xml文件夹创建View对象的时候会调用View的双参构造方法,即public SquareView(Context context, AttributeSetattrs),所以如果没有写全的话就会报错。
在onMeasure方法中实现了自定义尺寸大小,在onDraw方法中实现了自定义的绘制View。接下来做一个自定义的圆形View。
- @Override
- protected void onDraw(Canvas canvas) {
- //调用父类的onDraw函数,因为View这个类实现了一些基本的绘制功能,比如绘制背景颜色和背景图片
- super.onDraw(canvas);
- //半径
- int r = getMeasuredWidth()/2;
- //以圆心的横坐标为当前View的左起始位置+半径
- int centerX = getLeft() + r;
- //以圆心的横坐标为当前View的顶部起始位置+半径
- int centerY = getTop() + r;
- Paint paint = new Paint();
- paint.setColor(Color.YELLOW);
- canvas.drawCircle(centerX,centerY,r,paint);
- }
- <com.example.appb.CycloView
- android:layout_width="100dp"
- android:layout_height="wrap_content"
- />
运行效果:
2.3 自定义布局属性
首先需要在res/values/styles.xml文件声明一个自定义属性:
- <resources>
- <!--声明属性集合的名称-->
- <declare-styleable name="CycloView">
- <!--声明属性名称,和取值类型-->
- <attr name="default_size" format="dimension"/>
- </declare-styleable>
- </resources>
布局文件:
- <?xml version="1.0" encoding="utf-8"?>
- <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:hc="http://schemas.android.com/apk/res-auto"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:orientation="vertical">
-
- <com.example.appb.CycloView
- android:layout_width="100dp"
- android:layout_height="wrap_content"
- hc:default_size="100dp"
- />
-
- </LinearLayout>
Tips: 使用自定义属性需要在根标签添加命名空间,命名空间的取值是
"http://schemas.android.com/apk/res-auto"
修改构造函数,读取自定义属性的值:
- import android.content.Context;
- import android.content.res.TypedArray;
- import android.graphics.Canvas;
- import android.graphics.Color;
- import android.graphics.Paint;
- import android.util.AttributeSet;
- import android.view.View;
-
- import androidx.annotation.Nullable;
-
- public class CycloView extends View {
-
- private int defaultSize;
- public CycloView(Context context) {
- super(context);
- }
-
- public CycloView(Context context, @Nullable AttributeSet attrs) {
- super(context, attrs);
- //获取属性集合
- TypedArray typedArray = context.obtainStyledAttributes(attrs,R.styleable.CycloView);
- //获取default_size属性
- defaultSize = typedArray.getDimensionPixelSize(R.styleable.CycloView_default_size,100);
- //将TypedArray回收
- typedArray.recycle();
- }
- @Override
- protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
- super.onMeasure(widthMeasureSpec, heightMeasureSpec);
- int width = getMySize(defaultSize,widthMeasureSpec);
- int height = getMySize(defaultSize,heightMeasureSpec);
-
- if (width<height){
- height = width;
- }else {
- width = height;
- }
- setMeasuredDimension(width,height);
- }
-
- private int getMySize(int defaultSize, int measureSpec) {
- int mSize = defaultSize;
- int mode = MeasureSpec.getMode(measureSpec);
- int size = MeasureSpec.getSize(measureSpec);
- switch (mode){
- case MeasureSpec.UNSPECIFIED:
- mSize = defaultSize;
- break;
- case MeasureSpec.AT_MOST:
- mSize = size;
- break;
- case MeasureSpec.EXACTLY:
- mSize = size;
- break;
- }
- return mSize;
- }
-
- @Override
- protected void onDraw(Canvas canvas) {
- //调用父类的onDraw函数,因为View这个类实现了一些基本的绘制功能,比如绘制背景颜色和背景图片
- super.onDraw(canvas);
- //半径
- int r = getMeasuredWidth()/2;
- //以圆心的横坐标为当前View的左起始位置+半径
- int centerX = getLeft() + r;
- //以圆心的横坐标为当前View的顶部起始位置+半径
- int centerY = getTop() + r;
- Paint paint = new Paint();
- paint.setColor(Color.YELLOW);
- canvas.drawCircle(centerX,centerY,r,paint);
- }
- }

参考文档:
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。