Android学习笔记——Material Design
2017-08-17 13:02
106 查看
参考书籍:Android第一行代码(第二版).郭霖著
Material Design——谷歌发明的一套全新的界面设计语言,包含视觉、运动、互动效果等特性,Android5.0开始使用。相应的支持库Design Support库,将Material Design中最具代表性的一些控件和效果进行了封装。
新建MaterialTest项目。
1、Toolbar
Material控件。Toolbar不仅继承了ActionBar(只能位于活动顶部)的所有功能,且灵活性喝高,可配合其他控件完成一些Material Design效果。
任何一个新建项目,默认都会显示ActionBar,这是根据项目中指定的主题来显示的(标签中的android:theme属性指定)。可在资源文件中找到此主题(AppTheme):
这里的DarkActionBar是个深色的ActionBar主题。如要使用ToolBar代替ActionBar,需指定一个不带ActionBar的主题:Theme.AppCompat.NoActionBar(深色主题,主体颜色深,陪衬颜色淡)和Theme.AppCompat.Light.NoActionBar(淡色主题,主体颜色淡,陪衬颜色深)两种可选。
这里选用单色主题:
其中的属性代表的位置如下:
修改布局文件:
很多Material属性在5.0之前系统中不存在,为了兼容之前老版本,需使用app:attribute写法。之前把程序的主题指定为淡色主题,所以ToolBar也是淡色,ToolBar上面的各种元素会自动使用深色系(为和主体颜色区分),但很难看。为让ToolBar单独使用深色主题,使用android:theme属性指定为ThemeOverlay.AppCompat.Dark.ActionBar(但ToolBar上如有菜单按钮,弹出的菜单也会成深色,所以使用app:popupTheme(5.0中新增)将弹出的菜单指定成淡色主题)。
修改主程序:
运行出来的效果与ActionBar一样,但实际是ToolBar,已具备实现Material Design效果的能力。
修改标题栏文字内容:
添加一写action按钮。准备几张图片作为按钮图标,放在drawable-xxhdpi下,再在res目录下创建一个menu文件夹,在此文件夹下新建菜单资源文件toolbar.xml:
Toolbar中的action按钮只显示图标,菜单中的action按钮只显示文字。修改MainActivity:
运行程序:
2、滑动菜单
(1)DrawerLayout
滑动菜单就是将一些菜单选项隐藏起来,不放置在主屏幕上,可通过华东方式将菜单显示出来。可借助DrawerLayout控件实现。
DrawerLayout是一个布局,布局中允许放入两个直接子控件:第一个控件为主屏幕中显示内容,第二个为滑动菜单中显示的内容(此控件需指定layout_gravity属性,告诉DrawerLayout滑动菜单在屏幕左边left还是右边right,还可使用sart(根据系统语言判断,从左往右则为左边)/end)。修改activity_main.xml:
在Toolbar最左边加入一个导航按钮给用户提示,准备一张按钮图标图片,修改MainActivity:
运行程序:
(2)NavigationView
优化滑动菜单页面。NavigationView是Design Support库中提供的控件。须将库引入到项目中:
第二个库是一个开源项目CircleImageView,主页地址:https://github.com/hdodenhof/CircleImageView。
使用NavigationView需先准备menu(显示具体的菜单项)和headerLayout(显示头部布局)。准备所需图片,在menu文件夹下新建nav_menu.xml文件:
将组中菜单项设为单选。headerLayout可以随意定制,准备所需图片,新建布局文件nav_header.xml:
修改activity_main.xml:
替换之前的TextView,还需处理菜单点击事件,修改MainActivity:
运行程序:
4、悬浮按钮和可交互提示
立面设计代表——悬浮按钮。
(1)FloatingActionButton
Design Support中提供的控件。默认使用colorAccent作为按钮颜色。
准备响应图标,修改activity_main.xml:
可通过app:elevation属性指定悬浮按钮高度(值越大,投影范围越大,投影效果越淡),默认的足够。接下来处理点击事件,修改MainActivity:
方法与普通按钮一样。运行程序:
(2)Snackbar
Design Support提供,允许提示中加入一个可交互按钮(与Toast应用场景不同),用法简单,只是额外增加了一个按钮点击事件。修改MainActivity:
运行程序:
此Snackbar把悬浮按钮遮挡住了,要解决这个问题需要借助CoordinatorLayout.
(3)CoordinatorLayout
加强版的FrameLayout,有Design Support提供。可以监听其所有子控件的各种事件,自动帮助我们做出最为合理的响应。在布局文件中将原来的FrameLayout替换一下即可,运行程序:
由于传入的是FloatingActionButton本身view,而FloatingActionButton是CoordinatorLayout中的子控件,所以Snackbar的弹出隐藏会被CoordinatorLayout监听到。
5、卡片式布局
可让页面中的元素看起来像在卡片中一样,且还能有元角和投影。
(1)CardView
用于实现卡片式布局效果的重要控件,由appcompat-v7库提供。CardView也是一个FrameLayout,额外提供了圆角和阴影等效果,有立体感。
实现水果列表,需用到RecycleView、CardView控件,需先声明:
Glide是图片加载库,可用于加载本地图片、网络图片、GIF图片、本地视频等。项目主页:https://github.com/bumptech/glide。修改activity_main.xml:
定义一个实体类Fruit:
为RecyclerView子项指定一个自定义布局,在layout下新建fruit_item.xml:
centerCrop模式可让图片保持原有比例填充满ImageView,并将超出屏幕的部分裁剪掉。
为RecyclerView准备一个适配器,新建FruitAdapter类,让这个适配器继承自RecyclerView.Adapter,泛型指定为FruitAdapter.ViewHolder:
最后修改MainActivity:
运行程序:
(2)AppBarLayout
RecyclerView和Toolbar都放置在CoordinatorLayout中,由于CoordinatorLayout是加强版FrameLayout,其中的所有控件在不进行明确定位的情况下,默认摆放在布局的左上角,因此会出现遮挡。
可使用Design Support库提供的AppBarLayout工具。AppBarLayout实际上是一个垂直方向的LinearLayout,内部做了很多滚动事件的封装,并应用了些Material Design的设计理念。解决覆盖问题步骤:将Toolbar嵌套到AppBarLayout中;给RecyclerView指定一个布局行为。修改activity_main.xml:
RecyclerView滚动时就已将滚动事件都通知给AppBarLayout,当AppBarLayout接收到滚动事件时,它内部的子控件可以指定如何去影响这些事件,通过app:layout_scrollFlags属性实现。修改activity_main.xml:
scroll:RecyclerView向上滚动时,Toolbar会跟着一起向上滚动并实现隐藏;enterAlways: RecyclerView向下滚动时,Toolbar会跟着一起向下滚动并重新显示;snap:当Toolbar还没完全隐藏或显示时,会根据当前滚动的距离,自动选择是隐藏还是显示。
运行程序:
6、下拉刷新
SwipeRefreshLayout是用于实现下拉刷新功能的核心类,由support-v4库提供。将想要实现下拉刷新功能的控件放置其中,就可迅速让此控件支持下拉刷新。修改activity_main.xml:
还需在代码中处理具体的刷新逻辑,修改MainActivity:
运行程序:
7、可折叠式标题栏
Material Design中并没有规定标题栏必须长什么样,可以随意定制标题栏样式。需借助CollapsingToolbarLayout工具。
(1)CollapsingToolbarLayout
作用于Toolbar基础上的布局,由Design Support库提供。但只能作为AppBarLayout的直接子布局使用(AppBarLayout又必须是CoordinatorLayout的子布局)。
继续之前的项目,需要一个额外活动作为水果的详情展示界面,命名为FruitActivity,布局文件为activity_fruit.xml(主要分为两部分:水果标题栏和水果内容详情),标题栏部分使用CoordinatorLayout作为最外层布局:
接下来,修改FruitActivity:
处理RecyclerView点击事件打开FruitActivity.修改FruitAdapter:
运行程序:
往上拖动:
继续拖动:
(2)利用系统状态栏空间
Android5.0系统后可对状态栏的背景或颜色进行操作。这里可以实现系统差异型效果(5.0之前使用普通模式,之后使用背景状态栏融合模式)。
借助android: fitsSystemWindows属性。在CoordinatorLayout、AppBarLayout、CollapsingToolbarLayout这种嵌套结构布局中,将控件的fitsSystemWindows属性设为true就表示该控件会出现在系统状态栏里。此程序中对应的是ImageView控件(必须将其所有父布局都设置这个属性才行)。修改activity_fruit.xml:
光设属性还没用,须在程序主题中将状态栏颜色指定为透明色才行(android:statusBarColor属性从5.0系统开始才有)。系统差异化处理,在res下新建一个values-v21目录,在此目录下新建styles.xml文件:
对于5.0之前的系统,需对values/styles.xml进行修改:
定义了内部空的FruitActivityTheme主题,修改AndroidManifest.xml:
在Android5.0及以上系统运行程序:
Material Design——谷歌发明的一套全新的界面设计语言,包含视觉、运动、互动效果等特性,Android5.0开始使用。相应的支持库Design Support库,将Material Design中最具代表性的一些控件和效果进行了封装。
新建MaterialTest项目。
1、Toolbar
Material控件。Toolbar不仅继承了ActionBar(只能位于活动顶部)的所有功能,且灵活性喝高,可配合其他控件完成一些Material Design效果。
任何一个新建项目,默认都会显示ActionBar,这是根据项目中指定的主题来显示的(标签中的android:theme属性指定)。可在资源文件中找到此主题(AppTheme):
<resources> <!-- Base application theme. --> <style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar"> <!-- Customize your theme here. --> <item name="colorPrimary">@color/colorPrimary</item> <item name="colorPrimaryDark">@color/colorPrimaryDark</item> <item name="colorAccent">@color/colorAccent</item> </style> </resources>
这里的DarkActionBar是个深色的ActionBar主题。如要使用ToolBar代替ActionBar,需指定一个不带ActionBar的主题:Theme.AppCompat.NoActionBar(深色主题,主体颜色深,陪衬颜色淡)和Theme.AppCompat.Light.NoActionBar(淡色主题,主体颜色淡,陪衬颜色深)两种可选。
这里选用单色主题:
<resources> <!-- Base application theme. --> <style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar"> <!-- Customize your theme here. --> <item name="colorPrimary">@color/colorPrimary</item> <item name="colorPrimaryDark">@color/colorPrimaryDark</item> <item name="colorAccent">@color/colorAccent</item> </style> </resources>
其中的属性代表的位置如下:
修改布局文件:
<FrameLayout 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.support.v7.widget.Toolbar android:id="@+id/toolbar" android:layout_width="match_parent" android:layout_height="?attr/actionBarSize" android:background="?attr/colorPrimary" android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar" app:popupTheme="@style/ThemeOverlay.AppCompat.Light" /> </FrameLayout>
很多Material属性在5.0之前系统中不存在,为了兼容之前老版本,需使用app:attribute写法。之前把程序的主题指定为淡色主题,所以ToolBar也是淡色,ToolBar上面的各种元素会自动使用深色系(为和主体颜色区分),但很难看。为让ToolBar单独使用深色主题,使用android:theme属性指定为ThemeOverlay.AppCompat.Dark.ActionBar(但ToolBar上如有菜单按钮,弹出的菜单也会成深色,所以使用app:popupTheme(5.0中新增)将弹出的菜单指定成淡色主题)。
修改主程序:
public class MainActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); Toolbar toolbar = (Toolbar)findViewById(R.id.toolbar); setSupportActionBar(toolbar);//让ToolBar的外观功能和ActionBar一样 } }
运行出来的效果与ActionBar一样,但实际是ToolBar,已具备实现Material Design效果的能力。
修改标题栏文字内容:
<application android:allowBackup="true" android:icon="@mipmap/ic_launcher" android:label="@string/app_name" android:supportsRtl="true" android:theme="@style/AppTheme"> <activity android:name=".MainActivity" **android:label="Fruit"**> ...
添加一写action按钮。准备几张图片作为按钮图标,放在drawable-xxhdpi下,再在res目录下创建一个menu文件夹,在此文件夹下新建菜单资源文件toolbar.xml:
<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/delete" android:icon="@drawable/ic_delete" android:title="Delete" app:showAsAction="ifRoom"/> <item android:id="@+id/settings" android:icon="@drawable/ic_settings" android:title="Settings" app:showAsAction="never"/>
Toolbar中的action按钮只显示图标,菜单中的action按钮只显示文字。修改MainActivity:
public class MainActivity extends AppCompatActivity { ... @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: Toast.makeText(this, "You clicked Backup", Toast.LENGTH_SHORT).show(); break; case R.id.delete: Toast.makeText(this, "You clicked Delete", Toast.LENGTH_SHORT).show(); break; case R.id.settings: Toast.makeText(this, "You clicked Settings", Toast.LENGTH_SHORT).show(); break; default: } return true; } }
运行程序:
2、滑动菜单
(1)DrawerLayout
滑动菜单就是将一些菜单选项隐藏起来,不放置在主屏幕上,可通过华东方式将菜单显示出来。可借助DrawerLayout控件实现。
DrawerLayout是一个布局,布局中允许放入两个直接子控件:第一个控件为主屏幕中显示内容,第二个为滑动菜单中显示的内容(此控件需指定layout_gravity属性,告诉DrawerLayout滑动菜单在屏幕左边left还是右边right,还可使用sart(根据系统语言判断,从左往右则为左边)/end)。修改activity_main.xml:
<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 android:id="@+id/toolbar" android:layout_width="match_parent" android:layout_height="?attr/actionBarSize" android:background="?attr/colorPrimary" android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar" app:popupTheme="@style/ThemeOverlay.AppCompat.Light" /> </FrameLayout> <TextView android:layout_width="match_parent" android:layout_height="match_parent" android:layout_gravity="start" android:text="This is menu" android:textSize="30sp" android:background="#FFF"/> </android.support.v4.widget.DrawerLayout>
在Toolbar最左边加入一个导航按钮给用户提示,准备一张按钮图标图片,修改MainActivity:
public class MainActivity extends AppCompatActivity { private DrawerLayout mDrawerLayout; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); Toolbar toolbar = (Toolbar)findViewById(R.id.toolbar); setSupportActionBar(toolbar);//让ToolBar的外观功能和ActionBar一样 mDrawerLayout = (DrawerLayout) findViewById(R.id.drawer_layout); ActionBar actionBar = getSupportActionBar(); if (actionBar != null){ actionBar.setDisplayHomeAsUpEnabled(true);//让导航按钮显示出来 actionBar.setHomeAsUpIndicator(R.drawable.ic_menu);//toolbar最左侧的按钮叫做HomeAsUp按钮,默认图标是一个返回箭头(返回上一个活动),这里做了修改 } } ... @Override public boolean onOptionsItemSelected(MenuItem item) { switch (item.getItemId()){ case android.R.id.home://HomeAsUp的id永远是android.R.id.home mDrawerLayout.openDrawer(GravityCompat.START);//展示华东菜单 break; ... default: } return true; } }
运行程序:
(2)NavigationView
优化滑动菜单页面。NavigationView是Design Support库中提供的控件。须将库引入到项目中:
compile 'com.android.support:design:24.2.1' compile 'de.hdodenhof:circleimageview:2.1.0'
第二个库是一个开源项目CircleImageView,主页地址:https://github.com/hdodenhof/CircleImageView。
使用NavigationView需先准备menu(显示具体的菜单项)和headerLayout(显示头部布局)。准备所需图片,在menu文件夹下新建nav_menu.xml文件:
<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可以随意定制,准备所需图片,新建布局文件nav_header.xml:
<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="jojousername@163.com" android:textColor="#FFF" android:textSize="14sp"/> <TextView android:id="@+id/username" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_above="@id/mail" android:text="Jojo" android:textColor="#FFF" android:textSize="14sp"/> </RelativeLayout>
修改activity_main.xml:
<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 android:id="@+id/toolbar" android:layout_width="match_parent" android:layout_height="?attr/actionBarSize" android:background="?attr/colorPrimary" android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar" app:popupTheme="@style/ThemeOverlay.AppCompat.Light" /> </FrameLayout> <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" app:headerLayout="@layout/nav_header"/> </android.support.v4.widget.DrawerLayout>
替换之前的TextView,还需处理菜单点击事件,修改MainActivity:
protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); Toolbar toolbar = (Toolbar)findViewById(R.id.toolbar); setSupportActionBar(toolbar);//让ToolBar的外观功能和ActionBar一样 mDrawerLayout = (DrawerLayout) findViewById(R.id.drawer_layout); **NavigationView navigationView = (NavigationView) findViewById(R.id.nav_view);** ActionBar actionBar = getSupportActionBar(); if (actionBar != null){ actionBar.setDisplayHomeAsUpEnabled(true);//让导航按钮显示出来 actionBar.setHomeAsUpIndicator(R.drawable.ic_menu);//toolbar最左侧的按钮叫做HomeAsUp按钮,默认图标是一个返回箭头(返回上一个活动),这里做了修改 } **navigationView.setCheckedItem(R.id.nav_call);//将Call菜单项默认选中 navigationView.setNavigationItemSelectedListener(new NavigationView.OnNavigationItemSelectedListener() { @Override public boolean onNavigationItemSelected(@NonNull MenuItem item) { mDrawerLayout.closeDrawers();//将滑动菜单关闭 return true; } });** }
运行程序:
4、悬浮按钮和可交互提示
立面设计代表——悬浮按钮。
(1)FloatingActionButton
Design Support中提供的控件。默认使用colorAccent作为按钮颜色。
准备响应图标,修改activity_main.xml:
<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 android:id="@+id/toolbar" android:layout_width="match_parent" android:layout_height="?attr/actionBarSize" android:background="?attr/colorPrimary" android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar" app:popupTheme="@style/ThemeOverlay.AppCompat.Light" /> **<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"/>** </FrameLayout> ... </android.support.v4.widget.DrawerLayout>
可通过app:elevation属性指定悬浮按钮高度(值越大,投影范围越大,投影效果越淡),默认的足够。接下来处理点击事件,修改MainActivity:
protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); ... FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab); fab.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { Toast.makeText(MainActivity.this, "FAB clicked", Toast.LENGTH_SHORT).show(); } }); }
方法与普通按钮一样。运行程序:
(2)Snackbar
Design Support提供,允许提示中加入一个可交互按钮(与Toast应用场景不同),用法简单,只是额外增加了一个按钮点击事件。修改MainActivity:
... FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab); fab.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { Snackbar.make(v, "Data deleted", Snackbar.LENGTH_SHORT)//第一个参数是View,当前界面布局的任意一个View都行,会根据这个View自动查找最外层布局,用于展示Snackbar .setAction("Undo", new View.OnClickListener() {//设置动作 @Override public void onClick(View v) { Toast.makeText(MainActivity.this, "Data restored", Toast.LENGTH_SHORT).show(); } }).show(); } }); ...
运行程序:
此Snackbar把悬浮按钮遮挡住了,要解决这个问题需要借助CoordinatorLayout.
(3)CoordinatorLayout
加强版的FrameLayout,有Design Support提供。可以监听其所有子控件的各种事件,自动帮助我们做出最为合理的响应。在布局文件中将原来的FrameLayout替换一下即可,运行程序:
由于传入的是FloatingActionButton本身view,而FloatingActionButton是CoordinatorLayout中的子控件,所以Snackbar的弹出隐藏会被CoordinatorLayout监听到。
5、卡片式布局
可让页面中的元素看起来像在卡片中一样,且还能有元角和投影。
(1)CardView
用于实现卡片式布局效果的重要控件,由appcompat-v7库提供。CardView也是一个FrameLayout,额外提供了圆角和阴影等效果,有立体感。
实现水果列表,需用到RecycleView、CardView控件,需先声明:
compile 'com.android.support:recyclerview-v7:24.2.1' compile 'com.android.support:cardview-v7:24.2.1' compile 'com.github.bumptech.glide:glide:4.0.0'
Glide是图片加载库,可用于加载本地图片、网络图片、GIF图片、本地视频等。项目主页:https://github.com/bumptech/glide。修改activity_main.xml:
<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.support.design.widget.CoordinatorLayout android:layout_width="match_parent" android:layout_height="match_parent"> <android.support.v7.widget.Toolbar android:id="@+id/toolbar" android:layout_width="match_parent" android:layout_height="?attr/actionBarSize" android:background="?attr/colorPrimary" android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar" app:popupTheme="@style/ThemeOverlay.AppCompat.Light" /> **<android.support.v7.widget.RecyclerView android:id="@+id/recycler_view" android:layout_width="match_parent" android:layout_height="match_parent"/>** <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"/> </android.support.design.widget.CoordinatorLayout> ... </android.support.v4.widget.DrawerLayout>
定义一个实体类Fruit:
public class Fruit { private String name; private int imageId; public Fruit(String name, int imageId){ this.name = name; this.imageId = imageId; } public String getName() { return name; } public int getImageId() { return imageId; } }
为RecyclerView子项指定一个自定义布局,在layout下新建fruit_item.xml:
<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:orientation="vertical" android:layout_width="match_parent" android:layout_height="wrap_content"> <ImageView android:id="@+id/fruit_image" android:layout_width="match_parent" android:layout_height="100dp" android:scaleType="centerCrop"/> <TextView android:id="@+id/fruit_name" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center_horizontal" android:layout_margin="5dp" android:textSize="16sp"/> </LinearLayout> </android.support.v7.widget.CardView>
centerCrop模式可让图片保持原有比例填充满ImageView,并将超出屏幕的部分裁剪掉。
为RecyclerView准备一个适配器,新建FruitAdapter类,让这个适配器继承自RecyclerView.Adapter,泛型指定为FruitAdapter.ViewHolder:
public class FruitAdapter extends RecyclerView.Adapter<FruitAdapter.ViewHolder> { private Context mContext; private List<Fruit> mFruitList; static class ViewHolder extends RecyclerView.ViewHolder{ CardView cardView; ImageView fruitImage; TextView fruitName; public ViewHolder(View view){ super(view); cardView = (CardView)view; fruitImage = (ImageView) view.findViewById(R.id.fruit_image); fruitName = (TextView) view.findViewById(R.id.fruit_name); } } public FruitAdapter(List<Fruit> fruitList){ mFruitList = fruitList; } @Override public 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 new ViewHolder(view); } @Override public void onBindViewHolder(ViewHolder holder, int position) { Fruit fruit = mFruitList.get(position); holder.fruitName.setText(fruit.getName()); Glide.with(mContext).load(fruit.getImageId()).into(holder.fruitImage);//load()中可传入URL地址、本地路径、资源id //使用Glide不用担心图片像素高无法压缩导致内存泄露等问题 } @Override public int getItemCount() { return mFruitList.size(); } }
最后修改MainActivity:
public class MainActivity extends AppCompatActivity { private DrawerLayout mDrawerLayout; private Fruit[] fruits = {new Fruit("Apple", R.drawable.apple),new Fruit("Banana", R.drawable.banana), new Fruit("Orange", R.drawable.orange),new Fruit("Watermelon", R.drawable.watermelon), new Fruit("Pear", R.drawable.pear),new Fruit("Grape", R.drawable.grape), new Fruit("Pineapple", R.drawable.pineapple),new Fruit("Strawberry", R.drawable.strawberry), new Fruit("Cherry", R.drawable.cherry),new Fruit("Mango", R.drawable.mango)}; private List<Fruit> fruitList = new ArrayList<>(); private FruitAdapter adapter; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); ... initFruits(); RecyclerView recyclerView = (RecyclerView) findViewById(R.id.recycler_view); GridLayoutManager layoutManager = new GridLayoutManager(this, 2); recyclerView.setLayoutManager(layoutManager); adapter = new FruitAdapter(fruitList); recyclerView.setAdapter(adapter); } private void initFruits(){ fruitList.clear(); for (int i = 0; i < 50; i++){ Random random = new Random(); int index = random.nextInt(fruits.length); fruitList.add(fruits[index]);//随机挑选50个水果 } } ...
运行程序:
(2)AppBarLayout
RecyclerView和Toolbar都放置在CoordinatorLayout中,由于CoordinatorLayout是加强版FrameLayout,其中的所有控件在不进行明确定位的情况下,默认摆放在布局的左上角,因此会出现遮挡。
可使用Design Support库提供的AppBarLayout工具。AppBarLayout实际上是一个垂直方向的LinearLayout,内部做了很多滚动事件的封装,并应用了些Material Design的设计理念。解决覆盖问题步骤:将Toolbar嵌套到AppBarLayout中;给RecyclerView指定一个布局行为。修改activity_main.xml:
<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.support.design.widget.CoordinatorLayout android:layout_width="match_parent" android:layout_height="match_parent"> **<android.support.design.widget.AppBarLayout android:layout_width="match_parent" android:layout_height="wrap_content">** <android.support.v7.widget.Toolbar android:id="@+id/toolbar" android:layout_width="match_parent" android:layout_height="?attr/actionBarSize" android:background="?attr/colorPrimary" android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar" app:popupTheme="@style/ThemeOverlay.AppCompat.Light" /> **</android.support.design.widget.AppBarLayout>** <android.support.v7.widget.RecyclerView android:id="@+id/recycler_view" android:layout_width="match_parent" android:layout_height="match_parent" **app:layout_behavior="@string/appbar_scrolling_view_behavior"**/> ... </android.support.design.widget.CoordinatorLayout> ... </android.support.v4.widget.DrawerLayout>
RecyclerView滚动时就已将滚动事件都通知给AppBarLayout,当AppBarLayout接收到滚动事件时,它内部的子控件可以指定如何去影响这些事件,通过app:layout_scrollFlags属性实现。修改activity_main.xml:
<android.support.v7.widget.Toolbar android:id="@+id/toolbar" android:layout_width="match_parent" android:layout_height="?attr/actionBarSize" android:background="?attr/colorPrimary" android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar" app:popupTheme="@style/ThemeOverlay.AppCompat.Light" **app:layout_scrollFlags="scroll|enterAlways|snap"**/>
scroll:RecyclerView向上滚动时,Toolbar会跟着一起向上滚动并实现隐藏;enterAlways: RecyclerView向下滚动时,Toolbar会跟着一起向下滚动并重新显示;snap:当Toolbar还没完全隐藏或显示时,会根据当前滚动的距离,自动选择是隐藏还是显示。
运行程序:
6、下拉刷新
SwipeRefreshLayout是用于实现下拉刷新功能的核心类,由support-v4库提供。将想要实现下拉刷新功能的控件放置其中,就可迅速让此控件支持下拉刷新。修改activity_main.xml:
... **<android.support.v4.widget.SwipeRefreshLayout android:id="@+id/swipe_refresh" android:layout_width="match_parent" android:layout_height="match_parent" app:layout_behavior="@string/appbar_scrolling_view_behavior">** <android.support.v7.widget.RecyclerView android:id="@+id/recycler_view" android:layout_width="match_parent" android:layout_height="match_parent" app:layout_behavior="@string/appbar_scrolling_view_behavior"/> **</android.support.v4.widget.SwipeRefreshLayout>** ...
还需在代码中处理具体的刷新逻辑,修改MainActivity:
public class MainActivity extends AppCompatActivity { ... private SwipeRefreshLayout swipeRefresh; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); ... swipeRefresh = (SwipeRefreshLayout) findViewById(R.id.swipe_refresh); 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(); } ...
运行程序:
7、可折叠式标题栏
Material Design中并没有规定标题栏必须长什么样,可以随意定制标题栏样式。需借助CollapsingToolbarLayout工具。
(1)CollapsingToolbarLayout
作用于Toolbar基础上的布局,由Design Support库提供。但只能作为AppBarLayout的直接子布局使用(AppBarLayout又必须是CoordinatorLayout的子布局)。
继续之前的项目,需要一个额外活动作为水果的详情展示界面,命名为FruitActivity,布局文件为activity_fruit.xml(主要分为两部分:水果标题栏和水果内容详情),标题栏部分使用CoordinatorLayout作为最外层布局:
<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.support.design.widget.AppBarLayout android:id="@+id/appbar" android:layout_width="match_parent" android:layout_height="250dp"><!--250dp的视觉效果较好--> <android.support.design.widget.CollapsingToolbarLayout android:id="@+id/collapsing_toolbar" android:layout_width="match_parent" android:layout_height="match_parent" android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar" app:contentScrim="?attr/colorPrimary" app:layout_scrollFlags="scroll|exitUntilCollapsed"> <!--contentScrim指定CollapsingToolbarLayout在趋于折叠状态及折叠后的背景色(是普通的Toolbar)--> <!--scroll表示CollapsingToolbarLayout会随着水果内容详情滚动一起滚动,exitUntilCollapsed表示滚动完成折叠后保留在界面上不在移出屏幕--> <ImageView android:id="@+id/fruit_image_view" android:layout_width="match_parent" android:layout_height="match_parent" android:scaleType="centerCrop" app:layout_collapseMode="parallax"/><!--parallax表示折叠过程中产生一定的错位偏移--> <android.support.v7.widget.Toolbar android:id="@+id/toolbar" android:layout_width="match_parent" android:layout_height="?attr/actionBarSize" app:layout_collapseMode="pin"/><!--pin表示折叠过程中位置始终不变--> </android.support.design.widget.CollapsingToolbarLayout> </android.support.design.widget.AppBarLayout> <android.support.v4.widget.NestedScrollView android:layout_width="match_parent" android:layout_height="match_parent" app:layout_behavior="@string/appbar_scrolling_view_behavior"> <!--NestedScrollView在ScrollView基础上增加了嵌套响应滚动事件功能, 由于CoordinatorLayout本身已经可以响应滚动事件,因此内部需要使用NestedScrollView或RecyclerView这样的布局。 NestedScrollView和ScrollView内部都只允许存在一个直接子布局(通常先嵌套一个LinearLayout)--> <LinearLayout android:orientation="vertical" android:layout_width="match_parent" android:layout_height="wrap_content"> <!--这里使用TextView显示水果内容详情,并将其放在卡片式布局中--> <android.support.v7.widget.CardView android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginBottom="15dp" android:layout_marginLeft="15dp" android:layout_marginRight="15dp" android:layout_marginTop="35dp" app:cardCornerRadius="4dp"> <TextView android:id="@+id/fruit_content_text" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_margin="10dp"/> </android.support.v7.widget.CardView> </LinearLayout> </android.support.v4.widget.NestedScrollView> <!--加入一个悬浮按钮,获得额外的动画效果--> <android.support.design.widget.FloatingActionButton android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_margin="16dp" android:src="@drawable/ic_comment" app:layout_anchor="@id/appbar" app:layout_anchorGravity="bottom|end"/> <!--使用layout_anchor指定锚点为AppBarLayout,悬浮按钮出现在水果标题栏区域内; layout_anchorGravity定位在标题栏区域右下角--> </android.support.design.widget.CoordinatorLayout>
接下来,修改FruitActivity:
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); Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar); CollapsingToolbarLayout collapsingToolbar = (CollapsingToolbarLayout) findViewById(R.id.collapsing_toolbar); ImageView fruitImageView = (ImageView) findViewById(R.id.fruit_image_view); TextView fruitContentText = (TextView) findViewById(R.id.fruit_content_text); setSupportActionBar(toolbar);//将Toolbar作为ActionBar显示 ActionBar actionBar = getSupportActionBar(); if (actionBar != null){ actionBar.setDisplayHomeAsUpEnabled(true);//启用HomeAsUp按钮 } collapsingToolbar.setTitle(fruitName);//设置当前界面标题 Glide.with(this).load(fruitImageId).into(fruitImageView); String fruitContent = generateFruitContent(fruitName); fruitContentText.setText(fruitContent); } //将水果名循环拼接500次形成长字符串,作为内容详情 private String generateFruitContent(String fruitName){ StringBuilder fruitContent = new StringBuilder(); for (int i = 0; i < 500; i++){ fruitContent.append(fruitName); } return fruitContent.toString(); } @Override public boolean onOptionsItemSelected(MenuItem item) { switch (item.getItemId()){ case android.R.id.home://处理HomeAsUp点击事件 finish(); return true; } return super.onOptionsItemSelected(item); } }
处理RecyclerView点击事件打开FruitActivity.修改FruitAdapter:
public class FruitAdapter extends RecyclerView.Adapter<FruitAdapter.ViewHolder> { ... @Override public 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() {//给CardView注册点击事件监听器 @Override public void onClick(View v) { int position = holder.getAdapterPosition(); Fruit fruit = mFruitList.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; } ... }
运行程序:
往上拖动:
继续拖动:
(2)利用系统状态栏空间
Android5.0系统后可对状态栏的背景或颜色进行操作。这里可以实现系统差异型效果(5.0之前使用普通模式,之后使用背景状态栏融合模式)。
借助android: fitsSystemWindows属性。在CoordinatorLayout、AppBarLayout、CollapsingToolbarLayout这种嵌套结构布局中,将控件的fitsSystemWindows属性设为true就表示该控件会出现在系统状态栏里。此程序中对应的是ImageView控件(必须将其所有父布局都设置这个属性才行)。修改activity_fruit.xml:
<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:id="@+id/appbar" android:layout_width="match_parent" android:layout_height="250dp" **android:fitsSystemWindows="true"**><!--250dp的视觉效果较好--> <android.support.design.widget.CollapsingToolbarLayout android:id="@+id/collapsing_toolbar" android:layout_width="match_parent" android:layout_height="match_parent" android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar" **android:fitsSystemWindows="true"** app:contentScrim="?attr/colorPrimary" app:layout_scrollFlags="scroll|exitUntilCollapsed"> <!--contentScrim指定CollapsingToolbarLayout在趋于折叠状态及折叠后的背景色(是普通的Toolbar)--> <!--scroll表示CollapsingToolbarLayout会随着水果内容详情滚动一起滚动,exitUntilCollapsed表示滚动完成折叠后保留在界面上不在移出屏幕--> <ImageView android:id="@+id/fruit_image_view" android:layout_width="match_parent" android:layout_height="match_parent" android:scaleType="centerCrop" **android:fitsSystemWindows="true"** app:layout_collapseMode="parallax"/><!--parallax表示折叠过程中产生一定的错位偏移--> ... </android.support.design.widget.CollapsingToolbarLayout> </android.support.design.widget.AppBarLayout> ... </android.support.design.widget.CoordinatorLayout>
光设属性还没用,须在程序主题中将状态栏颜色指定为透明色才行(android:statusBarColor属性从5.0系统开始才有)。系统差异化处理,在res下新建一个values-v21目录,在此目录下新建styles.xml文件:
<resources> <style name="FruitActivityTheme" parent="AppTheme"><!--FruitActivityTheme继承了AppTheme中所有特性--> <item name="android:statusBarColor">@android:color/transparent</item><!--将状态栏指定为透明色--> </style> </resources>
对于5.0之前的系统,需对values/styles.xml进行修改:
<resources> <!-- Base application theme. --> <style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar"> <!-- Customize your theme here. --> <item name="colorPrimary">@color/colorPrimary</item> <item name="colorPrimaryDark">@color/colorPrimaryDark</item> <item name="colorAccent">@color/colorAccent</item> </style> **<style name="FruitActivityTheme" parent="AppTheme"></style>** </resources>
定义了内部空的FruitActivityTheme主题,修改AndroidManifest.xml:
... </activity> <activity android:name=".FruitActivity" **android:theme="@style/FruitActivityTheme"**></activity> </application> </manifest>
在Android5.0及以上系统运行程序:
相关文章推荐
- Android学习笔记之Material Design实战
- Android学习笔记:界面设计Material Design的基本使用方法(一)
- Android学习笔记:界面设计Material Design的基本使用方法(二)
- Android学习笔记:界面设计Material Design的基本使用方法(三)
- Android学习笔记(三)--ActionBar Tab 的使用。
- Android学习笔记——页面的布局方式
- [转载] Pro Android学习笔记(十一):了解Intent(中)
- Android学习笔记之(四):广播
- <7.25>新的启航 Android学习笔记(五)
- Android学习笔记(11):线性布局LinearLayout
- Android学习笔记(35)--- 使用sqlite 把.txt文件导进.db
- [Android学习笔记3]ImageSwitcher
- Pro Android学习笔记(一一七):Location(3):获取位置更新
- Android学习笔记七:基本视图组件:RadioGroup和RadioButton
- Pro Android学习笔记(一二一):Telephony API(3):读短信
- Android学习笔记——安卓环境搭建
- Android学习笔记:Android向服务器发送请求时的中文乱码问题
- Android学习笔记(13)————利用SharedPreferences来保存应用程序的数据