前言
在Android應(yīng)用開發(fā)過程中井辜,固定的一些控件和屬性可能滿足不了開發(fā)的需求绎谦,所以在一些特殊情況下,我們需要自定義控件與屬性粥脚,同樣窃肠,這也是面試中面試官問的幾率比較高的問題,也是由初級工程師通向中高級工程師必備的刷允。
實現(xiàn)步驟
- 繼承View類或其子類
- 復(fù)寫view中的一些函數(shù)
- 為自定義View類增加屬性(兩種方式)
- 繪制控件(導(dǎo)入布局)
- 響應(yīng)用戶事件
- 定義回調(diào)函數(shù)(根據(jù)自己需求來選擇)
哪些方法需要被重寫
- onDraw()
view中onDraw()是個空函數(shù)冤留,也就是說具體的視圖都要覆寫該函數(shù)來實現(xiàn)自己的繪制。對于ViewGroup則不需要實現(xiàn)該函數(shù)树灶,因為作為容器是“沒有內(nèi)容“的(但必須實現(xiàn)dispatchDraw()函數(shù)纤怒,告訴子view繪制自己)。 - onLayout()
主要是為viewGroup類型布局子視圖用的天通,在View中這個函數(shù)為空函數(shù)泊窘。 - onMeasure()
用于計算視圖大小(即長和寬)的方式,并通過setMeasuredDimension (width, height) 保存計算結(jié)果烘豹。 - onTouchEvent()
定義觸屏事件來響應(yīng)用戶操作瓜贾。
還有一些可以重寫的方法
1.onKeyDown() 當(dāng)按下某個鍵盤時
2.onKeyUp() 當(dāng)松開某個鍵盤時
3.onTrackballEvent() 當(dāng)發(fā)生軌跡球事件時
4.onSizeChange() 當(dāng)該組件的大小被改變時
5.onFinishInflate() 回調(diào)方法,當(dāng)應(yīng)用從XML加載該組件并用它構(gòu)建界面之后調(diào)用的方法 6.onWindowFocusChanged(boolean) 當(dāng)該組件得到携悯、失去焦點(diǎn)時
7.onAttachedToWindow() 當(dāng)把該組件放入到某個窗口時
8.onDetachedFromWindow() 當(dāng)把該組件從某個窗口上分離時觸發(fā)的方法
9.onWindowVisibilityChanged(int) 當(dāng)包含該組件的窗口的可見性發(fā)生改變時觸發(fā)的方法
View的繪制過程
自定義控件的三種方式
1.繼承已有的控件 當(dāng)要實現(xiàn)的控件和已有的控件在很多方面比較類似, 通過對已有控件的擴(kuò)展來滿足要求阐虚。
2.繼承一個布局文件 一般用于自定義組合控件,在構(gòu)造函數(shù)中通過inflater和addView()方法加載自定義控件的布局文件形成圖形界面(不需要onDraw方法)蚌卤。
3.繼承view 通過onDraw方法來繪制出組件界面。
自定義屬性的兩種方法
- 在布局文件中直接加入屬性奥秆,在構(gòu)造函數(shù)中去獲得逊彭。
布局文件:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
>
<com.lee.demo.initView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
inittext="@string/hello_world"
/>
</RelativeLayout>
在構(gòu)造方法中
public initView(Context context, AttributeSet attrs) {
super(context, attrs);
// TODO Auto-generated constructor stub
int textId = attrs.getAttributeResourceValue(null, "inittext", 0);
String text = context.getResources().getText(textId).toString();
}
- 在res/values/ 下建立一個attrs.xml 來聲明自定義view的屬性。
可以定義的屬性有:
<declare-styleable name = "名稱">
//參考某一資源ID (name可以隨便命名)
<attr name = "background" format = "reference" />
//顏色值
<attr name = "textColor" format = "color" />
//布爾值
<attr name = "focusable" format = "boolean" />
//尺寸值
<attr name = "layout_width" format = "dimension" />
//浮點(diǎn)值
<attr name = "fromAlpha" format = "float" />
//整型值
<attr name = "frameDuration" format="integer" />
//字符串
<attr name = "text" format = "string" />
//百分?jǐn)?shù)
<attr name = "pivotX" format = "fraction" />
//枚舉值
<attr name="orientation"> <enum name="horizontal" value="0" />
<enum name="vertical" value="1" />
</attr>
//位或運(yùn)算
<attr name="windowSoftInputMode">
<flag name = "stateUnspecified" value = "0" />
<flag name = "stateUnchanged" value = "1" />
</attr>
//多類型
<attr name = "background" format = "reference|color" />
</declare-styleable>
- attrs.xml進(jìn)行屬性聲明
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="myView">
<attr name="text" format="string"/>
<attr name="textColor" format="color"/>
</declare-styleable>
</resources>
- 添加到布局文件
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
xmlns:myview="http://schemas.android.com/apk/com.example.demo"
>
<com.lee.demo.initView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
myview:text = "test"
myview:textColor ="#ff0000"
/>
</RelativeLayout>
這里注意命名空間:
xmlns:前綴="http://schemas.android.com/apk/res/包名(或res-auto)"构订,一般AS會自動生成在構(gòu)造函數(shù)中獲取屬性值
public initView(Context context, AttributeSet attrs) {
super(context, attrs);
// TODO Auto-generated constructor stub
TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.myView);
String text = a.getString(R.styleable.myView_text);
int textColor = a.getColor(R.styleable.myView_textColor, Color.WHITE);
a.recycle();
}
或者:
public initView(Context context, AttributeSet attrs) {
super(context, attrs);
// TODO Auto-generated constructor stub
TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.myView);
int n = a.getIndexCount();
for(int i=0;i<n;i++){
int attr = a.getIndex(i);
switch (attr) {
case R.styleable.myView_text:
break;
case R.styleable.myView_textColor:
break;
}
}
a.recycle();
}
自定義隨手指移動的小球來學(xué)習(xí)自定義View
實現(xiàn)上面的效果我們大致需要分成這幾步:
1.在res/values/ 下建立一個attrs.xml 來聲明自定義view的屬性
2.一個繼承View并復(fù)寫部分函數(shù)的自定義view的類
3.一個展示自定義view 的容器界面
1.自定義view命名為cicleview侮叮,它有一個屬性值,格式為color:
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="cicleview">
<attr name="TextColor" format="color"/>
</declare-styleable>
</resources>
2.在構(gòu)造函數(shù)獲取獲得view的屬性配置和復(fù)寫onDraw和onTouchEvent函數(shù)實現(xiàn)繪制界面和用戶事件響應(yīng)悼瘾。
public class cicleview extends View{
//定義畫筆和初始位置
Paint p = new Paint();
public float currentX = 50;
public float currentY = 50;
public int textColor;
public cicleview(Context context, AttributeSet attrs) {
super(context, attrs);
//獲取資源文件里面的屬性囊榜,由于這里只有一個屬性值,不用遍歷數(shù)組亥宿,直接通過R文件拿出color值
//把屬性放在資源文件里卸勺,方便設(shè)置和復(fù)用
TypedArray array = context.obtainStyledAttributes(attrs,R.styleable.myView);
textColor = array.getColor(R.styleable.myView_TextColor,Color.BLACK);
array.recycle();
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
//畫一個藍(lán)色的圓形
p.setColor(Color.BLUE);
canvas.drawCircle(currentX,currentY,30,p);
//設(shè)置文字和顏色,這里的顏色是資源文件values里面的值
p.setColor(textColor);
canvas.drawText("BY finch",currentX-30,currentY+50,p);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
currentX = event.getX();
currentY = event.getY();
invalidate();//重新繪制圖形
return true;
}
}
這里通過不斷的更新當(dāng)前位置坐標(biāo)和重新繪制圖形實現(xiàn)效果烫扼,要注意的是使用TypedArray后一定要記得recycle(). 否則會對下次調(diào)用產(chǎn)生影響曙求。
3.把cicleview加入到activity_main.xml布局里面
<?xml version="1.0" encoding="utf-8"?><RelativeLayout 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"
xmlns:myview="http://schemas.android.com/apk/res-auto"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context="finch.scu.cn.myview.MainActivity">
<com.lee.demo.cicleview
android:layout_width="match_parent"
android:layout_height="match_parent"
myview:TextColor="#ff0000"
/>
</RelativeLayout>