android中foreground水波實現過程分析

hello,你們好,上一篇介紹了drawable如何顯示到view上,基本上是以background屬性來說的,其實在view中用到的drawable地方仍是挺多的,還不屬性drawable顯示到view的流程,能夠看下我寫的上一篇android中drawable顯示到view上的過程,今天要介紹的也是跟drawable一個相關的屬性foreground屬性,不過該屬性以前只是針對FrameLayout的,後來在23的api以後全部的view都能用該屬性,所以你們知道這麼回事就好了,並且在後面view源碼中也會看到該屬性兼容的代碼,該屬性通常在開發中能實現水波點擊的效果,不知道你們平時用得多很少,好了,下面仍是跟往常同樣,經過一個簡單的例子來介紹該屬性的使用:android

<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" />
複製代碼

simple
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的:canvas

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的顏色失去做用。api

寫到這的時候,你們知道了事例一中爲何加了foreground屬性顏色值以後,爲何設置textview的background以及text屬性都看不到了吧,由於foreground是在繪製以後最後繪製的,因此被foreground的顏色給覆蓋了。那第二個事例中爲何會有點擊的波紋效果呢,這個就須要瞭解?attr/selectableItemBackground表明的是啥,這個實際上是跟我們的主題style屬性相關,也就是順着app的application的style屬性能夠找到該屬性是什麼:bash

直接來到21下面的 Base.Theme.AppCompat.Light下面找:

此處找到了關於 selectableItemBackground屬性,但仍是style裏面的屬性,沒關係,我們繼續找父style,最後在 Theme.Material.Lightstyle下面找到了:

也就是說水波效果用到的資源文件是 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.Lightstyle下面定義的:app

繼續看下 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文件:ide

<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的文件:post

<?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文件:測試

效果你們能夠錄製的gif:

好了關於水波效果就說到這裏,後面主要說說StateListDrawable、RippleDrawable實現效果的繪製是怎麼來的,以及介紹drawable相關的api是如何使用的,以及使用drawable下面其餘的不經常使用的drawable來實現好玩的功能。spa

相關文章
相關標籤/搜索