自定義View基礎一

1.視圖(View)定義

視圖(View)表現爲顯示在屏幕上的各種視圖,如TextView、LinearLayout等。

2.視圖分類

  • 視圖View主要分兩類:
類別 解釋 特點
單一視圖 即一個View,如TextView 不包含子View
視圖組 即多個View組成的ViewGroup,LinearLayout 包含子View
  • Android 中的UI組件都由View、ViewGroup組成。

3.View類簡介

  • View類是Android中各種組件的基類,如View是ViewGroup基類。
  • View的構造函數:共有4個,具體如下:
// 如果View是在Java代碼裏面new的,則調用第一個構造函數
 public CarsonView(Context context) {
        super(context);
    }

// 如果View是在.xml裏聲明的,則調用第二個構造函數
// 自定義屬性是從AttributeSet參數傳進來的
    public  CarsonView(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

// 不會自動調用
// 一般是在第二個構造函數裏主動調用
// 如View有style屬性時
    public  CarsonView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

    //API21之後才使用
    // 不會自動調用
    // 一般是在第二個構造函數裏主動調用
    // 如View有style屬性時
    public  CarsonView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
        super(context, attrs, defStyleAttr, defStyleRes);
    }

自定義View必須重寫至少一個構造函數。

構造方法參數

構造函數參數最多有四個:

  • Context
  • AttributeSet ——XML屬性(當從XML inflate的時候)
  • int defStyleAttr ——應用到View的默認風格(定義在主題中)
  • int defStyleResource ——如果沒有使用defstyleAttr ,應用到View的默認風格

除了Context,其它參數只是用來通過XML屬性配置View的初始狀態(從佈局,style以及theme中)。

屬性

讓我們從如何定義XML屬性開始討論。這裏是一個XML中最基本的ImageView:

<ImageView  
  android:layout_width="wrap_content"
  android:layout_height="wrap_content"
  android:src="@drawable/icon"
  />

你有沒有想過這裏的layout_width,layout_height以及src是從哪裏來的?是通過<declare-styleable> 把這些屬性明確的聲明爲系統需要處理的東西。比如,src就是在這裏定義的:

<declare-styleable name="ImageView">  
  <!-- Sets a drawable as the content of this ImageView. -->
  <attr name="src" format="reference|color" />
  <!-- ...snipped for brevity... -->
</declare-styleable>

每個declare-styleable 產生一個R.styleable.[name] ,外加每個屬性的R.styleable.[name]_[attribute]。比如,上面的代碼產生R.styleable.ImageView和R.styleable.ImageView_src。
這些資源是什麼東西呢?R.styleable.[name]是所有資源的數組,系統使用它來查找屬性值。每個R.styleable.[name]_[attribute]只不過是這個數組的索引罷了,所以你可以一次性取出所有屬性,然後分別查詢每個的值。
如果你把它想象成一個cursor,R.styleable.[name]就可以看成是一個待查找的column列表,而R.styleable.[name]_[attribute]就是一個column索引。

AttributeSet

上面寫的XML是以一個AttributeSet的形式傳遞給View的。
通常不直接使用AttributeSet。而是使用Theme.obtainStyledAttributes()。這是因爲原始的屬性通常需要引用和應用樣式。比如,如果你在XML中定義了style="@style/MyStyle",這個方法先獲取MyStyle,然後把它的屬性混合進去。最終obtainStyledAttributes()返回TypedArray,可以用它來獲取屬性值。
這個過程簡化之後就像這樣:

public ImageView(Context context, AttributeSet attrs) {  
  TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.ImageView, 0, 0);
  Drawable src = ta.getDrawable(R.styleable.ImageView_src);
  setImageDrawable(src);
  ta.recycle();
}

這裏我們向obtainStyledAttributes()傳遞了兩個參數。第一個參數是AttributeSet attrs,即XML中的屬性,表示從layout文件中直接爲這個View添加的屬性集合(一種直接使用android:layout_width=“wrap_content"這種直接指定,還有一種通過style=”@style/somestyle"指定)。第二個參數是R.styleable.ImageView數組,它告訴這個方法我們想取哪個屬性的值,這裏表示要獲取ImageView屬性的值。
當獲取了TypedArray之後,我們既可以獲取單個屬性了。我們需要使用R.styleable.ImageView_src來正確索引數組中的屬性。

Theme屬性

AttributeSet並不是obtainStyledAttribute()獲取屬性值的唯一地方。屬性也可以存在於主題中。但是它在View inflate中的過程中扮演着不重要的角色,以內主題一般不會設置src這樣的屬性,但是如果使用obtainStyledAttributes()獲取主題屬性的話就有作用了。

Default style Attribute

你可能注意到了obtainStyledAttributes()的最後兩個參數我是用的值爲0。實際上它們是兩個資源引用:defStyleAttr和defStyleRes。這裏我們關注的是第一個。
defStyleAttr是obtainStyledAttributes()最令人困惑的參數。
它是一個爲某類型的View定義一個基本樣式的方法。比如,如果你想一次性修改app中所有TextView,你可以在主題中設置textViewStyle。如果不存在這個東西的話,你就需要手動爲每個TextView定義樣式了。
下面以TextView爲例介紹他實際是如何工作的:
首先,它是一個屬性(這裏是,R.attr.textViewStyle)。這裏是安卓系統蒂尼textViewStyle的地方:

<resources>  
  <declare-styleable name="Theme">
    <!-- ...snip... -->
    <!-- Default TextView style. -->
    <attr name="textViewStyle" format="reference" />
    <!-- ...etc... -->
  </declare-styleable>
</resource>

又一次,我們適用了declare-styleable,但是這次是用來定義存在於theme中的屬性。這裏我們說textViewstyle是一個referenc即,它的值只是一個資源的引用,這裏,他應該是一個指向style的引用。
接下來,我們必須在當前主題設置textViewstyle.默認的Android主題如下:

<resources>  
  <style name="Theme">
    <!-- ...snip... -->
    <item name="textViewStyle">@style/Widget.TextView</item>
    <!-- ...etc... -->
  </style>
</resource>

然後需要爲Appication或者Activity設置這個主題。通常是通過manifest設置:

<activity  
  android:name=".MyActivity"
  android:theme="@style/Theme"
  />

現在我們就可以在obtainStyledAttributes()裏使用它了:

TypedArray ta = theme.obtainStyledAttributes(attrs, R.styleable.TextView, R.attr.textViewStyle, 0);

最終的效果是,任何沒有在AttributeSet中定義的屬性都將用textViewStyle引用的樣式填充。

Default Style Resource

defStyleRes就要比defStyleAttr簡單多了它只是一個style資源。不需要間接的通過theme。
只有在defstyleAttris沒有定義的情況下,纔會使用style中的defStyleRes(設置爲0,或者沒有在主題中設置)。

優先級

我們現在有了一系列 通過obtainStyledAttributes()獲取屬性值的方式。這裏是它們的優先級,從高到低:

  1. 定義在AttributeSet中的值;
  2. 定義在AttributeSet中的style資源(比如:style=@style/blah)
  3. defStyleAttr指定的默認樣式
  4. defStyleResource指定的默認樣式資源(如果沒有defStyleAttr)
  5. 主題中的值

換句話說,任何直接在XML中設置的屬性都將首先被使用。但是如果你沒有設置,這些屬性也可以從其它地方獲取。

我們一般設置自定義View:

SomeView(Context context) {  
  this(context, null);
}
SomeView(Context context, AttributeSet attrs) {  
  // Call super() so that the View sets itself up properly
  super(context, attrs);
  // ...Setup View and handle all attributes here...
}

只需要這兩個參數的構造方法你就能隨意的使用obtainStyledAttributess()了。實現默認樣式的一個簡便方法是直接提供defStyleRes給它。那樣你就不要忍受defStyleAttr的痛苦了。

4.View視圖結構

  • 對於多View的視圖,結構時樹形結構:最頂層是ViewGroup
  • ViewGroup下可能有多個ViewGroup或View,如下圖:
    在這裏插入圖片描述
    一定要記住:無論是measure過程、layout過程還是draw過程,永遠都是從View樹的根節點開始測量或計算(即從樹的頂端開始),一層一層、一個分支一個分支進行(即樹形遞歸),最終計算整個View樹中各個View,最終確定整個View樹的相關屬性。

5 Adroid 座標系

Android的座標系定義爲:

  • 屏幕的左上角爲座標原點
  • 向右爲x軸方向
  • 向下爲y軸方向
    具體如下圖:
    在這裏插入圖片描述

6.View位置(座標)描述

  • View 的位置由4個頂點決定的(如下A、B、C、D)
    在這裏插入圖片描述
    4個頂點的位置描述分別由4個值決定(View的位置是相對父控件而言的)
  • Top:子View上邊界到父View上邊界的距離
  • Left:子View左邊界到父View左邊界的距離
  • Bottom:子View下邊界到父View上邊界的距離
  • Right:子View右邊界到父View左邊界的距離
    在這裏插入圖片描述

7.位置獲取方式

  • View的位置是通過view.getxxx函數進行獲取:
// 獲取Top位置
public final int getTop() {  
    return mTop;  
}  

// 其餘如下:
  getLeft();      //獲取子View左上角距父View左側的距離
  getBottom();    //獲取子View右下角距父View頂部的距離
  getRight();     //獲取子View右下角距父View左側的距離
  • 與MotionEvent中get()和getRaw()的區別
//get() :觸摸點相對於其所在組件座標系的座標
 event.getX();       
 event.getY();

//getRaw() :觸摸點相對於屏幕默認座標系的座標
 event.getRawX();    
 event.getRawY();

具體如下圖:
在這裏插入圖片描述

8. 角度(angle)&弧度(radian)

  • 自定義View實際上是將一些簡單的形狀通過計算,從而組合到一起形成的效果。
    這會設計到畫布的相關操作(旋轉)、正餘弦函數計算等,即會涉及到角度(angle)與弧(radian)的相關知識。
  • 角度和弧度都是描述角的一種度量單位,區別如下圖:
    在這裏插入圖片描述
    在默認的屏幕座標系中角度增大方向爲順時針。
    屏幕座標系角度增大方向
    注:在常見的數學座標系中角度增大方向爲逆時針。

9. 顏色相關

Android 中顏色相關內容包括顏色模式,創建顏色的方式,以及顏色的混合模式等。

顏色模式

顏色模式 解釋
ARGB8888 四通道高精度(32位)
ARGB4444 四通道地精度(16位)
RGB565 android 屏幕默認模式(16位)
Alpha8 僅有透明通道(8位)
備註 1.字母表示通道類
2.數值表示該類型用多少位二進制來描述
3.例子:ARGB8888,表示有四個通道(ARGB);每個對應的通道均用8位來描述。

以ARGB8888爲例介紹顏色定義:
在這裏插入圖片描述

9.2 定義顏色的方式

在java中定義顏色

 //java中使用Color類定義顏色
 int color = Color.GRAY;     //灰色

  //Color類是使用ARGB值進行表示
  int color = Color.argb(127, 255, 0, 0);   //半透明紅色
  int color = 0xaaff0000;                   //帶有透明度的紅色

在xml文件中定義顏色

在/res/values/color.xml文件中如下定義:

<?xml version="1.0" encoding="utf-8"?>
<resources>

    //定義了紅色(沒有alpha(透明)通道)
    <color name="red">#ff0000</color>
    //定義了藍色(沒有alpha(透明)通道)
    <color name="green">#00ff00</color>
</resources>

在xml文件中以“#”開頭定義顏色,後面跟十六進制的值,有如下幾種定義方式:

#f00            //低精度 - 不帶透明通道紅色
#af00           //低精度 - 帶透明通道紅色

#ff0000         //高精度 - 不帶透明通道紅色
#aaff0000       //高精度 - 帶透明通道紅色

9.3 引用顏色的方式

在java文件中引用xml中定義的顏色:

//方法1
int color = getResources().getColor(R.color.mycolor);

//方法2(API 23及以上)
int color = getColor(R.color.myColor);  

在xml文件(layout或style)中引用或者創建顏色

<!--在style文件中引用-->
    <style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
        <item name="colorPrimary">@color/red</item>
    </style>

 <!--在layout文件中引用在/res/values/color.xml中定義的顏色-->
  android:background="@color/red"     

 <!--在layout文件中創建並使用顏色-->
  android:background="#ff0000" 
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章