hello,大家好,上一篇介紹了drawable如何顯示到view上,基本上是以background
屬性來講的,其實在view中用到的drawable地方還是挺多的,還不屬性drawable顯示到view的流程,可以看下我寫的上一篇android中drawable顯示到view上的過程,今天要介紹的也是跟drawable一個相關的屬性foreground
屬性,不過該屬性之前只是針對FrameLayout的,後來在23的api之後所有的view都能用該屬性,因此大家知道這麼回事就行了,而且在後面view原始碼中也會看到該屬性相容的程式碼,該屬性一般在開發中能實現水波點選的效果,不知道大家平時用得多不多,好了,下面還是跟往常一樣,通過一個簡單的例子來介紹該屬性的使用:
<TextView
android:id="@+id/view"
android:layout_width="100dp"
android:layout_height="100dp"
android:layout_marginTop="50dp"
android:background="#cccccc"
android:foreground="#ff0000"
android:gravity="center"
android:text="我是測試的view" />
<TextView
android:id="@+id/view1"
android:layout_width="100dp"
android:layout_height="100dp"
android:layout_marginTop="50dp"
android:background="#cccccc"
android:foreground="?attr/selectableItemBackground"
android:gravity="center"
android:text="我是測試的view" />
複製程式碼
demo是很簡單,為了演示效果,上面textview的foreground屬性是一個顏色值,下面textview的foreground是獲取應用的style裡面的selectableItemBackground
屬性。第一個textview的foreground屬性顏色直接把background屬性覆蓋掉了,而第二個textview的foreground是一個波紋效果,因此帶著這些問題順著原始碼看下這些問題,直接看獲取view的foreground屬性地方:
在此處看到該屬性值在api>=23或view是frameLayout的時候呼叫了setForeground
方法,該方法其實跟setBackground
方法做的是類似的事,先是判斷有沒有foreground,如果有先銷燬掉foreground,然後呼叫applyForegroundTint
方法設定foreground的著色情況,最後也是觸發了重新繪製view。那直接看view繪製的時候,是怎麼繪製foreground的:
public void draw(Canvas canvas) {
final int privateFlags = mPrivateFlags;
final boolean dirtyOpaque = (privateFlags & PFLAG_DIRTY_MASK) == PFLAG_DIRTY_OPAQUE &&
(mAttachInfo == null || !mAttachInfo.mIgnoreDirtyState);
if (!dirtyOpaque) {
drawBackground(canvas);
}
if (!dirtyOpaque) onDraw(canvas);
onDrawForeground(canvas);
}
複製程式碼
這裡我把draw方法幾個關鍵方法給列出來了,先是繪製background,然後是onDraw,最後才是foreground,所以說在上面第一個例子中,因為最後才繪製foreground,因此顯示的結果只有foreground的顏色了,下面來看看onDrawForeground
方法是怎麼繪製foreground的:
public void onDrawForeground(Canvas canvas) {
onDrawScrollIndicators(canvas);
onDrawScrollBars(canvas);
final Drawable foreground = mForegroundInfo != null ? mForegroundInfo.mDrawable : null;
if (foreground != null) {
if (mForegroundInfo.mBoundsChanged) {
mForegroundInfo.mBoundsChanged = false;
final Rect selfBounds = mForegroundInfo.mSelfBounds;
final Rect overlayBounds = mForegroundInfo.mOverlayBounds;
if (mForegroundInfo.mInsidePadding) {
selfBounds.set(0, 0, getWidth(), getHeight());
} else {
selfBounds.set(getPaddingLeft(), getPaddingTop(),
getWidth() - getPaddingRight(), getHeight() - getPaddingBottom());
}
final int ld = getLayoutDirection();
//根據mForegroundInfo.mGravity得到foreground的bounds
Gravity.apply(mForegroundInfo.mGravity, foreground.getIntrinsicWidth(),
foreground.getIntrinsicHeight(), selfBounds, overlayBounds, ld);
foreground.setBounds(overlayBounds);
}
foreground.draw(canvas);
}
}
複製程式碼
其實跟background的繪製差不多,只不過在foreground設定bounds的時候,多了一個foreground.gravity的判斷,意思是foreground的權重,但是我測試過權重只有fill的情況下才起作用,其他的其中foreground.gravity都會讓foreground的顏色失去作用。
寫到這的時候,大家知道了事例一中為什麼加了foreground屬性顏色值之後,為什麼設定textview的background以及text屬性都看不到了吧,因為foreground是在繪製之後最後繪製的,所以被foreground的顏色給覆蓋了。那第二個事例中為什麼會有點選的波紋效果呢,這個就需要了解?attr/selectableItemBackground
代表的是啥,這個其實是跟我們們的主題style屬性相關,也就是順著app的application的style屬性可以找到該屬性是什麼:
Base.Theme.AppCompat.Light
下面找:
此處找到了關於selectableItemBackground
屬性,但還是style裡面的屬性,不要緊,我們們繼續找父style,最後在Theme.Material.Light
style下面找到了:
也就是說水波效果用到的資原始檔是item_background_material
的drawable檔案,繼續看下該資原始檔是怎麼定義的:
<ripple xmlns:android="http://schemas.android.com/apk/res/android"
android:color="?attr/colorControlHighlight">
<item android:id="@id/mask">
<color android:color="@color/white" />
</item>
</ripple>
複製程式碼
color顏色用的是?attr/colorControlHighlight
,我們們可以看下該屬性是怎麼定義的,該屬性也是在Theme.Material.Light
style下面定義的:
ripple_material_light
是怎麼定義的:
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:alpha="@dimen/highlight_alpha_material_light"
android:color="@color/foreground_material_light" />
</selector>
複製程式碼
此處定義了一個透明度為0.12,顏色為黑色的selector顏色值。在上一節我們知道drawable的子類是根據子類的各種標籤生成不同的drawable,而水波的資原始檔是ripple
標籤,所以從這裡可以知道實質是一個RippleDrawable
,關於RippleDrawable
後面再講解它們怎麼繪製的。下面我們嘗試下改變水波效果的顏色,按照系統自帶的這個水波效果來寫寫,定義了一個change.xml的drawable檔案:
<ripple xmlns:android="http://schemas.android.com/apk/res/android"
android:color="@drawable/ripple_color">
<item android:id="@android:id/mask">
<color android:color="@android:color/white" />
</item>
</ripple>
複製程式碼
可以看到這裡引用了一個ripple_color
的檔案:
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:alpha="0.5" android:color="@color/colorPrimary" />
</selector>
複製程式碼
用到了一個透明度為0.5,並且顏色用的是系統生成的顏色值。最後在view上引用change.xml
檔案:
好了關於水波效果就說到這裡,後面主要說說StateListDrawable、RippleDrawable實現效果的繪製是怎麼來的,以及介紹drawable相關的api是如何使用的,以及使用drawable下面其他的不常用的drawable來實現好玩的功能。