《第一行代码》第2版思维导图及所有学习笔记目录
主题定义
对应于ActionBar,由于ActionBar被限定于活动顶部,不能实现一些Material Design效果,因此已不推荐使用了。
ActionBar来自项目指定的主题定义的显示,如下:
1 2
| # AndroidManifest.xml android:theme="@style/AppTheme"
|
主题的定义:
1 2 3 4 5 6 7 8 9
| # res/values/styles.xml <resources> <style name="AppTheme" parent = "Theme.AppCompat.Light.DarkActionBar"> # 定义指定父主题 <item name="colorPrimary">@color/colorPrimary</item> <item name="colorPrimaryDark">@color/colorPrimaryDark</item> <item name="colorAccent">@color/colorAccent</item> </resources>
|
父主题Theme.AppCompat.Light.DarkActionBar自带了ActionBar,使用Toolbar则需要将父主题替换,主要有如下两种可选主题:
1 2
| Theme.AppCompat.Light.NoActionBar : 淡色主题,主题颜色设为淡色,陪衬颜色设为深色 Theme.AppCompat.NoActionBar: 深色主题,界面主题颜色设为深色,陪衬颜色设为淡色
|
为了使用Toolbar修改主题定义:
1 2 3 4 5 6
| <resources> <style name="AppTheme" parent = "Theme.AppCompat.Light.NoActionBar"> # 定义指定父主题 ... </resources>
|
主题中的颜色控制属性,主要如下几种:
1 2 3 4 5 6
| colorPrimary 指定标题栏背影色 colorPrimaryDrak 指定状态栏颜色 textColorPrimary 指定标题文字颜色 windowBackground 背景色 navigationBarColor 指定状态导航条颜色 colorAccent 指定浮动按钮颜色以及一些控件选择状态
|
每个属性指定颜色的位置如下:
Toolbar控件是由appcompat-v7库提供,引入布局:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| <FrameLayout ... xmlns:app="http://schemas.android.com/apk/res-auto" //引入app命名空间,因为android:attribute之类属性只支持5.0及以上系统,5.0以下需要使用app:xx来兼容 <android.support.v7.widget.Toolbar android:id="@+id/toolbar" android:layout_width="match_parent" android:layout_height="?attr/actionBarSize" // 高度设置为actionBar的高度 android:background="?attr/colorPrimary" android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar" // 为了Toolbar单独使用深色主题让效果更好,通过此属性设置,如不设置这使用默认的浅色主题效果 app:popupTheme="@style/ThemeOverlay.AppCompat.Light" // 将Toolbar中菜单按钮弹出的菜单项设为浅色主题 app:layout_scrollFlags="enterAlways|snap|scroll" /> </FrameLayout>
|
加入代码:
1 2
| Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar); setSupportActionBar(toolbar);
|
修改标题栏显示内容:
1 2 3 4 5 6
| # AndroidManifest.xml <activity ... android:label="Fruits"> // 未指定的话,则显示application中android:label指定的内容 </activity>
|
添加action按钮:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47
| # 引入布局 <?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/backup" android:icon="@drawable/ic_backup" android:title="Backup" app:showAsAction="always"/> <item android:id="@+id/del" android:icon="@drawable/ic_delete" android:title="Delete" app:showAsAction="ifRoom"/> <item android:id="@+id/setting" android:icon="@drawable/ic_settings" android:title="Setting" app:showAsAction="never"/> </menu>
# 加入代码 @Override public boolean onCreateOptionsMenu(Menu menu) { getMenuInflater().inflate(R.menu.toolbar,menu); return true; }
@Override public boolean onOptionsItemSelected(MenuItem item) { switch (item.getItemId()) { case R.id.backup: break; case R.id.del: break; case R.id.setting: break; default: break; } return true; }
|
效果如下:
滑动菜单
DrawerLayout
DrawerLayout是一个布局,允许放入两个子控件,第一个控件是主屏幕中显示的内容,第二个子控件是滑动菜单中显示的内容。
引入布局:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| <?xml version="1.0" encoding="utf-8"?> <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"> <FrameLayout android:layout_width="match_parent" android:layout_height="match_parent"> <android.support.v7.widget.Toolbar ... /> </FrameLayout> <TextView android:layout_gravity="start" //表示根据系统语言判断第二界面隐藏在左边还是右边,比如英语和中文从左边开始,就为左边,也可指定“right" "left" "end" .../> //滑动界面先加载一个TextView </android.support.v4.widget.DrawerLayout>
|
加入导航按钮:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| private DrawerLayout mlayout;
mlayout = (DrawerLayout) findViewById(R.id.drawer_layout); ActionBar actionBar = getSupportActionBar(); if (actionBar != null) { actionBar.setDisplayHomeAsUpEnabled(true); actionBar.setHomeAsUpIndicator(R.drawable.ic_menu); } # 也可以直接在布局文件中加入: app:navigationIcon="@drawable/ic_menu"
@Override public boolean onOptionsItemSelected(MenuItem item) { switch (item.getItemId()) { ... case android.R.id.home: mlayout.openDrawer(GravityCompat.START); break; ...
|
NavigationView
NavigationView是Design Support提供的一个空间,可以让滑动菜单设计好看又简单,首先需要加入依赖:
1 2
| compile 'com.android.support:design:24.2.1' compile 'de.hdodenhof:circleimageview:2.1.0'
|
Circleimageview项目主页地址
准备滑动菜单页面布局:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60
| # menu <?xml version="1.0" encoding="utf-8"?> <menu xmlns:android="http://schemas.android.com/apk/res/android"> <group android:checkableBehavior="single"> //指定这一组菜单项单选 <item android:id="@+id/nav_call" android:icon="@drawable/nav_call" android:title="Call"/> <item android:id="@+id/nav_friends" android:icon="@drawable/nav_friends" android:title="Friends"/> <item android:id="@+id/nav_location" android:icon="@drawable/nav_location" android:title="Location"/> <item android:id="@+id/nav_mail" android:icon="@drawable/nav_mail" android:title="Mail"/> <item android:id="@+id/nav_task" android:icon="@drawable/nav_task" android:title="Tasks"/> </group> </menu>
# headerLayout <?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="180dp" android:padding="10dp" android:background="?attr/colorPrimary">
<de.hdodenhof.circleimageview.CircleImageView android:id="@+id/icon_image" android:layout_width="70dp" android:layout_height="70dp" android:src="@drawable/nav_icon" android:layout_centerInParent="true"/>
<TextView android:id="@+id/mail" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignParentBottom="true" android:text="huaqinalee@gmail.com" android:textColor="#FFF" android:textSize="14sp"/>
<TextView android:id="@+id/usr_name" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_above="@+id/mail" android:text="Andy Lee" android:textColor="#fff" android:textSize="14sp"/> </RelativeLayout>
|
引入滑动菜单页面布局:
1 2 3 4 5 6 7 8 9
| # 将前面的TextView换为NavigationView <android.support.design.widget.NavigationView android:id="@+id/nav_view" android:layout_width="match_parent" android:layout_height="match_parent" android:layout_gravity="start" app:menu="@menu/nav_menu" // 引入menu app:headerLayout="@layout/nav_header" // 引入headerLayout />
|
加入响应代码:
1 2 3 4 5 6 7 8 9
| NavigationView navView = (NavigationView) findViewById(R.id.nav_view);
navView.setNavigationItemSelectedListener(new NavigationView.OnNavigationItemSelectedListener() { @Override public boolean onNavigationItemSelected(@NonNull MenuItem item) { return false; }
|
效果如下:
悬浮按钮和可交互提示
引入悬浮按钮布局:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| <FrameLayout android:layout_width="match_parent" android:layout_height="match_parent">
<android.support.v7.widget.Toolbar ... /> <android.support.design.widget.FloatingActionButton android:id="@+id/fab" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="bottom|end" //对于中英文,右下角 android:layout_margin="16dp" android:src="@drawable/ic_done" app:elevation="8dp" //悬浮高度 /> </FrameLayout>
|
加入响应代码:
1 2 3 4 5 6
| FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab); fab.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { }
|
Snackbar
提供交互的提示,与Toast类似,加入代码:
1 2 3 4 5 6 7 8 9 10 11
|
Snackbar.make(v, "Data deleted", Snackbar.LENGTH_SHORT).setAction("Undo", new View.OnClickListener() { @Override public void onClick(View v) { Toast.makeText(MainActivity.this, "Del undo!", Toast.LENGTH_SHORT).show(); } }).show();
|
CoordinatorLayout
直接如上加入Snackbar,提示的内容直接弹出会覆盖悬浮按钮,体验不好,所以就需要引入加强版FrameLayout:CoordinatorLayout。此布局可以监听子控件,并且做出响应调整。
引入布局:
1 2 3 4 5 6 7 8
| # 替换掉FrameLayout <android.support.design.widget.CoordinatorLayout android:layout_width="match_parent" android:layout_height="match_parent"> ... <android.support.design.widget.FloatingActionButton .../> </android.support.design.widget.CoordinatorLayout>
|
这样替换布局后,悬浮按钮会自动上移以避免覆盖,效果如下:
Snackbar.make时传入的view是Snackbar本身,包含在CoordinatorLayout中,所以能响应,如若传入的是外面的View,则不能响应。
卡片式布局
CardView
CardView也是appcompat-v7库提供的一个FrameLayout,不过增加了圆角阴影等立体效果。
基本用法:
1 2 3 4 5 6 7 8 9
| <android.support.v7.widget.CardView android:layout_width="match_parent" android:layout_height="wrap_content" android:cardCornerRadius="4dp" // 圆角弧度 app:elevation="5dp"/>// 投影高度 <TextView .../> </android.support.v7.widget.CardView>
|
RecyclerView和CardView等控件实现水果列表效果
添加依赖:
1 2 3 4
| compile 'com.android.support:recyclerview-v7:24.2.1' compile 'com.android.support:cardview-v7:24.2.1' compile 'com.github.bumptech.glide:glide:3.7.0'
|
Glide项目主页地址
引入RecyclerView布局:
1 2 3 4 5 6 7 8 9 10
| <android.support.design.widget.CoordinatorLayout <android.support.v7.widget.Toolbar ..../> <android.support.v7.widget.RecyclerView android:id="@+id/recycler_view" android:layout_width="match_parent" android:layout_height="match_parent"/> ... </android.support.design.widget.CoordinatorLayout>
|
这样子RecyclerView会将Toolbar给遮住,因为CoordinatorLayout(类似FrameLayout)布局默认置于左上角,后面通过APPBarLayout解决此问题
定义一个Fruit实体类(只有name和imageId两个字段)。接下来需要为RecyclerView的子项指定自定义布局fruit_item.xml。
引入CardView布局:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| <?xml version="1.0" encoding="utf-8"?> <android.support.v7.widget.CardView 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="wrap_content" android:layout_margin="5dp" app:cardCornerRadius="4dp">
<LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical"> <ImageView android:id="@+id/fruit_image" android:scaleType="centerCrop" //指定图片缩放方式 .../> <TextView android:id="@+id/fruit_name" .../>
</LinearLayout>
</android.support.v7.widget.CardView>
|
添加RecyclerView适配器FruitAdapter,如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42
| public class FruitAdapter extends RecyclerView.Adapter<FruitAdapter.ViewHolder> { private Context mContext; private List<Fruit> mLists;
public FruitAdapter(List<Fruit> lists) { mLists = lists; }
@Override public FruitAdapter.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { if (mContext == null) { mContext = parent.getContext(); } View view = LayoutInflater.from(mContext).inflate(R.layout.fruit_item, parent, false); return viewHolder(view); }
@Override public void onBindViewHolder(FruitAdapter.ViewHolder holder, int position) { Fruit fruit = mLists.get(position); holder.fruitName.setText(fruit.getName()); Glide.with(mContext).load(fruit.getImageId()).into(holder.fruitImage); }
@Override public int getItemCount() { return mLists.size(); }
static class ViewHolder extends RecyclerView.ViewHolder { CardView cardView; ImageView fruitImage; TextView fruitName; public ViewHolder(View itemView) { super(itemView); cardView = (CardView) itemView; fruitImage = (ImageView) itemView.findViewById(R.id.fruit_image); fruitName = (TextView) itemView.findViewById(R.id.fruit_name); } } }
|
添加加载RecyclerView代码:
1 2 3 4 5 6
| ... RecyclerView recyclerView = (RecyclerView) findViewById(...); GridLayoutManager layoutManager = new GridLayoutManager(this, 2); recyclerView.setLayoutManager(layoutManager); adapter = new FruitAdapter(fruitList); recyclerView.setAdapter(adapter);
|
AppBarLayout
APPBarLayout也是Design Support提供,解决RecyclerView遮挡Toolbar问题只需两步:
- 将Toolbar嵌套到APPBarLayout中。
- 给RecyclerView指定一个布局行为。
修改布局如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| <android.support.design.widget.AppBarLayout android:layout_width="match_parent" android:layout_height="wrap_content">
<android.support.v7.widget.Toolbar app:layout_scrollFlags="enterAlways|snap|scroll" /* ** enterAlways - Toolbar跟随向下滚动并重新显示 ** snap - Toolbar还未完全隐藏或显示时,根据滚动距离自动选择 ** scroll - Toolbar跟着向上滚动并隐藏 */ .../> </android.support.design.widget.AppBarLayout> <android.support.v7.widget.RecyclerView ... app:layout_behavior="@string/appbar_scrolling_view_behavior"/>
|
下拉刷新
support-v4提供的SwipeRefreshLayout可以很简单的实现刷新功能。
引入布局:
1 2 3 4 5 6 7 8 9 10 11
| <android.support.v4.widget.SwipeRefreshLayout android:id="@+id/swipe_refresg" android:layout_width="match_parent" android:layout_height="match_parent" app:layout_behavior="@string/appbar_scrolling_view_behavior"> // 需要将上述的布局行为移到其父控件了
<android.support.v7.widget.RecyclerView .../>
</android.support.v4.widget.SwipeRefreshLayout>
|
代码实现:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
| private SwipeRefreshLayout swipeRefresh; swipeRefresh = (SwipeRefreshLayout)findViewById(R.id.swipe_refresg); swipeRefresh.setColorSchemeResources(R.color.colorPrimary); swipeRefresh.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() { @Override public void onRefresh() { refreshFruits(); } });
private void refreshFruits() { new Thread(new Runnable() { @Override public void run() { try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } runOnUiThread(new Runnable() { @Override public void run() { initFruits(); adapter.notifyDataSetChanged(); swipeRefresh.setRefreshing(false); } }); } }).start();
|
可折叠式标题栏
如下所示,左边必须作为右边的子控件才能存在:
CollapsingToolbarLayout -> AppBarLayout -> CoordinatorLayout.
新建一个水果详情展示页面布局(activity_fruit.xml)来应用可折叠式标题栏。
引入布局:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69
| <android.support.design.widget.CoordinatorLayout 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="match_parent" android:fitsSystemWindows="true"> # 标题部分 <android.support.design.widget.AppBarLayout ...> <android.support.design.widget.CollapsingToolbarLayout ... android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar" // Toolbar主题,移到此父控件 app:contentScrim="?attr/colorPrimary" //折叠背景色,折叠后就变成一个普通Toolbar了 app:layout_scrollFlags="scroll|exitUntilCollapsed" /* ** Toolbar属性,移到父控件了 ** exitUntilCollapsed - 表示折叠后保留不隐藏 */ android:fitsSystemWindows="true"> /*标题栏内容:图片+普通标题栏*/ <ImageView ... android:scaleType="centerCrop" app:layout_collapseMode="parallax" // 指定折叠模式,parallax-折叠时错位偏移 android:fitsSystemWindows="true"/>
<android.support.v7.widget.Toolbar android:id="@+id/toolbar" android:layout_width="match_parent" android:layout_height="?attr/actionBarSize" app:layout_collapseMode="pin" // 表示折叠过程中Toolbar位置不变 />
</android.support.design.widget.CollapsingToolbarLayout> </android.support.design.widget.AppBarLayout> # 详情页部分 <android.support.v4.widget.NestedScrollView ... app:layout_behavior="@string/appbar_scrolling_view_behavior"> /* ** 同ScrollView,允许滚动查看,不过多了响应滚动事件 ** 因为外使用CoordinatorLayout,需要用此控件或RecyclerView,用法同上RecyclerView一样 ** 只允许存在一个直接子布局 */
<LinearLayout ...>
<android.support.v7.widget.CardView ...>
<TextView .../>
</android.support.v7.widget.CardView>
</LinearLayout>
</android.support.v4.widget.NestedScrollView> # 增加一个悬浮按钮 <android.support.design.widget.FloatingActionButton ... app:layout_anchor="@id/appBar" //指定描点,这里写APPBarLayout的id,这样按钮就出现在标题栏区域 app:layout_anchorGravity="bottom|end"/> </android.support.design.widget.CoordinatorLayout>
|
加入代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63
| # Adapter中添加RecyclerView子项响应代码 public FruitAdapter.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { if (mContext == null) { mContext = parent.getContext(); } View view = LayoutInflater.from(mContext).inflate(R.layout.fruit_item, parent, false); final ViewHolder holder = new ViewHolder(view); holder.cardView.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { int position = holder.getAdapterPosition(); Fruit fruit = mLists.get(position); Intent intent = new Intent(mContext,FruitActivity.class); intent.putExtra(FruitActivity.FRUIT_NAME,fruit.getName()); intent.putExtra(FruitActivity.FRUIT_IMAGE_ID,fruit.getImageId()); mContext.startActivity(intent); } });
return holder; }
# 详情页面代码 public class FruitActivity extends AppCompatActivity { public static final String FRUIT_NAME = "fruit_name"; public static final String FRUIT_IMAGE_ID = "fruit_image_id";
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_fruit);
Intent intent = getIntent(); String fruitName = intent.getStringExtra(FRUIT_NAME); int fruitImageId = intent.getIntExtra(FRUIT_IMAGE_ID,0); ... setSupportActionBar(toolbar); ActionBar actionBar = getSupportActionBar(); if (actionBar != null) { actionBar.setDisplayHomeAsUpEnabled(true); }
collapsingToolbar.setTitle(fruitName); Glide.with(this).load(fruitImageId).into(fruitImageView); fruitText.setText(fruitContent);
}
... @Override public boolean onOptionsItemSelected(MenuItem item) { switch (item.getItemId()) { case android.R.id.home: finish(); return true; } return super.onOptionsItemSelected(item); } }
|
背景图片和系统状态栏融合
对ImageView及其所有父布局属性设定,如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| <?xml version="1.0" encoding="utf-8"?> <android.support.design.widget.CoordinatorLayout ... android:fitsSystemWindows="true"> <android.support.design.widget.AppBarLayout ... android:fitsSystemWindows="true"> <android.support.design.widget.CollapsingToolbarLayout ... android:fitsSystemWindows="true"> <ImageView ... android:fitsSystemWindows="true"/> ...
|
接下来需要将状态栏指定撑透明,但是android:statusBarColor属性是API21开始支持的(即Android5.0)。先为5.0以上新建一个values-21/styles.xml,如下:
1 2 3 4 5
| <resources> <style name="FruitActivityTheme" parent="AppTheme"> <item name="android:statusBarColor">@android:color/transparent</item> </style> <resources>
|
为了支持5.0以前,对values/styles.xml进行修改,如下:
1 2 3 4
| ... <style name="FruitActivityTheme" parent="AppTheme"> </style> # 因为5.0以前不能指定状态栏颜色,所以留空即可了。
|
最后让activity调用此主题,如下:
1 2 3 4 5
| # AndroidManifest.xml <activity ... android:theme="@style/FruitActivityTheme" </activiy>
|
android5.0以上的版本还有一种融合及透明状态栏的方式:
1 2 3 4 5 6
| # 参考coolweather融合必应图片,及头布局间隔设置 if (Build.VERSION.SDK_INT >= 21) { View decorView = getWindow().getDecorView(); decorView.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | View.SYSTEM_UI_FLAG_LAYOUT_STABLE); getWindow().setStatusBarColor(Color.TRANSPARENT); }
|
效果图如下:
示例源码
整个Material Design示例源码的地址如下:
Material Design示例源码。