android-自定義控制元件
自定義控制元件兩種方式
1、繼承ViewGroup
例如:ViewGroup , LinearLayout, FrameLayout, RelativeLayout等。
2、繼承View
例如:View, TextView, ImageView, Button等。
View的測量、佈局、繪製過程
顯示一個View主要以下三個步驟:
1、Measure測量一個View的大小。
(1)MeasureSpec類
MeasureSpec類描述了父View對子View大小的期望,裡面包含了測量模式和大小。被組合到一個32位int型的數值中,其中高2位表示模式,低30位表示大小。
在對View進行測量時,Android提供了三種測量模式:
a、Exactly
控制元件的layout_width屬性或layout_height屬性指定為具體數值,或者指定為Match_parent
b、at_most
layout_width屬性或layout_height屬性指定為wrap_content時。
c、unspecified
通常情況下在繪製自定義View時才會使用。
View預設的onMeasuer()方法只支援Exactly模式,所以如果在自定義控制元件的時候不重寫onMeasure方法的話,就只能使用exactly模式,如果要讓自定義的view支援wrap_content屬性,那麼必須重寫onMeasure方法來指定wrap_content時的大小。
2、Layout擺放一個View的位置
(1)引數
a、layout
layout方法中接受4個引數,指定子View在父view中左、上、右、下的位置。
b、setFrame
判斷自身的位置和大小是否發生改變,如果改變,需要重繪。
c、onLayout
是ViewGroup用來決定子View擺放位置的。
3、Draw畫出View的顯示內容。
(1)draw
是由ViewRoot的performTraversals方法發起,它將呼叫DecorView的draw方法,並把成員變數canvas傳給draw方法,而在後面的draw遍歷中,傳遞的都是同一個canvas,所以android的繪製是同一個window中所有View都繪製在同一個畫布上。
(2)onDraw
View繪製自身的實現方法。
(3)dispatchDraw
先根據自身的padding剪裁畫布,所有的子View都將在畫布剪裁後的區域繪製。
遍歷所有子View,呼叫子View的computeScroll計運算元View的滾動值,根據滾動值和子View在父View中的座標進行畫布原點座標的移動,根據子大父View中的座標計算出子View的大小,然後對畫布進行裁剪。
其中measure和layout方法都是final,無法重寫,雖然draw不是final,但是也不建議重寫該方法。這三個方法都 已經寫好了View的邏輯,如果我們想實現自己的邏輯,而又不破壞View的工作流程,可以重寫onMeasure, onLayout, onDraw方法。
attributes defStyleAttr defStyleRes
自定義控制元件一般有以下四個構造方法:
public class MView extends View{
public MView(Context context) ;
public MView(Context context, @Nullable AttributeSet attrs);
public MView(Context context, @Nullable AttributeSet attrs, int defStyleAttr);
public MView(Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) ;
}
1、第一個構造方法,他只有一個context,這個構造方法使用在程式碼中直接new 出一個控制元件,它不附帶任何自定義屬性。
2、第二個構造方法,當你在佈局中使用<MView />的時候會被呼叫。
3、後兩個方法都是為了可以在外部style中直接給我們自定義控制元件設定屬性。
Attributes(佈局檔案), defStyleAttr(Theme),defStyleRes(外部)都屬於告訴程式從哪取屬性。
給View的一個屬性賦值一共有5種方式:
優先順序:
屬性直接賦值>對控制元件通過使用style方式賦值>自定義theme並且對屬性賦予style的引用的賦值方式/defStyleRes>自定義theme並且對屬性的賦值方式。
1、通過佈局檔案直接對屬性賦值。
<com.example.dsliang.viewstyleattributedemo.CustomView
android:layout_width="match_parent"
android:layout_height="match_parent"
app:attrA="set from xml file"
app:attrB="set from xml file"
/>
2、通過給控制設定style屬性,從而對某一些特點的屬性賦值。
<com.example.dsliang.viewstyleattributedemo.CustomView
style="@style/SpecialCustomViewStyle"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:attrA="set from xml file"
app:attrB="set from xml file"
/>
<style name="SpecialCustomViewStyle">
<item name="attrB">set from view style</item>
<item name="attrC">set from view style</item>
<item name="attrD">set from view style</item>
</style>
3、通過自定義theme的方式,給某一屬性賦值上。
<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
<!-- Customize your theme here. -->
<item name="colorPrimary">@color/colorPrimary</item>
<item name="colorPrimaryDark">@color/colorPrimaryDark</item>
<item name="colorAccent">@color/colorAccent</item>
<item name="android:textSize">24px</item>
</style>
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.dsliang.viewstyleattributedemo">
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
4、通過自定義theme的方式,給某一個屬性賦予一個style引用。
<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
<!-- Customize your theme here. -->
<item name="colorPrimary">@color/colorPrimary</item>
<item name="colorPrimaryDark">@color/colorPrimaryDark</item>
<item name="colorAccent">@color/colorAccent</item>
<item name="customViewStyle">@style/CustomViewStyle</item>
</style>
<style name="CustomViewStyle">
<item name="attrD">set from theme style</item>
<item name="attrE">set from theme style</item>
<item name="attrF">set from theme style</item>
</style>
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.dsliang.viewstyleattributedemo">
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
5、編寫View的時候,通過defStyleAttr引用給屬性設定預設值。
<style name="DefaultCustomViewStyle">
<item name="attrD">set from default view style</item>
<item name="attrE">set from default view style</item>
<item name="attrF">set from default view style</item>
</style>
public CustomView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.CustomView, defStyleAttr, R.style.DefaultCustomViewStyle);
......
a.recycle();
}