1. Toolbar
- Toolbar的强大之处在于:不仅继承了ActionBar的所有功能,而且灵活性很高,可以配合其他控件来完成一些Material Design的效果。
- 新建一个项目,默认显示ActionBar,是因为项目中指定的主题含有ActionBar。
android:theme="@style/AppTheme"
<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
- 准备使用Toolbar替代ActionBar,指定一个不带ActionBar的主题,通常以下两种:
<style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">
Light表示淡色主题,主体颜色淡色,陪衬颜色深色。
<style name="AppTheme" parent="Theme.AppCompat.NoActionBar">
默认是深色主题,主体颜色深色,陪衬颜色淡色。
- 我们需要了解几个名词colorPrimary、colorPrimaryDark、colorAccent、textColorPrimary、windowBackground和navigationBarColor所指定的位置。
2. 编写一个Toolbar
- 布局activity_main.xml:
<?xml version="1.0" encoding="utf-8"?>
<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="wrap_content"
android:layout_height="?attr/actionBarSize"
android:background="?attr/colorPrimary"
android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"
app:popupTheme="@style/ThemeOverlay.AppCompat.Light" />
</FrameLayout>
说明:
1.xmlns:android 表示可以使用android:id等。xmlns:app 表示可以使用app:attribute。
2.整体是淡色主题,Toolbar上的各种要素自动使用深色系,字体颜色是黑色。为了体验更好,借助android:theme单独设置Toolbar的主题是深色主题,这样字体颜色就会是白色。
3.Toolbar单独设置为深色主题,菜单按钮弹出的菜单项也会变成深色主题,太难看,所以我们使用app:popupTheme设置菜单项为淡色主题。
- 修改MainActivity:
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);
}
}
说明:外观和功能和ActionBar一致。
- 修改标题栏上显示的文字内容
android:label="Fruits"
- 添加action按钮
新建res-menu-toolbar.xml:
<?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/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" />
</menu>
说明:
1.showAsAction指定按钮的显示位置。always:永远显示在Toolbar中,如果屏幕空间不够则不显示;ifRoom:屏幕空间足够的情况下显示在Toolbar中,不够的话显示在菜单中;never表示永远显示在菜单当中。
2.Toolbar中的action按钮只会显示图标,菜单中的action按钮只会显示文字。
- 修改MainActivity:
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);
}
@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,"Backup",Toast.LENGTH_SHORT).show();
break;
case R.id.delete:
Toast.makeText(this,"Delete",Toast.LENGTH_SHORT).show();
break;
case R.id.settings:
Toast.makeText(this,"Settings",Toast.LENGTH_SHORT).show();
break;
}
return true;
}
}
说明:之前章节有讲,这里就不再赘述。
3. 显示DrawerLayout
- 修改activity_main.xml:
<?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
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:background="#FFF"
android:text="This is menu"
android:textColor="#FFF00000"
android:textSize="30sp" />
</android.support.v4.widget.DrawerLayout>
说明:
1.最外层是DrawerLayout,包含两个子控件,第一个控件是FrameLayout,第二个控件是TextView,前者显示主屏幕,后者显示滑动菜单内容。
2.第二个控件借助layout_gravity指定滑动出现的方式,left是左边,right是右边,start根据系统语言进行判断。
4. 主屏幕Toolbar左边添加导航按钮,实现点击显示滑动菜单
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
mDrawerlayout = (DrawerLayout) findViewById(R.id.drawer_layout);
ActionBar actionBar = getSupportActionBar();
if (actionBar != null) {
actionBar.setDisplayHomeAsUpEnabled(true);
actionBar.setHomeAsUpIndicator(R.drawable.ic_menu);
}
}
说明:
1. 获取Drawerlayout控件的实例,用于设置滑动菜单的显示方式。
2. 获取ActionBar的实例,setDisplayHomeAsUpEnabled显示导航按钮,按钮显示出来,利用setHomeAsUpIndicator设置按钮图标。
case android.R.id.home:
mDrawerlayout.openDrawer(GravityCompat.START);
break;
说明:设置滑动菜单显示方式。
5. NavigationView
我们可以任意定制我们的滑动菜单的布局,不过谷歌提供了一个一种更好的方法——使用NavigationView,让滑动菜单页面的实现变得非常简单。
- 添加依赖:
compile 'com.android.support:design:25.3.1'
compile 'de.hdodenhof:circleimageview:2.1.0'
- 创建menu菜单nav_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>
<item
android:id="@+id/nav_friends"
android:icon="@drawable/nav_friends"
android:title="Friends"></item>
<item
android:id="@+id/nav_location"
android:icon="@drawable/nav_location"
android:title="Location"></item>
<item
android:id="@+id/nav_mail"
android:icon="@drawable/nav_mail"
android:title="Mail"></item>
<item
android:id="@+id/nav_task"
android:icon="@drawable/nav_task"
android:title="Tasks"></item>
</group>
</menu>
说明:checkableBehavior表示item只可以单选。
- 定制headerLayout,它的布局命名为nav_header:
<?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:background="?attr/colorPrimary"
android:padding="10dp">
<de.hdodenhof.circleimageview.CircleImageView
android:id="@+id/icon_image"
android:layout_width="70dp"
android:layout_height="70dp"
android:layout_centerInParent="true"
android:src="@drawable/nav_icon" />
<TextView
android:id="@+id/username"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:text="fkq339@gmail.com"
android:textColor="#FFF"
android:textSize="14sp" />
<TextView
android:id="@+id/mail"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_above="@id/username"
android:text="Tony Green"
android:textColor="#FFF"
android:textSize="14sp" />
</RelativeLayout>
- 修改activity_main:
<?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
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:headerLayout="@layout/nav_header"
app:menu="@menu/nav_menu">
</android.support.design.widget.NavigationView>
</android.support.v4.widget.DrawerLayout>
说明:之前的滑动菜单为TextView,现在用NavigationView代替TextView。利用:app:headerLayout和app:menu设置我们刚才准备好的menu和headerLayout.
- 处理点击事件
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
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);
}
navigationview.setCheckedItem(R.id.nav_call);
navigationview.setNavigationItemSelectedListener(new NavigationView.OnNavigationItemSelectedListener() {
@Override
public boolean onNavigationItemSelected(@NonNull MenuItem item) {
mDrawerlayout.closeDrawer(Gravity.START);
return true;
}
});
}
说明:获取NavigationView的实例,并且设置Call菜单项为默认选中,点击后回调onNavigationItemSelected方法,这个时候我们关闭滑动菜单。
6. FloatingActionButton
- 布局:
<android.support.design.widget.FloatingActionButton
app:elevation="8dp"
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" />
说明:
1.elevation指定的是悬浮的高度。
2.layout_gravity让控件位于底部,end根据系统语言决定左边或者右边。
3.layout_margin表示控件周围有空隙,更加美观。
4.src 可以用一张图片展示在悬浮按钮上。
- 点击事件:
FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab);
fab.setOnClickListener(this);
说明:并没有特殊之处,它和普通的button一样。
7. Snackbar
- Snackbar不是Toast的替代品。
- Toast作用是告诉用户现在发生了什么事情,用户被动接收;而Snackbar则允许在提示的过程中加入一个可交互的按钮,当用户点击按钮的时候可以执行一些额外的逻辑操作。
- 举例子:点击按钮,执行删除的操作,Snackbar的交互就可以在点击后起到反悔的功能。
@Override
public void onClick(View v) {
Snackbar.make(v,"Data deleted",Snackbar.LENGTH_SHORT).setAction("Undo", new View.OnClickListener() {
@Override
public void onClick(View v) {
Toast.makeText(MainActivity.this,"Data restored",Toast.LENGTH_SHORT).show();
}
}).show();
}
说明:
1. Snackbar.make方法接收三个参数:第一个参数接收一个View,只要是当前布局的任意一个View就可以,Snackbar会使用这个View来自动查找最外层的布局,来展示Snackbar;第二个参数是Snackbar显示的内容;第三个参数是显示的时间。
2. 接着调用setAction来执行反悔的操作。
8. CoordinatorLayout
- CoordinatorLayout是一个加强版的FrameLayout,可以监听所有子控件的各种事件,然后做出最为合理的响应。
- Snackbar提示将悬浮按钮遮挡住了,而如果能让CoordinatorLayout监听到Snackbar的弹出事件,那么它自动会将内部的FloatingActionButton向上偏移,从而确保不会被Snackbar遮挡到。
- 使用非常简单:替换下FrameLayout即可。
<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.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" />
</android.support.design.widget.CoordinatorLayout>
说明:虽然Snackbar不在CoordinatorLayout里面,但是Snackbar的make方法接收的第一个参数View是FloatingActionButton,也就是说Snackbar基于FloatingActionButton触发的。FloatingActionButton是CoordinatorLayout的子控件,自然可以监听到Snackbar。
9. CardView
CardView也是一个FrameLayout,只是额外提供了圆角和阴影等效果。以下是结合RecyclerView的示例。其中的知识点我们已经在第3章详细讲解,这里不在赘述。
- 添加依赖:
compile 'com.android.support:recyclerview-v7:25.3.1'
compile 'com.android.support:cardview-v7:25.3.1'
compile 'com.github.bumptech.glide:glide:3.7.0'
- 整体布局:
<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.v7.widget.RecyclerView>
<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" />
</android.support.design.widget.CoordinatorLayout>
- 子项布局:
<?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"
android:orientation="vertical"
app:cardCornerRadius="4dp">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<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>
说明:
1.CardView没有好的定位方式,所以里面包裹一个LinearLayout。
2.app:cardCornerRadius设置的是圆角的大小。
3.scaleType指的是图片的缩放模式。
- 实体类
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 void setName(String name) {
this.name = name;
}
public int getImageId() {
return imageId;
}
public void setImageId(int imageId) {
this.imageId = imageId;
}
}
- FruitAdapter类
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> mFruitList) {
this.mFruitList = mFruitList;
}
@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 new ViewHolder(view);
}
@Override
public void onBindViewHolder(FruitAdapter.ViewHolder holder, int position) {
Fruit fruit = mFruitList.get(position);
holder.fruitName.setText(fruit.getName());
Glide.with(mContext).load(fruit.getImageId()).into(holder.fruitimage);
}
@Override
public int getItemCount() {
return mFruitList.size();
}
}
- MainActivity:
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;
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]);
}
}
10. AppBarLayout
- CardView示例中RecyclerView遮挡住了Toolbar,因为它俩都在CoordinatorLayout中,而CoordinatorLayout是一个FrameLayout,所以造成了这个bug.
- 解决方法:借助AppBarLayout:第一步将Toolbar嵌套到AppBarLayout中,第二步给RecyclerView指定一个布局行为。
- 修改activity_main:
<android.support.design.widget.AppBarLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<android.support.v7.widget.Toolbar
app:layout_scrollFlags="scroll|enterAlways|snap"
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.v7.widget.RecyclerView>
说明:
1.RecyclerView借助app:layout_behavior属性指定一个布局行为,目的是建立Toolbar和RecyclerView之间的关联。
2.借助app:layout_behavior,Toolbar在接收到RecyclerView滚动事件的时候,会进行相应的操作。
3.scroll表示Recy向上滚动的时候,Toolbar会跟着一起向上滚动并实现隐藏;enterAlwys表示Recy向下滚动的时候,Toolbar会跟着一起向下滚动并重新显示。snap表示当Toolbar还没有完全隐藏或显示的时候,会根据当前滚动的距离,自动选择是隐藏还是显示。
11. SwipeRefreshLayout
- 把想要实现下拉刷新功能的控件放置到SwipeRefreshLayout中,就可以迅速让这个控件支持下拉刷新。app:layout_behavior要放在SwipeRefreshLayout中。
<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"></android.support.v7.widget.RecyclerView>
</android.support.v4.widget.SwipeRefreshLayout>
- 修改MainActiivty,处理刷新逻辑
swiperefreshlayout = (SwipeRefreshLayout) findViewById(R.id.swipe_refresh);
swiperefreshlayout.setColorSchemeColors(getResources().getColor(R.color.colorPrimary));
swiperefreshlayout.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();
swiperefreshlayout.setRefreshing(false);
}
});
}
}).start();
}
说明:
1. setColorSchemeColors 处理下拉刷新的颜色。
2. onRefresh处理具体的刷新逻辑。
3. adapter.notifyDataSetChanged()通知页面刷新。
4. setRefreshing隐藏下拉刷新。
12. CollapsingToolbarLayout
- 借助CollapsingToolbarLayout可根据自己的喜好随意定制出标题栏的样式,让Toolbar更加丰富,不仅仅是展示一个标题栏,而是能够实现非常华丽的效果。
- CollapsingToolbarLayout不能独立存在,它在设计的时候就被限定只能作为AppBarLayout的直接子布局来使用。而AppBarLayout又必须是CoordinatorLayout的子布局。
- activity_fruit.xml 标题栏部分:
<?xml version="1.0" encoding="utf-8"?>
<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:orientation="vertical">
<android.support.design.widget.AppBarLayout
android:id="@+id/appBar"
android:layout_width="match_parent"
android:layout_height="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">
<ImageView
android:id="@+id/fruit_image_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scaleType="centerCrop"
app:layout_collapseMode="parallax" />
<android.support.v7.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
app:layout_collapseMode="pin"></android.support.v7.widget.Toolbar>
</android.support.design.widget.CollapsingToolbarLayout>
</android.support.design.widget.AppBarLayout>
</android.support.design.widget.CoordinatorLayout>
说明:
1. ThemeOverlay.AppCompat.Dark.ActionBar是一个深色的主题,保证字体颜色是浅色系。
2. contentScrim用于指定趋于折叠状态以及折叠以后的背景色。CollapsingToolbarLayout折叠之后就是一个普通的Toolbar,背景色是colorPrimary.
3. layout_scrollFlags:scroll表示CollapsingToolbarLayout会随着水果内容详情的滚动一起滚动,exitUntilCollapsed表示当CollapsingToolbarLayout随着滚动完成折叠之后就保留在界面上,不再移出屏幕。
4. 高级版的标题栏是由普通的标题栏加上图片组合而成的。
5. layout_collapseMode 用于指定CollapsingToolbarLayout折叠过程中的折叠模式,其中Toolbar指定成pin,表示在折叠的过程中位置始终不变,ImageView指定成parallax,表示在折叠过程中产生一定的错误偏移。
- activity_main 水果内容详情部分:
<android.support.v4.widget.NestedScrollView
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="@string/appbar_scrolling_view_behavior">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<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>
说明:
1. NestedScrollView除了具有ScrollView的功能之外,还具有嵌套响应滚动事件的功能,并且指定了布局行为,向CoodinatorLayout传递滚动事件。
2. NestedScrollView只能有一个子布局,我们添加一个LinearLayout,里面是一个卡片式布局,布局里面是一个TextView,显示水果的内容详情。
- 添加FloatingActionButton:
<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" />
说明:
1. FloatingActionButton和AppBarLayout以及NestedScrollView是平级的。
2. layout_anchor指定一个锚点,将锚点设置为AppBarLayout,这样悬浮按钮就会出现在水果标题栏的区域内。
3. layout_anchorGravity将悬浮按钮定位在标题栏区域的右下角。
- FruitActivity:
public class FruitActivity extends AppCompatActivity {
private static final String FRUIT_NAME = "fruit_name";
private static final String FRUIT_IMAGE_ID = "fruit_image_id";
@Override
protected void onCreate(@Nullable 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);
ActionBar actionbar = getSupportActionBar();
if (actionbar != null) {
actionbar.setDisplayHomeAsUpEnabled(true);
}
collapsingToolbar.setTitle(fruitName);
Glide.with(this).load(fruitImageId).into(fruitImageView);
String fruitContext = generateFruitContext(fruitName);
}
private String generateFruitContext(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:
finish();
return true;
}
return super.onOptionsItemSelected(item);
}
}
说明:
1. 通过intent获取传入的水果名称和水果图片的资源id,然后通过fv拿到布局文件中各个控件的实例。显示Toolbar,启用HomeAsUp按钮。
2. collapsingToolbar的setTilte设置当前页面的标题;使用Glide加载传入的水果图片。 onOptionsItemSelected方法中处理了HOMEASUP按钮的点击事件。
- 修改FruitAdapter,设置点击事件:
@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);
final ViewHolder holder = new ViewHolder(view);
holder.cardview.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
int position = holder.getAdapterPosition();
Log.e("FruitAdapter","position:"+position);
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;
}
说明:通过Intent传递数据启动FruitActivity。
13. 充分利用系统状态栏空间
- 借助android:fitsSystemWindows="true"这个属性实现背景图和状态栏的有效融合:
<?xml version="1.0" encoding="utf-8"?>
<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:fitsSystemWindows="true"
android:layout_height="match_parent"
android:orientation="vertical">
<android.support.design.widget.AppBarLayout
android:fitsSystemWindows="true"
android:id="@+id/appBar"
android:layout_width="match_parent"
android:layout_height="250dp">
<android.support.design.widget.CollapsingToolbarLayout
android:fitsSystemWindows="true"
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">
<ImageView
android:fitsSystemWindows="true"
android:id="@+id/fruit_image_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scaleType="centerCrop"
app:layout_collapseMode="parallax" />
说明:
1. ImageView以及父类标签都要指定属性。
- 指定状态栏颜色为透明色并且区分版本:
- 设置状态栏颜色为透明色是从Android 5.0系统开始才有的,之前的系统无法指定这个属性,所以需要区别对待。
- 创建values-v21目录,新建style.xml的resource file:
<resources>
<style name="FruitActivityTheme" parent="AppTheme">
<item name="android:statusBarColor">@android:color/transparent</item>
</style>
</resources>
说明:
1.values-v21只有5.0才可以读取。
2.FruitActivityTheme专门给FruitActivity使用,parent主题是AppTheme,也就是说它继承了AppTheme的所有特性。
3.指定FruitActivityTheme为透明色。
3.values-styles.xml指定FruitActivity的5.0之前的主题设置:
<style name="FruitActivityTheme" parent="AppTheme"></style>
说明:5.0之前无法指定状态栏的颜色,这里什么都不做即可。
4.配置FruitActivity主题
<activity android:name=".FruitActivity" android:theme="@style/FruitActivityTheme"></activity>