1. 程式人生 > >android-自定義控制元件

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();
    }