Android UI進階之旅1 Material Design基本概念以及樣式

小楠總發表於2017-12-21

###關於Material Design的基本概念

Material Design(簡稱MD):從Android5.0開始引入的,是一種全新的虛擬的設計語言(翻譯為“材料設計”),其實是谷歌提倡的一種設計風格、理念、原則。是擬物設計和扁平化設計一種結合體驗。還吸取了最新一些科技理念。這種設計風格是跨平臺:我們在網頁、IOS等地方也會經常看見。

例如:為了增加APP的層次感,可以通過設計View的Z軸座標大小來完成。我們要相容的時候,可以自己通過layerlist來實現。

####不同人員對於Material Design的認識

  1. 對於美工:遵循MD的介面設計、圖示合集。
  2. 對於產品經理:遵循MD介面設計、頁面的跳轉及動畫效果、互動設計。
  3. 對於開發人員:參與原型設計、輔助美工原型設計的素材準備。開發實現MD的設計----介面、動畫、轉場動畫等等。

其中,擼程式碼才是我們的重點。關於更多的介紹,可以去谷歌官網MD官網,當然國內也有翻譯好的文件,例如極客學院的文件:極客學院文件

###Material Design的使用及開發

谷歌開放以及收集了一些最新的開源的專案(很多是自己開發的),彙集到最新的support相容支援包以及最新的5.X API裡面。

  1. android-support-v4:最低相容到Android 1.6系統,裡面有類似ViewPager等控制元件。
  2. android-support-v7:appcompat、CardView、gridlayout、mediarouter、palette、preference、recyclerView(最低相容到3.0)
  3. v14 preference:設定頁面,可以通過配置檔案達到介面設計的效果。(用得比較少)

其中,v7包最低相容到Android 2.1的系統(不同的庫可能會高一些),這個工程可以讓開發人員統一開發標準,在任何的系統版本下保證相容性。(比如:Theme,value,佈局,新的控制元件,新的動畫特效實現)

######Tips1:現在開發幾乎都是相容到4.0,所以一般不會出現什麼相容性問題。 ######Tips2:相容包報錯問題:SDK升級:API升級、相容包的升級、工具升級。因為新的相容包可能引入了新的類、資源等等。

###Material Design初探--樣式的使用

首先我們需要使用v7包,為什麼要用appcompat專案呢?因為裡面是谷歌精心準備的---解決android碎片化開發蛋疼的問題,讓我們app編譯出來在各種高低版本之間、不同的廠商生產的ROM之間顯示出來的效果UI控制元件等有一較一致的體驗。

######Tips:SDK更新的歷史上幾個特別重要的版本:14(4.0)、19(4.4)、21(5.0),而appcompat裡面就對這幾個不同的版本做了相容,如果我們使用appcompat包,系統會自動根據最近的版本來使用。

現在用Android Studio建立專案的時候預設都是使用了v7包,因此我們可以使用v7包裡面的主題,並且配置相關的顏色,例如:

<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
    <!--主色調,一般是指標題欄的顏色-->
    <item name="colorPrimary">#f00</item>
    <!--狀態列的顏色-->
    <item name="colorPrimaryDark">#0f0</item>
    <item name="android:statusBarColor">#0f0</item>
    <!--頁面中所有UI控制元件的基本色調-->
    <item name="colorAccent">#00f</item>

    <!--以下幾個我們一般不會去配置-->
    
    <!--背景顏色,但是我們一般不會配置,為了防止過度繪製-->
    <item name="windowBackground">#fff</item>
    <!--底部導航條的背景顏色-->
    <item name="android:navigationBarColor">#f00</item>
    <!--字型的基調顏色,實際發現沒有生效-->
    <item name="textColorPrimary">#000</item>
</style>
複製程式碼

其中,各種顏色的對應關係如下圖所示:

MD的樣式.jpg

######Tips1:某些屬性最低的API比較高,因此需要在不同的values資料夾中配置。 ######Tips2:上面只是為了方便寫Demo,直接把顏色寫死,實際專案中我們需要自己建立colors檔案。

####MaterialDesign相容性控制元件的使用

尤其是在appcompat-V7裡面有很多為相容而生的控制元件,這樣就可以做到高低版本和不同的ROM之間體驗一致!還可以配合appcompat的主題使用達到體驗一致性。例如:

  1. android.support.v7.app.AlertDialog

  2. 進度條樣式設定 style="@style/Widget.AppCompat.ProgressBar.Horizontal"

  3. SwipeRefreshLayout下拉重新整理

  4. PopupWindow、ListPopupWindow、PopupMenu、Button、EditText等等

  5. android.support.v7.widget.LinearLayoutCompat 給包裹在裡面的所有子控制元件新增間隔線(例如“關於頁面”的條目)

     <android.support.v7.widget.LinearLayoutCompat
         android:layout_width="match_parent"
         android:layout_height="match_parent"
         app:divider="@drawable/abc_list_divider_mtrl_alpha"
     	app:dividerPadding="10dp"
         app:showDividers="beginning|middle"
         android:orientation="vertical" >
    複製程式碼

其中,showDividers屬性是設定在哪一個條目的位置顯示。比如只設定end,那麼只會在最後一個條目的下面新增分隔線。當然,你也可以設定padding,這裡就不贅述了。這裡有個坑就是,只能同時設定左右Padding。

######Tips:如果你的專案是使用了Material Design的話,推薦使用這些相容性控制元件,否則的話就不用這麼麻煩了。

####擴充套件--LinearLayoutCompat原始碼分析

看原始碼需要有目的去看,因此我們可以先猜想:

LinearLayoutCompat是如何做到給裡面的所有的child之間新增分割線的?

首先我們知道View的繪製會經過三個方法:onMearsue(測量自身和裡面的所有子控制元件),onLayout(擺放裡面所有的子控制元件),onDraw(繪製)

猜想:

  1. mearsuredWidth,mearsuredHeight會變大(加上分割線)
  2. 擺放子控制元件位置會有一定的體現(childView: left/top/right/bottom)
  3. onDraw繪製的時候也會有體現(childView: left/top/right/bottom)

先看onMearsue:

@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    if (mOrientation == VERTICAL) {
        measureVertical(widthMeasureSpec, heightMeasureSpec);
    } else {
        measureHorizontal(widthMeasureSpec, heightMeasureSpec);
    }
}
複製程式碼

onMeasure分為了水平和豎直的情況,我們這次以豎直情況為例分析。我們猜想可以知道,在測量的時候,肯定加了分隔線的高度(只看核心程式碼):

void measureVertical(int widthMeasureSpec, int heightMeasureSpec) {

	for (int i = 0; i < count; ++i) {
        final View child = getVirtualChildAt(i);

		//如果有分隔線,那麼測量的時候就加上分割線的Drawable的高度
        if (mTotalLength > 0 && hasDividerBeforeChildAt(count)) {
            mTotalLength += mDividerHeight;
        }
	}
}
複製程式碼

下面繼續來看onLayout:

@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
    if (mOrientation == VERTICAL) {
        layoutVertical(l, t, r, b);
    } else {
        layoutHorizontal(l, t, r, b);
    }
}
複製程式碼

同理,我們只看layoutVertical:

void layoutVertical(int left, int top, int right, int bottom) {
    for (int i = 0; i < count; i++) {
        if (hasDividerBeforeChildAt(i)) {
            childTop += mDividerHeight;
        }
    }
}
複製程式碼

也能看到,相關的程式碼。最後看繪製:

@Override
protected void onDraw(Canvas canvas) {
    if (mDivider == null) {
        return;
    }

    if (mOrientation == VERTICAL) {
        drawDividersVertical(canvas);
    } else {
        drawDividersHorizontal(canvas);
    }
}
複製程式碼

相信看到這裡就明白了。我們最後分析一些分割線是如何繪製上去的:

void drawVerticalDivider(Canvas canvas, int left) {
    mDivider.setBounds(left, getPaddingTop() + mDividerPadding,
            left + mDividerWidth, getHeight() - getPaddingBottom() - mDividerPadding);
    mDivider.draw(canvas);
}
複製程式碼

mDivider其實是一個Drawable物件,畫之前需要設定一個繪製範圍,然後進行繪製即可。

更多的關於LinearLayoutCompat的使用,可以參考:

http://blog.csdn.net/zhangphil/article/details/48899585

如果覺得我的文字對你有所幫助的話,歡迎關注我的公眾號:

公眾號:Android開發進階

我的群歡迎大家進來探討各種技術與非技術的話題,有興趣的朋友們加我私人微信huannan88,我拉你進群交(♂)流(♀)

相關文章