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

關于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,我拉你進群交(♂)流(♀)

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容