第一行代码第二版(郭霖著)笔记之第十二章(Material Design 实战)

来源:互联网 发布:java 数据库管理工具 编辑:程序博客网 时间:2024/05/16 03:39

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以及父类标签都要指定属性。
  • 指定状态栏颜色为透明色并且区分版本:

    1. 设置状态栏颜色为透明色是从Android 5.0系统开始才有的,之前的系统无法指定这个属性,所以需要区别对待。
    2. 创建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>
阅读全文
1 0