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()獲取屬性值的方式。這裏是它們的優先級,從高到低:
- 定義在AttributeSet中的值;
- 定義在AttributeSet中的style資源(比如:style=@style/blah)
- defStyleAttr指定的默認樣式
- defStyleResource指定的默認樣式資源(如果沒有defStyleAttr)
- 主題中的值
換句話說,任何直接在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"