有如下幾個步驟:android
自定義一個CustomView(extends View )類
編寫values/attrs.xml,在其中編寫styleable和item等標籤元素
在佈局文件中CustomView使用自定義的屬性(注意namespace)
在CustomView的構造方法中經過TypedArray獲取
自定義屬性的聲明文件,在res/values 目錄下新建一個 attrs.xml文件
如要用系統定義過的android:text"屬性,不須要寫format
能夠看一下系統中自定義屬性的文件 Sdk/platforms/android-xx/data/res/values/attrs.xmlcanvas
有多個styleable都要用到的共同的屬性,在resources開頭進行定義,後續引用只須要引用名字就能夠了
<?xml version="1.0" encoding="utf-8"?>
<resources>
<attr name="textColor" format="color"/>
<attr name="textSize" format="dimension"/>
1
2
3
4
使用系統原有的屬性時,在前面加上android命名
<declare-styleable name="title_attrs">
<!--尺寸值,格式是dimension-->
<attr name="width" format="dimension"/>
<attr name="height" format="dimension"/>
<attr name="textColor"/>
<attr name="textSize"/>
<!--聲明須要使用系統定義過的text屬性,注意前面須要加上android命名-->
<attr name="android:text"/>
</declare-styleable>app
<declare-styleable name="RectangleView">
<attr name="textColor"/>
<attr name="textSize"/>
<attr name="android:text"/>
</declare-styleable>ide
</resources>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
在佈局文件中對應的去用
首先須要加上一個命名空間xmlns:title="http://schemas.android.com/apk/res-auto"
而後直接利用這個命名空間title設置屬性便可
<com.sky.customapplication.TitleView
android:id="@+id/title_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
title:textColor="#000"
title:textSize="26sp"
android:text="im text">佈局
</com.sky.customapplication.TitleView>
1
2
3
4
5
6
7
8
9
在自定義控件代碼中獲取各屬性
R.styleable.RectangleView是剛剛attrs文件中的namethis
TypedArray array = context.getTheme().obtainStyledAttributes(attrs, R.styleable.RectangleView, defStyleAttr, 0);
//mText = array.getString(R.styleable.RectangleView_android_text);
mTextColor = array.getColor(R.styleable.RectangleView_textColor, Color.BLACK);
mTextSize = array.getDimensionPixelSize(R.styleable.RectangleView_textSize, 40);
array.recycle(); //注意回收
1
2
3
4
5
onDraw()
實現三個構造方法,在第三個構造方法中進行初始化、解析自定義屬性的值
初始化筆刷,mBounds是繪製時控制文本繪製範圍的長方形
public class RectangleView extends View implements View.OnClickListener {spa
private Paint mPaint;
private Rect mBounds;
//private String mText;
private float mTextSize;
private int mTextColor;
private int mCount;
private String text;orm
public RectangleView(Context context) {
this(context, null);
}xml
public RectangleView(Context context, @Nullable AttributeSet attrs) {
this(context, attrs, 0);
}utf-8
public RectangleView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
//新建畫筆
mPaint = new Paint(Paint.ANTI_ALIAS_FLAG); //抗鋸齒
mBounds = new Rect();
//加載自定義屬性集合
TypedArray array = context.getTheme().obtainStyledAttributes(attrs, R.styleable.RectangleView, defStyleAttr, 0);
// 將解析的屬性傳入到畫筆顏色變量當中(本質上是自定義畫筆的顏色)
// 第二個參數是默認設置顏色(即無指定color狀況下使用)
//mText = array.getString(R.styleable.RectangleView_android_text);
mTextColor = array.getColor(R.styleable.RectangleView_textColor, Color.BLACK);
mTextSize = array.getDimensionPixelSize(R.styleable.RectangleView_textSize, 40);
array.recycle(); //記得回收
setOnClickListener(this);
}
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
在onDraw中繪製
@Override
protected void onDraw(Canvas canvas) {
//畫筆顏色
mPaint.setColor(Color.YELLOW);
//畫一個長方形
canvas.drawRect(0, 0, getWidth(), getHeight(), mPaint);
//設置畫字的顏色
mPaint.setColor(mTextColor);
mPaint.setTextSize(mTextSize);
text = String.valueOf(mCount);
mPaint.getTextBounds(text, 0, text.length(), mBounds);
float textWidth = mBounds.width();
float textHeight = mBounds.height();
//文字繪製的起點是從文字的左下角開始的,實際看見文字的Y座標須要加上文字的自身高度
canvas.drawText(text, getWidth()/2 - textWidth/2, getHeight()/2 + textHeight/2, mPaint);
}
@Override
public void onClick(View v) {
mCount++;
invalidate(); //視圖重繪,onDraw調用
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
在佈局中引用
<com.sky.customapplication.RectangleView
android:layout_width="200dp"
android:layout_height="100dp"
title:textColor="#00FF33"
title:textSize="30sp"
android:padding="15dp"
/>
1
2
3
4
5
6
7
手動支持wrap_content屬性可是此時,若是設置layout_width和layout_height 屬性爲 wrap_content,並不會適應自身大小,而是填滿父控件,和match_parent效果相同。這是由於使用系統的onMeasure方法時,系統幫咱們測量的高度和寬度都是MATCH_PARNET,當咱們設置明確的寬度和高度時,系統幫咱們測量的結果就是咱們設置的結果;而當咱們設置爲WRAP_CONTENT系統幫咱們測量的結果也是MATCH_PARENT的長度。因此,當設置了WRAP_CONTENT時,咱們須要本身進行測量,即重寫onMesure方法: