当前位置:   article > 正文

自定义View的三种实现方式及自定义属性使用介绍_自定义view的三个方法

自定义view的三个方法

一 前言

尽管Android系统提供了不少控件,但是有很多酷炫效果仍然是系统原生控件无法实现的。好在Android允许自定义控件,来弥补原生控件的不足。但是在很多初学者看来,自定义View似乎很难掌握。其中有很大一部分原因是我们平时看到的自定义View使用中,有多种形式,有的寥寥数笔,有的逻辑很复杂,有的直接继承View或ViewGroup,有的却直接继承系统的原生控件,有的可以直接使用系统定义的属性,而有的却自定义了自己的属性…所以不明白使用规则的开发者,很容易被这只“纸老虎”吓到。
实际上实现自定义View的方式,从整体上看,只分为三种:组合控件继承控件自绘控件。然后就是根据需要来添加自定义的属性,就这么简单。本文将会针对这4个方面进行详细的讲解。主要内容如下:
在这里插入图片描述

二 三种自定义控件的方法

2.1 组合控件

组合控件,顾名思义,就是将系统原有的控件进行组合,构成一个新的控件。这种方式下,不需要开发者自己去绘制图上显示的内容,也不需要开发者重写onMeasure,onLayout,onDraw方法来实现测量、布局以及draw流程。所以,在实现自定义view的三种方式中,这一种相对比较简单。

实际开发中,标题栏就是一个比较常见的例子。因为在一个app的各个界面中,标题栏基本上是大同小异,复用率很高。所以经常会将标题栏单独做成一个自定义view,在不同的界面直接引入即可,而不用每次都把标题栏布局一遍。本节就自定义一个标题栏,包含标题和返回按钮两个控件,来介绍这种组合控件的实现方式。

1、定义标题栏布局文件
定义标题栏的布局文件custom_title_view.xml,将返回按钮和标题文本进行组合。这一步用于确定标题栏的样子,代码如下所示:

```java
<?xml version="1.0" encoding="utf-8"?>
 2 <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
 3     android:layout_width="match_parent"
 4     android:layout_height="wrap_content"
 5     android:background="@android:color/holo_orange_light">
 6     <Button
 7         android:id="@+id/btn_left"
 8         android:layout_width="wrap_content"
 9         android:layout_height="wrap_content"
10         android:layout_centerVertical="true"
11         android:layout_marginLeft="5dp"
12         android:text="Back"
13         android:textColor="在这里插入代码片@android:color/white" />
14 
15     <TextView
16         android:id="@+id/title_tv"
17         android:layout_width="wrap_content"
18         android:layout_height="wrap_content"
19         android:layout_centerInParent="true"
20         android:text="Title"
21         android:textColor="@android:color/white"
22         android:textSize="20sp" />
23 </RelativeLayout>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24

这个布局很简单,就不多说了。

2、根据给定布局实现自定义View

  public class CustomTitleView extends FrameLayout implements View.OnClickListener {
 2     private View.OnClickListener mLeftOnClickListener;
 3     private Button mBackBtn;
 4     private TextView mTittleView;
 5 
 6     public CustomTitleView(@NonNull Context context, @Nullable AttributeSet attrs) {
 7         super(context, attrs);
 8         LayoutInflater.from(context).inflate(R.layout.custom_title_view, this);
 9         mBackBtn = findViewById(R.id.btn_left);
10         mBackBtn.setOnClickListener(this);
11         mTittleView = findViewById(R.id.title_tv);
12     }
13 
14     @Override
15     public void onClick(View v) {
16         switch (v.getId()) {
17             case R.id.btn_left:
18                 if (mLeftOnClickListener != null) {
19                     mLeftOnClickListener.onClick(v);
20                 }
21                 break;
22         }
23     }
24 
25     public void setLeftOnClickListener(View.OnClickListener leftOnClickListener) {
26         mLeftOnClickListener = leftOnClickListener;
27     }
28 
29     public void setTittle(String title){
30         mTittleView.setText(title);
31     }
32 }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32

为了便于理解和记忆,这里对该部分做一点说明:

  • 代码中对外提供了两个接口,一是动态设置标题,二是使用者可以自定义返回按钮的点击事件。
  • CustomTitleView的构造函数,要选择两个参数的,选择其它参数的构造函数会报错。这一点是笔者开发机测试的结果,暂时不清楚是不是所有手机上都是这样。
  • 这里是继承的FrameLayout,但是继承LinearLayout,RelativeLayout等系统布局控件都可以。之所以要继承这些系统现成的ViewGroup,是因为这样可以不用再重写onMeasure,onLayout等,这样省事很多。由于这里是一个布局控件,要用LayoutInflater来填充,所以需要继承ViewGroup,如果继承View的直接子类,编译会不通过。所以,CustomTitleView自己就是一个容器,完全可以当成容器使用,此时CustomTitleView自身的内容会和其作为父布局添加的子控件,效果会叠加,具体的叠加效果是根据继承的容器特性决定的。

3、在Activity的布局文件中添加CustomTitleView

在Activity的布局文件activity_custom_view_compose_demo.xml中,像使用系统控件一样使用CustomTitleView即可。前说了,CustomTitleView自己就是继承的现成的系统布局,所以它们拥有的属性特性,CustomTitleView一样拥有。

 1 <?xml version="1.0" encoding="utf-8"?>
 2 <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
 3     android:layout_width="match_parent"
 4     android:layout_height="match_parent">
 5     <com.example.demos.customviewdemo.CustomTitleView
 6         android:id="@+id/customview_title"
 7         android:layout_width="match_parent"
 8         android:layout_height="wrap_content">
 9     </com.example.demos.customviewdemo.CustomTitleView>
10 </RelativeLayout>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

4、在Activity中操作CustomTitleView

 public class CustomViewComposeDemoActivity extends AppCompatActivity {
 2 
 3     private CustomTitleView mCustomTitleView;
 4     @Override
 5     protected void onCreate(Bundle savedInstanceState) {
 6         super.onCreate(savedInstanceState);
 7         setContentView(R.layout.activity_custom_view_compose_demo);
 8         mCustomTitleView = findViewById(R.id.customview_title);
 9         mCustomTitleView.setTittle("This is Title");
10         mCustomTitleView.setLeftOnClickListener(new View.OnClickListener() {
11             @Override
12             public void onClick(View v) {
13                 finish();
14             }
15         });
16 
17     }
18 }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18

在第8行中,获取到CustomTitleView实例,第9行设置标题文字,第10行自定义“Back”按钮点击事件。

5、效果图

按照如上的4步,就通过组合控件完成了一个比较简单的自定义标题栏。可见,这种方式是非常简单的。
在这里插入图片描述

2.2 继承控件

通过继承系统控件(View子类控件或ViewGroup子类控件)来完成自定义View,一般是希望在原有系统控件基础上做一些修饰性的修改,而不会做大幅度的改动,如在TextView的文字下方添加下划线,在LinearLayout布局中加一个蒙板等。这种方式往往都会复用系统控件的onMeasure和onLayout方法,而只需要重写onDraw方法,在其中绘制一些需要的内容。下面会分别继承View类控件和ViewGroup类控件来举例说明。

2.2.1 继承View类系统控件

如下示例为在TextView文字下方显示红色下划线,其基本步骤如下:
(1)继承View控件,并重写onDraw方法

1 @SuppressLint("AppCompatCustomView")
 2 public class UnderlineTextView extends TextView{
 3     public UnderlineTextView(Context context, @Nullable AttributeSet attrs) {
 4         super(context, attrs);
 5     }
 6 
 7     @Override
 8     protected void onDraw(Canvas canvas) {
 9         super.onDraw(canvas);
10         Paint paint = new Paint();
11         paint.setColor(Color.RED);
12         paint.setStrokeWidth(5);
13         int width = getWidth();
14         int height = getBaseline();
15         canvas.drawLine(0,height,width,height,paint);
16     }
17 }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

(2)在布局文件中调用
就像使用一个普通TextView一样使用UnderlineTextView。

 1 <?xml version="1.0" encoding="utf-8"?>
 2 <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
 3     android:layout_width="match_parent"
 4     android:layout_height="match_parent">
 5     <com.example.demos.customviewdemo.UnderlineTextView
 6         android:layout_width="wrap_content"
 7         android:layout_height="wrap_content"
 8         android:textSize="50dp"
 9         android:layout_centerInParent="true"
10         android:text="Hello World!"/>
11 </RelativeLayout>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

(3)效果图
在这里插入图片描述

2.2.2 继承ViewGroup类系统控件

如下示例演示,在layout布局上添加一个浅红色的半透明蒙板,这种需求在工作中也是非常常见的。
(1)继承ViewGroup类系统控件

 public class ForegroundLinearLayout extends LinearLayout{
    public ForegroundLinearLayout(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
    }

    @Override
    protected void dispatchDraw(Canvas canvas) {
        super.dispatchDraw(canvas);
        canvas.drawColor(Color.parseColor("#50FF0000"));
    }
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

(2)在布局文件中调用
对ForegroundLinearLayout的使用,就和使用其父类LinearLayout一样。

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
       <com.example.demos.customviewdemo.ForegroundLinearLayout
        android:layout_width="match_parent"
        android:layout_height="200dp"
        android:layout_centerInParent="true"
        android:gravity="center">

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_centerInParent="true"
            android:text="Hello World!"
            android:textColor="@android:color/black"
            android:textSize="50dp" />
    </com.example.demos.customviewdemo.ForegroundLinearLayout>
</RelativeLayout>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18

(3)效果图
在宽为全屏宽度,高为200dp的布局范围内,绘制完子其子控件TextView后,在上面覆盖了一层浅红色的半透明蒙板。
在这里插入图片描述

从上面两个例子可见,继承系统原有的控件来实现自定义View,步骤非常简单,比组合控件简单多了。但是这一节需要对Canvas,paint等绘制方面的知识有一定的了解,且还需要对ViewGroup的中内容的绘制顺序有一定的了解,才能在原生控件的基础上做出想要的效果来。

2.3 自绘控件

这三种方法中,自绘控件是最复杂的,因为所有的绘制逻辑和流程都需要自己完成。采用自绘控件这种方式时,如果自定义View为最终的叶子控件,那么需要直接继承View;而不过自定义View为容器类控件,则需要直接继承ViewGroup。这里依然针对直接继承View和ViewGroup分别举例进行说明。

2.3.1 自绘叶子View控件

这里通过画一个直方图来展示自绘View控件的实现。

(1)直接继承View类

自绘叶子View控件时,最主要工作就是绘制出丰富的内容,这一过程是在重写的onDraw方法中实现的。由于是叶子view,它没有子控件了,所以重写onLayout没有意义。onMeasure的方法可以根据自己的需要来决定是否需要重写,很多情况下,不重写该方法并不影响正常的绘制。

 1 public class HistogramView extends View{
 2 
 3     private Paint mPaint;
 4     private Path mPath;
 5 
 6     public HistogramView(Context context, @Nullable AttributeSet attrs) {
 7         super(context, attrs);
 8         mPaint = new Paint();
 9         mPath = new Path();
10     }
11 
12     @Override
13     protected void onDraw(Canvas canvas) {
14         super.onDraw(canvas);
15         //绘制坐标轴
16         mPaint.reset();
17         mPath.reset();
18         mPaint.setColor(Color.BLACK);
19         mPaint.setStyle(Paint.Style.STROKE);
20         mPath.moveTo(100,100);
21         mPath.rLineTo(0,402);
22         mPath.rLineTo(800,0);
23         canvas.drawPath(mPath,mPaint);
24         //绘制文字
25         mPaint.reset();
26         mPaint.setTextSize(30);
27         mPaint.setStyle(Paint.Style.FILL);
28         canvas.drawText("Froyo",160,540,mPaint);
29         canvas.drawText("CB",280,540,mPaint);
30         canvas.drawText("ICS",380,540,mPaint);
31         canvas.drawText("J",480,540,mPaint);
32         canvas.drawText("KitKat",560,540,mPaint);
33         canvas.drawText("L",690,540,mPaint);
34         canvas.drawText("M",790,540,mPaint);
35         //绘制直方图,柱形图是用较粗的直线来实现的
36         mPaint.reset();
37         mPaint.setColor(Color.GREEN);
38         mPaint.setStrokeWidth(80);
39         float[] lines3={
40                 200,500,200,495,
41                 300,500,300,480,
42                 400,500,400,480,
43                 500,500,500,300,
44                 600,500,600,200,
45                 700,500,700,150,
46                 800,500,800,350,
47         };
48         canvas.drawLines(lines3,mPaint);
49     }
50 }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50

(2)在Activity界面的布局文件中引入

和其它自定义控件一样,直接在布局文件中引入即可。

 1 <?xml version="1.0" encoding="utf-8"?>
 2 <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
 3     android:layout_width="match_parent"
 4     android:layout_height="match_parent">
 5 
 6     <com.example.demos.customviewdemo.HistogramView
 7         android:layout_width="match_parent"
 8         android:layout_height="wrap_content"/>
 9 
10 </RelativeLayout>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

(3)效果图

在这里插入图片描述

2.3.2 自绘ViewGroup控件

这里通过自定义一个父布局控件,并添加一个子view来作为例子讲解该方法的实现。

(1)直接继承ViewGroup类

自绘ViewGroup控件,需要直接继承ViewGroup,在该系列第一篇文章中将绘制流程的时候就讲过,onLayout是ViewGroup中的抽象方法,其直接继承者必须实现该方法。所以这里,onLayout方法必须要实现的,如果这里面的方法体为空,那该控件的子view就无法显示了。要想准确测量,onMeasure方法也是要重写的。下面例子中,只演示了第一个子view的测量和布局,onLayout方法中的child.layout,就完成了对子view的布局。

 1 public class CustomLayout extends ViewGroup {
 2     public CustomLayout(Context context, AttributeSet attrs) {
 3         super(context, attrs);
 4     }
 5 
 6     @Override
 7     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
 8         super.onMeasure(widthMeasureSpec, heightMeasureSpec);
 9         if (getChildCount() > 0) {
10             //只测量第一个child
11             View child = getChildAt(0);
12             measureChild(child, widthMeasureSpec, heightMeasureSpec);
13         }
14     }
15 
16     @Override
17     protected void onLayout(boolean changed, int l, int t, int r, int b) {
18         if (getChildCount() > 0) {
19             //只布局第一个child
20             View child = getChildAt(0);
21             child.layout(0, 0, child.getMeasuredWidth(), child.getMeasuredHeight());
22         }
23     }
24 }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24

(2)在布局文件中和普通父布局一样被引入

 1 <?xml version="1.0" encoding="utf-8"?>
 2 <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
 3     android:layout_width="match_parent"
 4     android:layout_height="match_parent">
 5 
 6     <com.example.demos.customviewdemo.CustomLayout
 7         android:layout_width="match_parent"
 8         android:layout_centerInParent="true"
 9         android:layout_height="wrap_content">
10         <TextView
11             android:layout_width="wrap_content"
12             android:layout_height="wrap_content"
13             android:text="Hello World!"
14             android:textSize="50dp"/>
15 
16     </com.example.demos.customviewdemo.CustomLayout>
17 
18 </RelativeLayout>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18

(3)效果图

在这里插入图片描述

上述代码中第8行没有起效,从布局上看TextView应该是处于屏幕的正中央,但是实际结果却还是在左上方显示。这是因为CustomLayout控件,并没有实现“android:layout_centerInParent”这个属性,所以是无效的。关于属性的问题,正是下一节要介绍的内容。

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

我们在使用Android原生控件的时候,经常可以看到在布局文件中可以设置很多的属性值,如

1 <TextView
2         android:id="@+id/title_tv"
3         android:layout_width="wrap_content"
4         android:layout_height="wrap_content"
5         android:layout_centerInParent="true"
6         android:text="Title"
7         android:textColor="@android:color/white"
8         android:textSize="20sp" />
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

这里可以根据需要随时设置TextView要显示的文字,文字颜色,文字大小等各种属性,给使用者带来了极大的方便。我们在使用自定义View的时候,也非常希望能够像TextView等系统原生控件一样通过设置属性值来个性化自定义View。本节咱们在上一节自定义直方图的基础上,来介绍自定义属性的基本使用流程。

1、在values中编写需要的属性

在res/values/下新建资源文件,这里咱们命名为attrs.xml,在其中编写所需要的属性

1 <?xml version="1.0" encoding="utf-8"?>
2 <resources>
3     <declare-styleable name="HistogramView">
4         <attr name="textColor" format="color"/>
5         <attr name="histogramColor" format="color"/>
6     </declare-styleable>
7 </resources>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

这里中的name是自行命名的,可以理解为这个自定义属性集合的名称。代码中包含了两个自定义属性,名称分别为“textColor”和“histogramColor”,这里用来设置直方图中文字的颜色和直方图的颜色。format表示的是属性的格式,这里均设置为“color”,表示对应的属性是用来设置颜色值的。对于“format”,后面还会详细讲到。其它的就是固定的格式了,直接套用就行。

2、在自定义View中引入属性

 1 public class HistogramView extends View{
 2 
 3     private Paint mPaint;
 4     private Path mPath;
 5     private int mTextColor,mHistogramColor;
 6 
 7     public HistogramView(Context context, @Nullable AttributeSet attrs) {
 8         super(context, attrs);
 9         mPaint = new Paint();
10         mPath = new Path();
11         initAttrs(context,attrs);
12     }
13 
14 /*           新增代码                               */
15     private void initAttrs(Context context, AttributeSet attrs){
16         TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.HistogramView);
17         mTextColor = typedArray.getColor(R.styleable.HistogramView_textColor,Color.BLACK);
18         mHistogramColor = typedArray.getColor(R.styleable.HistogramView_histogramColor,Color.GREEN);
19         typedArray.recycle();
20     }
21 
22     @Override
23     protected void onDraw(Canvas canvas) {
24         super.onDraw(canvas);
25         //绘制坐标轴
26         mPaint.reset();
27         mPath.reset();
28         mPaint.setColor(Color.BLACK);
29         mPaint.setStyle(Paint.Style.STROKE);
30         mPath.moveTo(100,100);
31         mPath.rLineTo(0,402);
32         mPath.rLineTo(800,0);
33         canvas.drawPath(mPath,mPaint);
34         //绘制文字
35         mPaint.reset();
36         mPaint.setTextSize(30);
37         mPaint.setColor(mTextColor);
38         mPaint.setStyle(Paint.Style.FILL);
39         canvas.drawText("Froyo",160,540,mPaint);
40         canvas.drawText("CB",280,540,mPaint);
41         canvas.drawText("ICS",380,540,mPaint);
42         canvas.drawText("J",480,540,mPaint);
43         canvas.drawText("KitKat",560,540,mPaint);
44         canvas.drawText("L",690,540,mPaint);
45         canvas.drawText("M",790,540,mPaint);
46         //绘制直方图,柱形图是用较粗的直线来实现的
47         mPaint.reset();
48         mPaint.setColor(mHistogramColor);
49         mPaint.setStrokeWidth(80);
50         float[] lines3={
51                 200,500,200,495,
52                 300,500,300,480,
53                 400,500,400,480,
54                 500,500,500,300,
55                 600,500,600,200,
56                 700,500,700,150,
57                 800,500,800,350,
58         };
59         canvas.drawLines(lines3,mPaint);
60     }
61 }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61

将上述代码和前面第三节中自绘直方图代码对比,红色部分是修改或新增的代码。第15~20行代码就是初始化属性的地方,这个过程需要在构造函数中完成。其中,第16行是和自定义属性集建立联系,第17和18行是获取开发者在布局文件中使用时设置的相应属性值,如果没有设置,则会使用默认设置的颜色,分别为Color.BLACK和Color.GREEN。这里注意第19行,用完后一定要回收资源。这样就初始化了文字颜色mTextColor值和mHistogramColor值,在后面onDraw中就使用该值来绘制对应的部分。

3、在布局文件中设置属性值

 1 <?xml version="1.0" encoding="utf-8"?>
 2 <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
 3     xmlns:app="http://schemas.android.com/apk/res-auto"
 4     android:layout_width="match_parent"
 5     android:layout_height="match_parent">
 6 
 7     <com.example.demos.customviewdemo.HistogramView
 8         android:layout_width="match_parent"
 9         android:layout_height="wrap_content"
10         app:textColor="@android:color/holo_red_light"
11         app:histogramColor="@android:color/holo_blue_bright"/>
12 
13 </RelativeLayout>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

这段代码中,第3,10,11行和以往的布局文件有些不一样,这是使用自定义属性时的固定格式。第3行中,如果布局文件中没有这一句,一定要加上,这句是声明命名空间,只有声明了命名空间才能使用自定义属性。“app”是该命名空间的名称,这里是自行命名的,不一定非要用“app”。第10行和11行,“app:attrName”表示用的是自定义的属性,固定用法,前面mTextColor和mHistogramColor值就是从这里获取的。

还记得上一节结尾处说“android:layout_centerInParent”没有起效吗?现在我们知道,这里设置的属性,是在自定义View代码中获取该值,根据该值来确定显示效果的。“android:layout_centerInParent”的值在View的源码中没有被使用,所以设置后也就无效了。“android:layout_width”和“android:layout_height”属性是必须要有的,所有的View的实现都要用到这两个属性来确定宽高。现在,咱们应该明白,设置控件属性值是如何起作用的了吧。

4、效果图

在这里插入图片描述

四 自定义属性格式汇总

在上面一节中,仅仅只是对文字颜色和直方图颜色的属性值做了设置,是为了演示自定义属性的使用步骤。在实际开发中,完全可以定义更多类型的属性,如显示文字的内容,文字的大小,直方图的宽度等。format也不只限定于“color”,还有“String”,“Integer”等,多种多样。本节就汇总一下平时比较常用的一些属性format。

本节主要参考了如下文章:【Android自定义View全解】

1、常用的11种format类型

 1 <?xml version="1.0" encoding="utf-8"?>
 2 <resources>
 3     <declare-styleable name="SelfAttr">
 4         <!--1.reference:参考某一资源ID-->
 5         <attr name="background" format="reference" />
 6         <!--2. color:颜色值-->
 7         <attr name = "textColor" format = "color" />
 8         <!--3.boolean:布尔值-->
 9         <attr name = "focusable" format = "boolean" />
10         <!--4.dimension:尺寸值-->
11         <attr name = "layout_width" format = "dimension" />
12         <!--5. float:浮点值-->
13         <attr name = "fromAlpha" format = "float" />
14         <!--6.integer:整型值-->
15         <attr name = "lines" format="integer" />
16         <!--7.string:字符串-->
17         <attr name = "text" format = "string" />
18         <!--8.fraction:百分数-->
19         <attr name = "pivotX" format = "fraction" />
20         <!--9.enum:枚举值。属性值只能选择枚举值中的一个-->
21         <attr name="orientation">
22             <enum name="horizontal" value="0" />
23             <enum name="vertical" value="1" />
24         </attr>
25         <!--10.flag:位或运算。属性值可以选择其中多个值-->
26         <attr name="gravity">
27             <flag name="top" value="0x01" />
28             <flag name="bottom" value="0x02" />
29             <flag name="left" value="0x04" />
30             <flag name="right" value="0x08" />
31             <flag name="center_vertical" value="0x16" />
32             ...
33         </attr>
34         <!--11.混合类型:属性定义时可以指定多种类型值-->
35         <attr name = "background_2" format = "reference|color" />
36     </declare-styleable>
37 </resources>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37

如上列出了平时工作中在常见的11种类型的格式,说是11种,但最后一种是前面10种的组合而已。看到上述的属性名称应该很熟悉吧,都是系统原生控件的属性名称。

2、使用举例
如下对上述属性的使用一一举例演示,可以对照着来理解,都是平时常用的系统控件。

 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" />
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26

结语

关于自定义View的3中实现方式以及自定义属性的使用,这里就讲完了。读完后,是不是发现基本的实现流程其实非常简单呢?当然,本文为了说明实现流程,所以举的例子都比较简单,但不是说绘制内容也一样简单。就好像办理入学手续很简单,但读书这件事却不那么容易一样。要完成一些酷炫的自定义View,还需要好好地掌握Canvas,Paint,Path等工具的使用,以及View的绘制流程原理。当然,本文肯定有很多描述不妥或不准确的地方,欢迎来拍砖。

声明:本文内容由网友自发贡献,转载请注明出处:【wpsshop博客】
推荐阅读
相关标签
  

闽ICP备14008679号