概述
Material Design爲開發者提供了一系列的UI控件,對於開發者來說實在是福音。
今天這篇博客就記錄一下UI新控件方面的知識。
先看一下大綱
UI控件
Android5.0基於Material Design,提供了很多新控件,這些控件無論在功能、體驗和性能上都很出色,下面一一列舉各個控件的使用方法。
SnackBar
SnackBar通過在屏幕底部展示簡潔的信息,爲一個操作提供了一個輕量級的反饋,並且在Snackbar中還可以包含一個操作,在同一時間內,僅且只能顯示一個 Snackbar,它的顯示依賴於UI,不像Toast那樣可以脫離應用顯示。它的用法和Toast很相似,唯一不同的就是它的第一個參數不是傳入Context而是傳入它所依附的父視圖,但是他比Toast更強大。
(Dialog > SnackBar > Toast)
gradle
compile 'com.android.support:design:23.1.1'
最簡單的提示
Snackbar.make(view, "SnackBar Demo Text", Snackbar.LENGTH_LONG).show();
設置一個Action,自帶點擊事件
Snackbar.make(view, "Change Text", Snackbar.LENGTH_LONG)
.setAction("OK", new View.OnClickListener() {
@Override
public void onClick(View v) {
tv.setText("New Text");
}
}).show();
設置背景顏色
Snackbar sb=Snackbar.make(view, "SnackBar demo", Snackbar.LENGTH_LONG);
sb.setAction("OK", new onClickListener()());
sb.getView().setBackgroundColor(0xfff44336);
sb.show();
設置Action文字顏色
默認的Action文字顏色是ColorAccent,也可以通過api設置sb.setActionTextColor();
其他API
sb.dismiss();消失
sb.isShown();是否在顯示
sb.isShownOrQueued();是否在顯示或者在等待隊列
sb.setText();設置文字
顯示時長
LENGTH_LONG:長時間
LENGTH_SHORT:短時間
LENGTH_INDEFINITE:無限期,知道調用dismiss或者下一個SnackBar顯示
注意1.SnackBar始終從底部彈出
SnackBar調用make時傳進去一個View,它會順着這個View去找父級,一直找到CoordinatorLayout或者FrameLayout,然後在它底部彈出。如果你的佈局中沒有包含這兩個容器的一個,它就會一直找到Widnow的DecorView,效果就是在屏幕底部彈出。
看源碼:
do {
if(view instanceof CoordinatorLayout) {
...
}
if(view instanceof FrameLayout) {
...
}
} while(view != null);
瞭解這特性有兩個用處,解決異常Bug,實現特殊需求。
Demo:
<android.support.design.widget.CoordinatorLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_margin="30dp">
<android.support.design.widget.FloatingActionButton
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@mipmap/ic_launcher"
app:borderWidth="0dp"
app:fabSize="normal" />
</android.support.design.widget.CoordinatorLayout>
注意2.懸浮按鈕被snackBar遮擋
<RelaticeLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginBottom="30dp">
<android.support.design.widget.FloatingActionButton
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@mipmap/ic_launcher"
app:borderWidth="0dp"
app:fabSize="normal" />
</RelaticeLayout>
這個問題一看很棘手,其實也很簡單用一下Material design庫裏的CoordinatorLayout即可,Java代碼完全不用動。
<android.support.design.widget.CoordinatorLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_margin="30dp">
<android.support.design.widget.FloatingActionButton
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@mipmap/ic_launcher"
app:borderWidth="0dp"
app:fabSize="normal" />
</android.support.design.widget.CoordinatorLayout>
稍微總結下:
1.比toast更加好,畢竟snackbar 可以響應點擊事件
2.比Dialog輕量,可以替換很多Dialog的場景
3.snackbar 同一時間有且只有一個在顯示
4.snackbar 上不要有圖標
5.snackbar上action 只能有一個
6.如果有懸浮按鈕FloatingActionButton的話,snackbar 在彈出的時候不要覆蓋這個button
7.瞭解爲什麼SnackBar是從底部彈出的
CardView
CardView曾經開始流行在Google+,後來越來越多的APP也引入了Card這樣的佈局方式,說到底,CardView也是一個容器佈局,只是他提供了一種卡片的形式,Google所幸提供了一個CardView控件,方便大家使用這種佈局,開發者可以設置大小和視圖高度,圓角的角度等。
<android.support.v7.widget.CardView
android:id="@+id/cardview"
android:layout_width="match_parent"
android:layout_height="80dp"
android:layout_margin="16dp"
android:clickable="true"
android:foreground="?android:attr/selectableItemBackground"
app:cardBackgroundColor="@color/colorAccent"
app:cardCornerRadius="4dp"
app:cardElevation="6dp">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:text="TextView in CardView"
android:textStyle="bold"
android:textColor="@color/colorPrimaryDark"
android:textSize="26sp" />
</android.support.v7.widget.CardView>
其實不用CardView也能實現,但是CardView做了很好的封裝,只需要設置幾個屬性,就可以實現很好的效果,並且也減少了drawable資源,而且即便你什麼都不設置,它也會設置合適的默認值。
Ps:
在低版本中設置了 CardElevation 之後 CardView 會自動留出空間供陰影顯示,而 Lollipop 之後則需要手動設置 Margin 邊距來預留空間,導致我在設置 Margin 在 Android 5.x 機器上調試好後,在 Kitkat 機器調試時發現邊距非常大,嚴重地浪費了屏幕控件。因此,我們需要自定義兩個dimen作爲CardView的Margin值。
/res/value
/res/value-v21
android:layout_margin=”@dimen/xxx_card_margin”android:foreground=”?attr/selectableItemBackground”
這個屬性會在 Lollipop 上自動加上 Ripple 效果,在舊版本則是一個變深/變亮的效果。- 讓點擊效果更加貼近 Material Design,要實現這個效果不難,我們只需要藉助Lollipop的一個新屬性
android:stateListAnimator
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_enabled="true" android:state_pressed="true">
<objectAnimator
android:duration="@android:integer/config_shortAnimTime"
android:propertyName="translationZ"
android:valueTo="@dimen/touch_raise"
android:valueType="floatType" />
</item>
<item>
<objectAnimator
android:duration="@android:integer/config_shortAnimTime"
android:propertyName="translationZ"
android:valueTo="0dp"
android:valueType="floatType" />
</item>
</selector>
android:stateListAnimator="@anim/touch_raise"
Toolbar
Toolbar是在 Android 5.0 開始推出的一個 Material Design 風格的導航控件 ,Google 非常推薦大家使用 Toolbar 來作爲Android客戶端的導航欄,以此來取代之前的 Actionbar 。與 Actionbar 相比, Toolbar 明顯要靈活的多。它不像 Actionbar 一樣,一定要固定在Activity的頂部,而是可以放到界面的任意位置。除此之外,在設計 Toolbar 的時候,Google也留給了開發者很多可定製修改的餘地,這些可定製修改的屬性在API文檔中都有詳細介紹,如:
- 設置導航欄圖標;
- 設置App的logo;
- 支持設置標題和子標題;
- 支持添加一個或多個的自定義控件;
- 支持Action Menu;
使用
前面提到 Toolbar 是在 Android 5.0 纔開始加上的,Google 爲了將這一設計向下兼容,自然也少不了要推出兼容版的 Toolbar 。爲此,我們需要在工程中引入 appcompat-v7 的兼容包,使用 android.support.v7.widget.Toolbar 進行開發。
MXL文件:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<android.support.v7.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@color/color_0176da">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Clock" />
</android.support.v7.widget.Toolbar>
</LinearLayout>
MENU文件:
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<item
android:id="@id/action_search"
android:icon="@mipmap/ic_search"
android:title="@string/menu_search"
app:showAsAction="ifRoom" />
<item
android:id="@id/action_notification"
android:icon="@mipmap/ic_notifications"
android:title="@string/menu_notifications"
app:showAsAction="ifRoom" />
<item
android:id="@+id/action_item1"
android:title="@string/item_01"
app:showAsAction="never" />
<item
android:id="@+id/action_item2"
android:title="@string/item_02"
app:showAsAction="never" />
</menu>
Activity:
public class ToolBarActivity extends BaseActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_tool_bar);
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
toolbar.setNavigationIcon(R.mipmap.ic_drawer_home);//設置導航欄圖標
toolbar.setLogo(R.mipmap.ic_launcher);//設置app logo
toolbar.setTitle("Title");//設置主標題
toolbar.setSubtitle("Subtitle");//設置子標題
toolbar.setTitleTextColor(Color.rgb(250, 250, 250));//設置標題顏色
toolbar.setSubtitleTextColor(Color.rgb(250, 250, 250));//設置子標題顏色
toolbar.inflateMenu(R.menu.base_toolbar_menu);//設置右上角的填充菜單
toolbar.setOnMenuItemClickListener(new Toolbar.OnMenuItemClickListener() {
@Override
public boolean onMenuItemClick(MenuItem item) {
return true;
}
});
}
}
設置更多Menu的icon:
<item name="actionOverflowButtonStyle">@style/ActionButton.Overflow.White</item>
<style name="ActionButton.Overflow.White"
parent="android:style/Widget.Material.Light.ActionButton.Overflow">
<item name="android:src">@mipmap/ic_more</item>
</style>
設置返回menu的icon
toolbar.setNavigationIcon(R.mipmap.ic_drawer_home);
通過着色改變顏色
Drawable drawable = getResources().getDrawable(R.drawable.abc_ic_ab_back_mtrl_am_alpha);
drawable.setColorFilter(Color.RED, PorterDuff.Mode.SRC_IN);
toolbar.setNavigationIcon(R.mipmap.ic_drawer_home);
精衛填坑
- 坑1:在XML中設置Toolbar屬性不生效
<android.support.v7.widget.Toolbar
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@color/color_0176da"
android:logo="@mipmap/ic_launcher"
android:navigationIcon="@mipmap/ic_drawer_home"
android:subtitle="456"
android:title="123">
解決方法:在根佈局中加入自定義屬性的命名空間
xmlns:toolbar="http://schemas.android.com/apk/res-auto"
<android.support.v7.widget.Toolbar
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@color/color_0176da"
toolbar:navigationIcon="@mipmap/ic_drawer_home"
toolbar:logo="@mipmap/ic_launcher"
toolbar:subtitle="456"
toolbar:title="123">
爲什麼會出現這種問題呢?我猜測是因爲這個控件是兼容版的控件,用 android:xxx 設置無效是的這些屬性是在兼容包中,不在默認的Android SDK中,所以我們需要額外的引入。至於爲什麼IDE不報錯,估計就是bug了吧!
坑2:menu字體顏色不能改變
在style中設置:<item name="actionMenuTextColor">#ffffff</item>
並沒有用
網上有人建議直接改全局的字體顏色
在style中設置:<item name="android:textColorPrimary">#ffffff</item>
但是這樣其它所有文字的默認顏色都變了,慎重慎重!坑3:ActionMode不能懸浮
解決方法:在style中設置懸浮<item name="windowActionModeOverlay">true</item>
坑4:自定義的View位於 title 、 subtitle 和 actionmenu 之間,這意味着,如果 title 和 subtitle 都在,且 actionmenu選項 太多的時候,留給自定義View的空間就越小
坑5:必須設置爲沒有ActionBar
使用Toolbar必須要求當前Activity沒有ActionBar,方法有兩種。<style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar"> supportRequestWindowFeature(Window.FEATURE_NO_TITLE); RequestWindowFeature(Window.FEATURE_NO_TITLE);
FloatingActionButton
FloatingActionButton從名字可以看出它是一個浮動的按鈕,它是一個帶有陰影的圓形按鈕,可以通過fabSize來改變其大小,主要負責界面的基本操作,這個按鈕總體來說還是比較簡單的。
默認FloatingActionButton 的背景色是應用主題的 colorAccent(其實MD中的控件主題默認基本都是應用的這個主題),可以通過app:backgroundTint 屬性或者setBackgroundTintList (ColorStateList tint)方法去改變背景顏色。
- FloatingActionButton 的大小尺寸,可以用過app:fabSize 屬性設置
- android:src屬性改變drawable
- app:rippleColor設置點擊button時候的顏色(水波紋效果)
- app:borderWidth設置 button 的邊框寬度
- app:elevation設置普通狀態陰影的深度(默認是 6dp)
- app:pressedTranslationZ設置點擊狀態的陰影深度(默認是 12dp)
<android.support.design.widget.FloatingActionButton
android:id="@+id/fab_search"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_alignParentRight="true"
android:layout_margin="20dp"
android:clickable="true"
android:src="@android:drawable/ic_dialog_email"
app:borderWidth="1dp"
app:fabSize="normal"
app:rippleColor="#0000ff"
app:elevation="3dp"
app:pressedTranslationZ="6dp"/>
NavigationView
NavigationView可以和DrawerLayout一起結合使用
代碼:
<android.support.v4.widget.DrawerLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/drawer_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true">
<android.support.design.widget.NavigationView
android:id="@+id/navigation_view"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_gravity="start"
app:headerLayout="@layout/navigation_header"
app:menu="@menu/drawer" />
</android.support.v4.widget.DrawerLayout>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="192dp"
android:background="?attr/colorPrimaryDark"
android:gravity="center"
android:orientation="vertical"
android:padding="16dp"
android:theme="@style/ThemeOverlay.AppCompat.Dark">
<ImageView
android:layout_width="72dp"
android:layout_height="72dp"
android:src="@mipmap/profile"
app:border_color="@color/primary_light"
app:border_width="2dp" />
<TextView
android:layout_marginTop="10dp"
android:textSize="18sp"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="APP開發者"/>
</LinearLayout>
和普通的menu比,就多了單選屬性
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
tools:context=".MainActivity">
<group android:checkableBehavior="single">
<item
android:id="@+id/navigation_item_example"
android:icon="@drawable/ic_favorite"
android:title="@string/navigation_example" />
<item
android:id="@+id/navigation_item_blog"
android:icon="@drawable/ic_favorite"
android:title="@string/navigation_my_blog" />
<item
android:id="@+id/navigation_item_about"
android:icon="@drawable/ic_favorite"
android:title="@string/navigation_about" />
</group>
</menu>
navigationView.setNavigationItemSelectedListener(
new NavigationView.OnNavigationItemSelectedListener() {
@Override
public boolean onNavigationItemSelected(MenuItem menuItem) {
return true;
}
});
在Material Design中,Navigation drawer導航抽屜,被設計用於應用導航,提供了一種通用的導航方式,體現了設計的一致性。
而NavigationView的典型用途就是配合之前v4包的DrawerLayout,作爲其中的Drawer部分,即導航菜單的本體部分。NavigationView是一個導航菜單框架,使用menu資源填充數據,使我們可以更簡單高效的實現導航菜單。它提供了不錯的默認樣式、選中項高亮、分組單選、分組子標題、以及可選的Header。
TextInputLayout
使用過EditText的同學肯定知道,有一個叫hint的屬性,它可以提示用戶此處應該輸入什麼內容,然而當用戶輸入真實內容之後,hint的提示內容就消失了,用戶的體驗效果是十分不好的,TextInputLayout的出現解決了這個問題。
初始狀態
點擊輸入內容
當用戶在輸入的時候hint的內容就會跑到輸入內容的上邊去,並且是以動畫的形式,體驗非常好。其中TextInputLayout中字體的顏色是style文件中的colorAccent。
看下代碼:
<android.support.design.widget.TextInputLayout
android:id="@+id/til"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:errorEnabled="true">
<EditText
android:id="@+id/et"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="Password"/>
</android.support.design.widget.TextInputLayout>
textLayout = (TextInputLayout)findViewById(R.id.til);
textLayout.setHint("Password");
非常簡單的使用方法,只是在EditText外再套一層TextInputLayout。
錯誤提示
TextInputLayout還有一個挺不錯的設計,就是錯誤提示,當用戶輸入內容不正確時,可以在底部彈出一個錯誤提示。要使用錯誤提示,需要先設置errorEnable爲true。
textInputLayout.setErrorEnabled(true);
app:errorEnable=”true”
然後調用setError(“輸入錯誤”);
設置樣式
<style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">
<item name="colorAccent">#3498db</item>
</style>
TabLayout
我們在使用viewpager的時候,經常會使用TabPageIndicator來與其配合。達到很漂亮的滑動效果。但是TabPageIndicator是第三方的,而且比較老了,當然了現在很多大神都已經開始自己寫TabPageIndicator來滿足需求,在2015年的google大會上,新的Android Support Design庫就包含了TabLayout,它可以實現TabPageIndicator的效果,而且兼容性非常好,最低可以兼容到2.2,32個贊不能再少了。
XML代碼
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:orientation="vertical">
<android.support.design.widget.TabLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@color/titleBlue"
app:tabIndicatorColor="@color/white"
app:tabSelectedTextColor="@color/gray"
app:tabTextColor="@color/white" />
<android.support.v4.view.ViewPager
android:layout_width="fill_parent"
android:layout_height="0dp"
android:layout_weight="1" />
</LinearLayout>
java代碼
TabLayout tabLayout;
tabLayout = (TabLayout)view.findViewById(R.id.tab_FindFragment_title);
tabLayout.setTabGravity(TabLayout.GRAVITY_FILL);
tabLayout.setTabMode(TabLayout.MODE_FIXED);
tabLayout.addTab(tabLayout.newTab().setText("Tab1"));
tabLayout.addTab(tabLayout.newTab().setText("Tab2"));
tabLayout.addTab(tabLayout.newTab().setText("Tab3"));
tabLayout.addTab(tabLayout.newTab().setText("Tab4"));
viewPager.stAdapter(adapter);
tabLayout.setViewPager(viewPager);
//tabLayout.setupWithViewPager(viewPager);
屬性
tabLayout.setTabMode(TabLayout.MODE_FIXED);
MODE_FIXED:固定模式,每一個Tab等寬,不管多少都顯示全部。
MODE_SCROLLABLE:滾動模式,適合多Tab,長短不一
tabLayout.setTabGravity(TabLayout.GRAVITY_FILL);
GRAVITY_CENTER:居中顯示
GRAVITY_FILL:充滿整個寬度
setIcon: tab.setIcon(R.drawable.ic_1);
要icon橫向顯示,可以使用setCustomView
tab.setCustomView(getCustomView(index));
精衛填坑
tabLayout.setupWithViewPager(viewPager);
看源碼,這個方法中調用了setTabsFromPagerAdapter(adapter);
public void setTabsFromPagerAdapter(@NonNull PagerAdapter adapter) {
removeAllTabs();
for (int i = 0, count = adapter.getCount(); i < count; i++) {
addTab(newTab().setText(adapter.getPageTitle(i)));
}
}
removeAllTabs()
這個就是說把前面所有tablayout添加的view都刪掉。也就是說在之前不管怎麼處理view都被幹掉。然後設置爲PagerAdapter返回的title。
tabLayout.addTab(tab1);
tabLayout.addTab(tab2);
tabLayout.addTab(tab3);
tabLayout.addTab(tab4);
tabLayout.setupWithViewPager(viewPager);
tabLayout.setupWithViewPager(viewPager);
tabLayout.addTab(tab1);
tabLayout.addTab(tab2);
tabLayout.addTab(tab3);
tabLayout.addTab(tab4);
通過Adapter返回title
@Override
public CharSequence getPageTitle(int position) {
return “xxx”;
}
Notification
Notification作爲一個時間觸發性的交互提示接口,讓我們獲得消息的時候,在狀態欄,鎖屏界面顯示相應的消息
Google在Android5.X中,又進一步的改進了通知欄,優化了Notification,在5.X設備上一個標準的通知是這樣的,長按會顯示消息的來源。
- 基本的Notification
頂部顯示橫條- 摺疊式Notification
下拉時顯示擴展模式,高度變高- 懸掛式Notification
在頂部懸浮顯示,提示性更強- 顯示等級的Notification
VISIBILITY_PUBLIC 只有在沒有鎖屏時會顯示通知
VISIBILITY_PRIVATE 任何情況都會顯示通知
VISIBILITY_SECRET 在安全鎖和沒有鎖屏的情況下顯示通知
AppbarLayout、CollapsingToolbarLayout、NestedScrollView、CoordinatorLayout
這幾個控件就不一一說了,把他們合在一起寫一個Demo一看便懂。Demo效果還是很明顯的。
下載鏈接:http://download.csdn.net/detail/u011002668/9583550
RecyclerView
這個控件是Material Design新增控件中最複雜的,但是可能也是大家最熟悉的,因爲很多人都開始使用它來替代ListView了,關於RecyclerView,張鴻洋有一片博客說的非常好了,很全面,這裏我就不再說了,直接把鏈接發出來。
http://download.csdn.net/detail/u011002668/9583550
總結
到這裏,Material Design的UI控件已經基本講完了,下一篇也是最後一篇,會講一下Material動畫,相信也有很多東西會讓大家眼前一亮。
本期節目就到這裏,感謝大家收看,我們下期再見~