最近研究了一下Material Design和Android 5的新特性,這裏作下總結概括。html
Material Design在我認爲就是相似於卡片同樣的設計,固然並不僅是卡片,material design能夠把一個佈局或者控件當作實際生活中得一個卡片來對待。android
那麼具備如下幾個方面的屬性(具體參照 http://www.google.com/design/spec/material-design/introduction.html 上面某些效果看起來仍是挺炫的):git
同時material design定義了許多了規範,是通過google產品設計工程師用心總結起來,整體看起來體驗蠻炫。具體效果能夠參照chrome的新書籤,感受比之前高大上許多。在手機設備上也定義了一些規範,這裏只總結我的較爲關注和在手機上比較好實現的一些規範。github
佈局:layout通常內容距離邊界邊距爲16dpchrome
可觸摸控件:大小通常爲48*48dp,實際內容爲40*40或者24*24dpapi
imageView能夠經過設置padding來實現或者讓設計截圖留好padding緩存
相似按鈕這樣的可觸摸控件通常可觸摸高度爲48dp,實際高度爲36dp網絡
那麼在安卓內該如何實現這樣的按鈕?app
It's big problem,這裏想到了兩種方式,但沒有徹底實現:
less
material design定義了button控件有三種模式,以下圖:
分別爲:
這三種按鈕建議使用頻率是 flat button>raised button>floating action button,主要是爲了不過多的層疊
後面還列出一些其餘material design列出的規範:
Dividers——1dp thick,opacity 12% black or 12% white
Snackbars & toasts
• Single-line snackbar height: 48dp
• Multi-line snackbar height: 80dp
• Text: Roboto Regular 14sp
• Action button: Roboto Medium 14sp, all-caps text
• Default background fill: #323232 100%
以上內容是關於對於material design的一個簡單介紹和我的概括,不少具體規範能夠參照官方material design手冊,設計應該比碼農要關注更多吧,讀完但是要費蠻多功夫的。後面的內容是我的使用Android對material design的實現,其中有一部分摘自於網絡內容,尤爲是圖片,算我盜版唄。
首先,Android 5提供了material theme實現了大多數的控件的樣式,可是我的推薦使用Appcompat包得Theme.AppCompat,其實際上就是繼承了material theme,並實現了對5下部分控件的兼容,material design有如下主題能夠選擇:
Theme.Material (dark version)
Theme.Material.Light (light version)
Theme.Material.Light.DarkActionBar
Theme.AppCompat
Theme.AppCompat.Light
Theme.AppCompat.Light.NoActionBar
Theme.AppCompat.NoActionBar
其次,要配置調色板,通常能夠配置的顏色如圖所示:
其代碼大概能夠是
<resources> <style name="AppTheme" parent="android:Theme.Material"> <item name="android:colorPrimary">@color/primary</item> <item name="android:colorPrimaryDark">@color/primary_dark</item> <item name="android:colorAccent">@color/accent</item> </style> </resources>
這部分具體在個人demo中以下:
<style name="AppBaseTheme" parent="Theme.AppCompat.Light.NoActionBar"> <!-- 主要文字顏色 --> <item name="android:textColorPrimary">#ffaa66cc</item> <!-- 窗口的背景顏色 --> <!--<item name="android:windowBackground">@android:color/white</item>--> <!-- 默認switch顏色 --> <item name="colorSwitchThumbNormal">#ffcc0000</item> <item name="colorControlActivated">#ff669900</item> <item name="toolbarStyle">@style/MyToolBar</item> <item name="switchStyle">@style/MySwitch</item> <item name="searchViewStyle">@style/MySearchViewStyle</item> </style> <style name="AppTheme" parent="@style/AppBaseTheme"/>
<application android:name=".common.MyApplication" android:allowBackup="true" android:icon="@mipmap/ic_launcher" android:label="@string/app_name" android:theme="@style/AppTheme"> ...
其中涉及到material design提到的三種組件:
System bar(Status bar) ——即android手機顯示電量,時間等狀態的系統轉太爛;height:24p ,color:通常暗色,也能夠設計爲應用的一種元素色或者半透明
Android navigation bar —— android的導航欄,這裏指左右導航欄,其實安卓手機底部的有返回鍵、home鍵的這一欄也稱爲navigation bar,在屏幕上的時虛擬導航欄,是在手機硬件上得稱爲硬件導航欄;height:48p,color:同status bar側邊導航;推薦:左導航右是當前頁的二級內容 width=Screen width - 56dp;最大width:左320dp,右:全屏
Tools bar -> App bar (Action bar),即系統狀態欄下應用的標題欄,過去是action bar,如今通常用toolbar;橫屏48dp 豎屏最小56dp,菜單欄通常是一個薄紙片,而不是bar的一個擴展
這裏關於系統的狀態欄和底部導航欄有些配置,介紹以下:
隱藏StatusBar——一、style定義android:windowFullScreen爲true;二、getWindow().addFlags(WindowManager.LayoutParams.FLAY_FULLSCREEN);三、View.setSystemUiVisibility(View.SYSTEM_UI_FLAG_FULLSCREENSYSTEM_UI_FLAG)
隱藏NavigationBar(API 14)——view.setSystemUiVisibility(View.SYSTEM_UI_FLAG_HIDE_NAVIGATION|View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY);
設置顏色(API 21)——getWindow().setStatusBarColor(…);getWindow().setNavigationBarColor();
設置透明(API 14)
getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION)
能夠經過View的一些常量來改變屏幕的一些特性(View.setSystemUiVisibility(UiOptions)(API 11)),具體以下(隱藏系統欄和低能模式會在有app bar時失效):
SYSTEM_UI_FLAG_FULLSCREEN 全屏隱藏系統狀態欄 (lean back)模式
SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN 全屏可是不隱藏系統狀態欄
SYSTEM_UI_FLAG_HIDE_NAVIGATION 全屏並隱藏導航欄
SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION 全屏不隱藏導航欄
SYSTEM_UI_FLAG_LOW_PROFILE 低能狀態
SYSTEM_UI_FLAG_LAYOUT_STABLE 保持狀態欄和導航欄佈局穩定,相似於invisible
SYSTEM_UI_FLAG_IMMERSIVE immersive 模式
SYSTEM_UI_FLAG_IMMERSIVE_STICKY 不清除flag的immersive模式,過一段時間隱藏的系統欄和導航欄會再次自動隱藏
具體效果參照demo的StatusBarActivity。
<ripple xmlns:android="http://schemas.android.com/apk/res/android" android:color="@android:color/holo_red_dark"> <item android:drawable="@android:color/holo_green_dark"/> </ripple>`
Everything provided by AppCompat’s toolbar (action modes, etc)
EditText
Spinner
CheckBox
RadioButton
SwitchCompat
CheckedTextView
res/values/styles.xml.
<style name="AppBaseTheme" parent="Theme.AppCompat.Light"> <item name="windowActionBar">false</item> <item name="windowNoTitle">true</item> <!-- 主要文字顏色 --> <item name="android:textColorPrimary">#ffaa66cc</item> <item name="android:textColorHighlight">@color/abc_primary_text_material_dark</item> <item name="colorPrimaryDark">#ffaa66cc</item> <item name="colorPrimary">#ffaa66cc</item> <item name="colorAccent">?attr/colorAccent</item> <!-- 窗口的背景顏色 --> <!--<item name="android:windowBackground">@android:color/white</item>--> <!-- 默認switch顏色 --> <item name="colorSwitchThumbNormal">#ffcc0000</item> <item name="colorControlActivated">#ff669900</item> <item name="toolbarStyle">@style/MyToolBar</item> <item name="switchStyle">@style/MySwitch</item> <item name="searchViewStyle">@style/MySearchViewStyle</item> <item name="editTextStyle">@style/Widget.AppCompat.EditText</item> <item name="buttonStyle">@style/MyButton</item> <item name="dialogTheme">@style/Theme.AppCompat.Light.Dialog</item> </style>
res/values-v21/styles.xml.
<resources xmlns:android="http://schemas.android.com/apk/res/android"> <style name="AppTheme" parent="@style/AppBaseTheme"> <!-- 系統狀態欄 顏色--> <item name="android:colorPrimaryDark">@android:color/holo_green_dark</item> <!-- 底部導航欄顏色 --> <item name="android:navigationBarColor">@android:color/holo_purple</item> <item name="android:colorAccent">?attr/colorAccent</item> <!-- 觸摸顏色,包括button ripple 背景--> <item name="android:colorControlHighlight">@android:color/holo_red_dark</item> </style> </resources>
res/layout/my_activity.xml
res/layout-v21/my_activity.xml
RecyclerView是官方首先推出來的一個大控件,官方但願關於展現集合類型的數據均用Recyclerview來實現,能夠用來替代listview和gridview;他對比listview和gridview有如下特色
簡單使用:
StaggeredGridLayoutManager指佈局按照瀑布流模式來排列布局
1 private void initRecycler() { 2 // use this setting to improve performance if you know that changes 3 4 RecyclerView.LayoutManager layoutManager = new StaggeredGridLayoutManager(3, OrientationHelper.VERTICAL); 5 RecyclerView.LayoutManager layoutManager1 = new LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false); 6 RecyclerView.LayoutManager layoutManager2 = new LinearLayoutManager(this, LinearLayoutManager.HORIZONTAL, false); 7 RecyclerView.LayoutManager layoutManager3 = new GridLayoutManager(this, 3); 8 mRecyclerView.setLayoutManager(layoutManager); 9 mRecyclerView.setAdapter(new Adapter(Arrays.asList("button & background", "status bar & nav bar", 10 "cardView & ripple", "toolbar", "switchCompat", "exit", "add1", "add2"))); 11 // in content do not change the layout size of the RecyclerView 12 mRecyclerView.setHasFixedSize(false); 13 }
1 private class Adapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> implements View.OnClickListener { 2 private List<String> list; 3 private OnItemClickListener listener; 4 5 public Adapter(List<String> list) { 6 super(); 7 this.list = new ArrayList<>(list); 8 listener = new OnItemClickListener() { 9 @Override 10 public void onItemClick(View v, int layoutPosition) { 11 onClick(v); 12 } 13 }; 14 } 15 16 @Override 17 public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { 18 Context context = parent.getContext(); 19 if (viewType == 1) { 20 ImageView imageView = new ImageView(context); 21 imageView.setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, 150)); 22 return new MyViewHolder(new ImageView(context)); 23 } 24 Button btn = new Button(context); 25 btn.setMinHeight((int) (context.getResources().getDisplayMetrics().density * (36 + random.nextInt(36) + 0.5f))); 26 return new MyViewHolder(btn); 27 } 28 29 @Override 30 public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) { 31 if (position != getItemCount() - 1) { 32 ((Button) holder.itemView).setText(list.get(position)); 33 } else { 34 ((ImageView) holder.itemView).setImageResource(R.mipmap.ic_launcher); 35 } 36 } 37 38 @Override 39 public int getItemViewType(int position) { 40 if (position == getItemCount() - 1) { 41 return 1; 42 } 43 return super.getItemViewType(position); 44 } 45 46 @Override 47 public int getItemCount() { 48 return list == null ? 0 : list.size() + 1; 49 } 50 51 public void add(int position, String str) { 52 list.add(position, str); 53 notifyItemInserted(position); 54 } 55 56 public void delete(int position) { 57 list.remove(position); 58 notifyItemRemoved(position); 59 } 60 61 @Override 62 public void onClick(View v) { 63 int index; 64 switch ((String) v.getTag()) { 65 case "button & background": 66 if (Utils.hasLollipop()) { 67 getWindow().setExitTransition(new Fade()); 68 } 69 70 startActivity( 71 new Intent(HomeActivity.this, ButtonActivity.class), 72 ActivityOptions.makeCustomAnimation(HomeActivity.this, android.R.anim.fade_in, 73 android.R.anim.slide_out_right).toBundle()); 74 break; 75 case "status bar & nav bar": 76 startActivity(new Intent(HomeActivity.this, StatusBarActivity.class)); 77 break; 78 case "cardView & ripple": 79 startActivity(new Intent(HomeActivity.this, CardViewActivity.class)); 80 break; 81 case "toolbar": 82 startActivity(new Intent(HomeActivity.this, ToolBarActivity.class)); 83 break; 84 case "switchCompat": 85 startActivity(new Intent(HomeActivity.this, SwitchActivity.class)); 86 break; 87 case "exit": 88 exit(); 89 break; 90 case "delete 1": 91 delete(list.indexOf("delete 1")); 92 break; 93 case "delete 2": 94 delete(list.indexOf("delete 2")); 95 break; 96 case "add1": 97 index = list.indexOf("delete 1"); 98 if (index < 0) { 99 add(list.indexOf("add1"), "delete 1"); 100 } 101 break; 102 case "add2": 103 index = list.indexOf("delete 2"); 104 if (index < 0) { 105 add(list.indexOf("add1"), "delete 2"); 106 } 107 break; 108 } 109 } 110 111 private class MyViewHolder extends RecyclerView.ViewHolder { 112 public MyViewHolder(View itemView) { 113 super(itemView); 114 115 if (listener != null) { 116 itemView.setOnClickListener(new View.OnClickListener() { 117 @Override 118 public void onClick(View v) { 119 if (v.getTag() == null) { 120 v.setTag(list.get(getLayoutPosition())); 121 } 122 listener.onItemClick(v, getLayoutPosition()); 123 } 124 }); 125 } 126 } 127 128 @Override 129 public String toString() { 130 return super.toString(); 131 } 132 } 133 }
其中,onCreateViewHolder方法用來建立對應的Viewholder,onBindViewHolder用來指定當viewholder被綁定時該作什麼操做,getItemViewType方法指定數據類型,好生成不一樣類型的View。
PS:
Recyclerview內部定義的position有兩種position,他們在大多時候相同,僅在某些狀況下不一致,以下:
官方上說list大多數狀況是瓷磚,四角方方正正,但cardview是真的卡片。那麼CardView在android的實現其實能夠設置圓角自帶陰影的FrameLayout,適用狀況:文字內容在三行以上,一個佈局有兩個以上的動做事件;其餘狀況請酌情考慮使用list,不要濫用。如下是幾個要點:
palette是官方給的一個新API,能夠從一個圖片抽取6種不一樣元素的顏色,這個API使得android的界面能夠顯得更加富有變化和活潑,好比在一個listview中,抽取每一個item的圖片來做爲這個item的標題欄,來區分這個item和其餘item的顏色分類;或者抽從app的一個主要圖片抽取顏色賦予到status bar等等。
官方的這個類只提供了6種顏色,其實也是提取某些範圍的顏色值,提取出來的顏色是徹底不透明的。具體用法以下:
1 build = Palette.from(((BitmapDrawable) getResources().getDrawable(R.mipmap.ic_launcher)).getBitmap()) 2 .maximumColorCount(16).resizeBitmapSize(192); 3 build.generate(new Palette.PaletteAsyncListener() { 4 @Override 5 public void onGenerated(Palette palette) { 6 Palette.Swatch swatch = palette.getVibrantSwatch(); 7 int tc = swatch.getTitleTextColor(), bc = swatch.getBodyTextColor(); 8 if (Utils.hasLollipop()) { 9 getWindow().setStatusBarColor(swatch.getRgb()); 10 } 11 } 12 });
PS:建議使用22.1以上的support包,效率會提高5倍以上
AppCompatActivity用來代替ActionBarActivity,提供在安卓全部版本一致性的App bar,即Toolbar。AppCompatActivity要求使用Theme.AppCompact主題或者該主題的子類。之後建議使用AppCompatActivity來當作全部activity的父類。
那麼如今有個問題,咱們爲何要使用toolbar,我我的總結了如下三點緣由:
那麼ToolBar默認提供了哪些強大的功能:
如下三張圖分別展現了這三個功能,具體示例和實現能夠參照demo
toolbar默認元素圖以下:
若是但願toolbar充當如圖系統中的app bar,那麼須要在OnCreate中調用setSupportActionBar(toolbar),這時toolbar即成爲系統的app bar,擁有action bar絕大多數功能。
設置NavigationIcon,需在setSupportActionBar後調用toolbar.setNavigationIcon;
設置Logo,主標題和副標題調用setLogo、setTitle、setSubTitle便可。若是不但願顯示toolbar自帶的標題,那麼能夠設置標題爲"",注意爲null無效;或者設置getSupportActionBar().setDisplayShowTitleEnabled(false);
設置toolbar的NavigationIcon隨着drawerlayout的抽屜變化而改變圖標,設置getSupportActionBar().setDisplayHomeAsUpEnabled(true)和getSupportActionBar().setHomeButtonEnabled(true);
設置右側菜單,需在activity的onCreateOptionsMenu方法中設置本身的菜單,而後能夠在onOptionsItemSelected方法中捕獲到每一個菜單被選擇到獲得的事件,也能夠toolbar本身設置監聽;
在android 5要退出應用通常啓動app的一個singleTop的activity,而後再finish這個activity。在android 5以上不推薦由咱們來退出程序,應該交給系統來完成這樣的工做,若是在android 5仍是須要這樣的功能,能夠經過下面簡單的代碼實現:
1 List<ActivityManager.AppTask> list = activityManager.getAppTasks(); 2 for (ActivityManager.AppTask appTask : list) { appTask.finishAndRemoveTask(); 3 }
在android 5之前都是經過拿getRuningTask來獲取相關信息,可是如今android 5認爲這個方法會泄露隱私,所以廢棄了getRuningTask等方法,所以在android 5以上須要經過runningAppProcessInfo來獲取相關信息,具體代碼以下:
1 boolean isOnForeGround = false; List<ActivityManager.RunningAppProcessInfo> pList =activityManager.getRunningAppProcesses(); for(ActivityManager.RunningAppProcessInfo processInfo : pList){ if(processInfo.importance == ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND){ 2 for(String activityPack : processInfo.pkgList){ if(activityPack.equals(context.getPackageName())){ isOnForeGround = true; 3 return; 4 } 5 } 6 } }
即更改如彈出dialog默認的背景,能夠由下面代碼來作
1 WindowManager.LayoutParams lp = dialog.getWindow().getAttributes(); 2 lp.alpha = 0.5f; 3 lp.dimAmount = 0.6f; 4 dialog.getWindow().setAttributes(lp); dialog.getWindow().setFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND, WindowManager.LayoutParams.FLAG_DIM_BEHIND);
大概總結這麼多,demo下載地址
https://github.com/jonyChina162/LollipopTest
裏面還有我本身作的一個ppt