安卓开发之Material Design实战

Material Design是由谷歌的设计工程师们基于传统优秀的设计原则发明的一套全新的界面设计语言,包含了视觉、运动、互动效果等特性。这次来学习一下Material Design的相关知识,参考书籍为《第一行代码》。

Toolbar

ActionBar由于涉及的原因,被限定只能位于Activity的顶部,从而不能实现Material Design的一些效果。因此Android 5.0中新引入了一个控件Toolbar,具有很高的灵活性,其出现的目的就是为了取代ActionBar。ActionBar图如下图所示:

要想使用Toolbar代替ActionBar,我们首先得知道ActionBar是怎样指定然后显示的,打开AndroidManifest.xml文件:
在这里插入图片描述
可以看到有个Theme属性,指定了项目的主题为AppTheme,并且在style文件中,打开values/style.xml:
在这里插入图片描述
果然有个AppTheme的主题,并且它的parent主题为Theme.AppCompat.Light.DarkActionBar,这个就是我们之前项目中常见的深色ActionBar主题,如下图所示:
在这里插入图片描述

因此,想要使用ToolBar,我们首先得替换这儿的Theme.AppCompat.Light.DarkActionBar为为Theme.AppCompat.Light.NoActionBar,这样就隐藏掉ActionBar了,效果如下:
在这里插入图片描述
修改成NoActionBar后,我们看下AppTheme的属性,指定了不同属性的颜色,每个属性又代表一个位置:

  • < item name=“colorPrimary”>@color/colorPrimary < /item>
  • < item name=“colorPrimaryDark”>@color/colorPrimaryDark< /item>
  • < item name=“colorAccent”>@color/colorAccent< /item>

先看下color.xml中定义的颜色,我们可以自己指定颜色:
在这里插入图片描述
不同属性对应的位置如下图所示:
在这里插入图片描述

隐藏掉ActionBar之后,我们就得添加ToolBar了,修改activity_main.xml代码,添加Toolbar:
在这里插入图片描述
高度设置为和ActionBar一样的高度,在MainActivity中传入ToolBar的实例:
在这里插入图片描述
运行程序:
在这里插入图片描述
这里看到文字元素变成了黑色,因为我们前面指定了浅色的主题,为了和主体颜色分开,Toolbar上面的各元素都会使用深色系,所以这里我们让ToolBar单独使用深色主题,并且单独将弹出的菜单指定为浅色主体:
在这里插入图片描述
效果如下:
在这里插入图片描述
和ActionBar效果很相似,但是这里已经是ToolBar了。然后就可以在Manifest文件activity中通过label属性,显示制定ToolBar文字内容:
在这里插入图片描述
效果如下:
在这里插入图片描述
接下来为了让ToolBar不显得太单调,我们添加几个菜单项,新建menu文件夹并新建编辑menu.xml文件,和我们在学习Activity时添加菜单项的操作一样:
在这里插入图片描述
icon指定菜单按钮的图片,app:showAsAction用于指定菜单按钮的显示位置,主要有下面几种值:

  • always:永远显示在ToolBar中,屏幕空间不够的话不显示,ToolBar上的菜单按钮只会显示图标
  • ifRoom:屏幕空间够的话显示在ToolBar中,不够的话显示在菜单中,菜单的按钮只会显示文字
  • never:永远显示在菜单中

有了菜单文件后,就重写onCreateOptionsMenu()方法和onOptionsItemSelected()方法,前者加载菜单文件,后者添加监听事件:
在这里插入图片描述
效果如下:点击图标会Toast相应的内容
在这里插入图片描述

DrawerLayout

在替换掉ActionBar后,继续完成滑动菜单以及主屏幕内容的填充,滑动菜单就是常见的左滑出现一些菜单项。如下图所示:
在这里插入图片描述
要实现这样的效果,利用Google提供的DrawerLayout和NavigationView实现。DrawerLayout实现可以滑动显示菜单,该布局中放入两个直接的子控件,分别显示主屏幕内容以及滑动菜单的显示内容;NavigationView就放在DrawerLayout里面用于实现显示具体的菜单内容。DrawerLayout此时是作为最外层的控件。
在这里插入图片描述

滑动菜单的显示内容

这里先不考虑主屏幕内容,它肯定是由ToolBar和一些内容组成的,那么先写出NavigationView的内容,NavigationView又是由两部分组成,上面的头像、个人信息头布局以及下面的菜单选项。

首先由于NavigationView是由Material库提供的,因此需引入依赖库,并且为了实现头像的圆形化效果,这里还引入了circleimageview:
在这里插入图片描述
头布局包括圆形图片、个人信息等,在layout下新建头布局文件nav_header.xml,包括circleimageview以及TextView:

<?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/circleimg"
        android:layout_width="70dp"
        android:layout_height="70dp"
        android:src="@drawable/nav_icon"
        android:layout_centerInParent="true"/>
    <TextView
        android:id="@+id/name"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentBottom="true"
        android:textSize="14sp"
        android:textColor="#FFF"
        android:text="Andy" />
    <TextView
        android:id="@+id/mail"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_above="@+id/name"
        android:textSize="14sp"
        android:textColor="#FFF"
        android:text="[email protected]" />
</RelativeLayout>

下面的菜单选项和前面ToolBar菜单一样,在menu下新建菜单文件nav.xml:
在这里插入图片描述
这样NavigationView的两部分布局都定义好了,引入到DrawerLayout中,通过app:menu以及app:headerLayout绑定菜单以及头部布局:
在这里插入图片描述
这里android:layout_gravity="start"代表的是根据系统语言判断是左滑显示菜单还是右滑显示菜单,例如中文是左滑,阿拉伯文是右滑,当然也可以显示指定right或者left。设置完成后还要对菜单项设置点击事件,修改MainActivity代码:
在这里插入图片描述
代码中调用setCheckedItem设置默认选择项,调用setNavigationItemSelectedListener设置子项点击事件,这里设置点击后关闭滑动菜单,关闭操作通过drawerLayout实例的closeDrawers方法:
在这里插入图片描述

扫描二维码关注公众号,回复: 11506511 查看本文章

这样滑动菜单布局就完成了,上面点击菜单子项是关闭滑动菜单,接下来还得完成滑动菜单的打开,左滑是一种方式,但是如果没有提示的话会显得不友好,如下图所示点击ToolBar左边图标也可以显示滑动菜单:
在这里插入图片描述
在MainActivity中添加如下代码:
在这里插入图片描述
首先调用getSupportActionBar得到ActionBar实例,虽然这个ActionBar的实例的具体实现是由ToolBar完成的,接着在ActionBar不为空的情况下,调用setDisplayHomeAsUpEnabled(true)让导航按钮显示出来,调用setHomeAsUpIndicator来设置一个导航按钮图片。实际上ToolBar最左侧的按钮就叫做Home按钮,id是android.R.id.home,默认是左箭头作用是返回上一个Activity,这里将作用和图标都进行了修改。设置完成之后,设置点击事件,在onOptionsItemSelected方法中添加Home按钮的点击事件,调用drawerLayout实例的openDrawer方法,传入一个Gravity参数,注意这里要和前面定义的一致(start是根据系统语言判断的):
在这里插入图片描述
至此DrawerLayout里面的滑动菜单就通过NavigationView完成了。接下来看主屏幕内容。

主屏幕内容

首先添加一个这个项目中没有使用的悬浮按钮效果,可以直接跳过这一节。

悬浮按钮FloatingActionButton与SnackBar

效果如下,点击悬浮按钮(FloatingActionButton),按钮上移并提示数据删除,点击恢复撤销删除(通过SnackBar实现,比Toast高级)。这里没有具体实现功能,只是Toast了一下。
在这里插入图片描述
这里使用到了Material库提供的比Toast更加先进的提示工具SnackBar。因为Toast只会提示而无法交互,SnackBar提供了上图中可以添加恢复的选型。
为了实现点击后按钮上移,这里使用的布局为CoordinatorLayout,将ToolBar与FloatingActionButton放在CoordinatorLayout里面然后将CoordinatorLayout放到DrawerLayout中,与NavigationView同级,修改activity_main.xml文件:
在这里插入图片描述
使用CoordinatorLayout的原因是CoordinatorLayout是一种加强版的FrameLayout,它可以监听所有子控件的各种事件,并自动做出合理响应。这里如果不使用CoordinatorLayout而是使用FrameLayout,那么点击按钮后SnackBar的提示就会遮住按钮,而CoordinatorLayout可以监听到SnackBar的提示内容,并自动把SnackBar上移。android:layout_gravity="bottom|end"代表这个悬浮按钮位于屏幕右下角,然后在MainActivity中设置按钮的点击事件:
在这里插入图片描述
由上图看出,SnackBar使用make方法创建对象,传入参数依次为(当前界面布局任意一个View对象,内容,时长)与Toast相似,这里传入的第一个参数就是FloatingActionButton本身,而FloatingActionButton是CoordinatorLayout的子控件,因此SnackBar的事件也能被监听到了。接着通过调用setAction设置动作,并且为这个动作添加了一个点击监听事件,最后通过show()方法显示出来。

屏幕主体内容的添加

上面的悬浮按钮FloatingActionButton与SnackBar只是添加了一种效果,其实并没有完全实现其真正的功能,主体内容部分才是我们的重点关注对象。这里我们使用前面学过的RecyclerView滚动控件放置主体内容数据,修改activity_main.xml文件,添加recyclerview(使用前要添加依赖),它和ToolBar、FloatingActionButton同级:
在这里插入图片描述
然后就是和之前学习recyclerview一样新建Fruit类,新建子项布局文件,新建自定义适配器:
Fruit类:在这里插入图片描述

子项布局文件fruit_item.xml,这里使用了Material库提供的MaterialCardView布局,比起FrameLayout来说提供了圆角和阴影的特殊效果:
在这里插入图片描述
其中 app:cardCornerRadius=“5dp”,指定了卡片圆角的弧度,数值越大,圆角的弧度也越大。此外还可以通过app:elevation属性置顶卡片的高度,高度值越大,投影范围越大,但是投影效果越淡,这里就不指定卡片高度了。ImageView中的scaleType属性指定了图片的缩放模式,使用centerCrop模式可以让图片保持原有比例填充满ImageView,并将超出屏幕的部分裁剪掉。

接下为RecyclerView准备适配器,作为MVC中的C,新建FruitAdapter,和之前学的一样:

public class FruitAdapter extends RecyclerView.Adapter<FruitAdapter.ViewHolder> {
    private Context context;
    private List<Fruit> fruits;
    @NonNull
    @Override
    public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        View view = LayoutInflater.from(context).inflate(R.layout.fruit_item,parent,false);
        final ViewHolder viewHolder = new ViewHolder(view);
        return viewHolder;
    }

    public FruitAdapter(Context context, List<Fruit> fruits) {
        this.context = context;
        this.fruits = fruits;
    }

    @Override
    public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
        Glide.with(context).load(fruits.get(position).getIcon()).into(holder.fruit_icon);
        holder.fruit_name.setText(fruits.get(position).getName());
    }

    @Override
    public int getItemCount() {
        return fruits.size();
    }

    static class ViewHolder extends RecyclerView.ViewHolder{
         private TextView fruit_name;
         private ImageView fruit_icon;
         public ViewHolder(@NonNull View itemView) {
             super(itemView);
             fruit_icon = itemView.findViewById(R.id.fruit_icon);
             fruit_name = itemView.findViewById(R.id.fruit_name);
         }
     }
}

不同的地方在于onBindViewHolder方法中加载图片使用了第三方库Glide,因为图片像素很高的话如果不进行压缩很容易造成之前学习Bitmap时说的OOM内存溢出问题,Glide可以自动帮我们避免这些问题。用法也很简单,使用with方法并传入一个Context或Activity或Fragment参数,然后通过load方法加载图片,可以是网络加载参数就是URL地址,也可以加载本地路径图片,或是资源id,最后通过into()方法将图片设置到具体某一个ImageView中。

适配器准备好后,修改MainActivity代码,为Recyclerview指定布局方式,并设置适配器:和之前学的一样
在这里插入图片描述
在这里插入图片描述

编写完成后运行会发现虽然图片显示了出来,但是上滑的时候图片会遮挡住住ToolBar,这是因为CoordinatorLayout是一种加强版的FrameLayout,ToolBar以及RecyclerView都放在这个加强版的“FrameLayout”中,正是因为CoordinatorLayout遵循FrameLayout布局方式,那么其内的多有控件在不明确定位的情况下,默认都会放在布局的左上角,后面的控件会遮住前面的控件,如下图所示:
在这里插入图片描述
在这里插入图片描述
因此这里为了解决这个问题,又使用了Material库提供的AppBarLayout,AppBarLayout实际上是一个vertical方向的LinearLayout,它在内部做了很多滚动事件的封装,它必须作为CoordinatorLayout的子布局使用。修改activity_main.xml文件如下:将TooblBar嵌套到AppBarLayout里面,并为RecyclerView添加app:layout_behavior属性:
在这里插入图片描述
@string/appbar_scrolling_view_behavior的值为android.support.design.widget.AppBarLayout$ScrollingViewBehavior ,指向AppBarLayout.ScrollingViewBehavior,用来通知AppBarLayout 这个特殊的view何时发生了滚动事件,这个behavior需要设置在触发滚动事件的view之上这里就是RecyclerView,这样上滑时就不会被遮挡住了。但是这样滚动事件触发时只是没有遮挡ToolBar,并没有什么特殊的效果,因此还需进一步优化。
优化使用到了app:layout_scrollFlags属性,AppBarLayout里面定义的view只要设置了app:layout_scrollFlags属性,就可以在RecyclerView滚动事件发生的时候被触发。那么我们就在ToolBar上设置这个属性,这样滚动事件发生的时候就会被触发。app:layout_scrollFlags属性里面必须启用scroll这个flag,这样这个view才会滚动出屏幕,否则它将一直固定在顶部。
在这里插入图片描述
使用到的flag的含义是:

  • scroll:RecyclerView向上滚动的时候,ToolBar也会跟着向上滚动并实现隐藏
  • enterAlways: RecyclerView向下滚动的时候,ToolBar也会跟着向下滚动并显示
  • snap:表示ToolBar还没有完全隐藏或显示的时候,会根据当前滚动的距离自动选择隐藏还是显示
  • exitUntilCollapsed:表示随着滚动完成折叠之后就保留在界面上,不移出屏幕

编辑完成后编译运行,效果如下图所示:
在这里插入图片描述
至此已经完成了主屏幕内容的大部分工作,接下来在以上项目的基础上需要实现一些其他的效果。

下拉刷新

AndroidX库提供了SwipeRefreshLayout核心类,用法也比较简单,将RecyclerView嵌套在SwipeRefreshLayout中就可以,并且由于RecyclerView变成了SwipeRefreshLayout的子控件,我们要将之前RecyclerView中的app:layout_behavior属性移到SwipeRefreshLayout中:SwipeRefreshLayout与AppBarLayout、FloatingActionButton同级
在这里插入图片描述
然后在MainActivity中添加具体的刷新逻辑:
在这里插入图片描述
setColorSchemeResources指定了刷新进度条的颜色,可以自定义;然后为swipeRefreshLayout指定了监听事件,在监听事件中,首先开启一个线程,然后将线程沉睡2000ms,否则将看不到刷新过程就直接刷新了;沉睡结束后,调用runOnUiThread将线程切回到主线程更新UI:更新数据然后调用适配器的notifyDataSetChanged通知数据已经更新了,最后调用 swipeRefreshLayout的setRefreshing(false)方法表示刷新事件结束,并隐藏进度条。这里为了效果演示,将前面initFruits方法注释掉,也就是主屏幕没有数据:
在这里插入图片描述
由于注释掉后主屏幕就没有内容了,刷新时进行数据初始化,那么刷新后就有数据了,效果如下图所示:
在这里插入图片描述

完整的activity_main.xml文件:

<?xml version="1.0" encoding="utf-8"?>
<androidx.drawerlayout.widget.DrawerLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:id="@+id/drawerlayout"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
    <androidx.coordinatorlayout.widget.CoordinatorLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent">
        <com.google.android.material.appbar.AppBarLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content">
            <androidx.appcompat.widget.Toolbar
                android:id="@+id/toolbar"
                android:layout_width="match_parent"
                android:layout_height="?android:attr/actionBarSize"
                android:background="?android:attr/colorPrimary"
                android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"
                app:popupTheme="@style/ThemeOverlay.AppCompat.Light"
                app:layout_scrollFlags="scroll|enterAlways|snap"/>
        </com.google.android.material.appbar.AppBarLayout>
        <androidx.swiperefreshlayout.widget.SwipeRefreshLayout
            android:id="@+id/pullToRefresh"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            app:layout_behavior="@string/appbar_scrolling_view_behavior">
            <androidx.recyclerview.widget.RecyclerView
                android:id="@+id/recyclerview"
                android:layout_width="match_parent"
                android:layout_height="match_parent" />
        </androidx.swiperefreshlayout.widget.SwipeRefreshLayout>
        <com.google.android.material.floatingactionbutton.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"/>
    </androidx.coordinatorlayout.widget.CoordinatorLayout>
    <com.google.android.material.navigation.NavigationView
        android:id="@+id/nav_view"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_gravity="start"
        app:menu="@menu/nav"
        app:headerLayout="@layout/nav_header"/>

</androidx.drawerlayout.widget.DrawerLayout >
数据详细内容的添加

虽然完成了主屏幕内容的添加,但是我们点击任意一个子项是没有任何响应的,这里添加各个子项的点击事件以启动新的详细介绍页面。既然是新的页面那也就是新的Activity,我们需要新建Activity以及新建其对应的布局文件。最终实现效果如下:

在这里插入图片描述
新建布局文件activity_fruit.xml:
在这里插入图片描述

可以看到,这里没有像activity_main.xml中那样将Toolbar直接嵌套到AppBarLayout里面,而是先嵌套了Material库提供的CollapsingToolbarLayout布局再嵌套了ToolBar,它可以让ToolBar不仅仅是展示一个标题栏,能实现更加丰富的效果。CollapsingToolbarLayout在设定的时候就被限定必须作为AppBarLayout的直接子布局使用,而AppBarLayout又必须是CoordinatorLayout的子布局,因此这里我们使用了CoordinatorLayout作为最外层的布局。

CollapsingToolbarLayout中 android:theme以及app:layout_scrollFlags属性是将之前ToolBar的属性提到了上一层(即CollapsingToolbarLayout),以实现更加高级的ToolBar效果。contentScrim属性代表在区域折叠以及折叠之后的背景色。

接着就要在CollapsingToolbarLayout中定义标题栏的具体内容了,由效果图也可以看到,这里的标题栏更加高级,它是由ImageView图片和ToolBar普通标题栏组合而成的,那么我们就在CollapsingToolbarLayout里面嵌套ImageView和ToolBar。可以看到里面有个共同属性
app:layout_collapseMode,它用于指定当前控件在CollapsingToolbarLayout折叠过程中的折叠模式,有以下值可选:

  • pin:表示在折叠的过程中位置始终保持不变
  • parallax:表示在折叠的过程中产生一定的错位偏移
    效果如下图所示,由ImageView图片和ToolBar普通标题栏组合而成:
    在这里插入图片描述
    上滑:
    在这里插入图片描述

接下来添加文本以及悬浮按钮:
在这里插入图片描述
继续往activity_main.xml文件的CoordinatorLayout布局中添加:
在这里插入图片描述

首先将TextView嵌套到MaterialCardView中,前面已经提到过MaterialCardView,它提供了圆角和阴影的特殊效果,然后将MaterialCardView嵌套到一个vertical方向的LinearLayout中,最后把这个LinearLayout嵌套到NestedScrollView中。NestedScrollView是一种加强的ScrollView,它允许使用滚动的方式查看屏幕外的数据,还增加了嵌套相应滚动事件的功能。这里还要嵌套一个LinearLayout的原因是ScrollView还是NestedScrollView它们内部都只允许存在一个直接子布局,所以如果我们想要在其中放入很多东西的话,通常都需要嵌套一个LinearLayout。

添加NestedScrollView后,还需要添加悬浮按钮,这个直接套用前面的悬浮按钮布局,不同的是app:layout_anchor以及app:layout_anchorGravity两个属性,前者指定锚点,将appbar设置为锚点,这个悬浮按钮就会出现在标题栏的区域内,为了达到上图的效果,还需要指定app:layout_anchorGravity属性为右下角。

布局文件完成后,新建FruitActivity.java实现布局的加载,并实现相应数据的加载:

public class FruitActivity extends AppCompatActivity {
    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_fruit);

        Intent intent = getIntent();
        String fruit_Name = intent.getStringExtra("fruit_name");
        int fruit_Image = intent.getIntExtra("fruit_image",0);

        Toolbar toolbar = findViewById(R.id.fruit_toolBar);
        setSupportActionBar(toolbar);
        ActionBar actionBar = getSupportActionBar();
        if(actionBar!=null){
            actionBar.setDisplayHomeAsUpEnabled(true);
        }

        CollapsingToolbarLayout collapsingToolbarLayout = findViewById(R.id.collapsingToolBar);
        ImageView imageView = findViewById(R.id.fruit_image);
        TextView textView = findViewById(R.id.fruit_context);
        
        collapsingToolbarLayout.setTitle(fruit_Name);
        Glide.with(this).load(fruit_Image).into(imageView);
        textView.setText("我是" + fruit_Name);
    }

    @Override
    public boolean onOptionsItemSelected(@NonNull MenuItem item) {
        switch (item.getItemId()){
            case android.R.id.home:
                finish();
                return true;
        }
        return super.onOptionsItemSelected(item);
    }

}

onCreate方法中,首先通过Intent获取了启动FruitActivity时传过来的fruit_name与fruit_image,接着使用了Toolbar的标准用法,将它作为ActionBar显示,并调用setDisplayHomeAsUpEnabled启动Home按钮,默认图标箭头作用是返回上一个Activity,id就是android.R.id.home。因此重写onOptionsItemSelected方法在其中添加监听事件,当点击返回按钮时就finish当前Activity。

接下来填充界面上的内容,调用CollapsingToolbarLayout实例的setTitle方法将传进来的fruit_name设置为界面标题内容,然后使用Glide加载传入的fruit_image并设置到标题栏的ImageView上面,最后添加水果详情,这里就直接设置为“我是”+fruit_name了。

最后还要添加FruitActivity的启动事件,需要在MainActivity中的RecyclerView中添加点击子项的监听事件,按照之前学习的步骤,应该是在FruitAdapter的onCreateViewHolder中添加:
在这里插入图片描述

完整的FruitAdapter代码如下:

public class FruitAdapter extends RecyclerView.Adapter<FruitAdapter.ViewHolder> {
    private Context context;
    private List<Fruit> fruits;
    @NonNull
    @Override
    public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        View view = LayoutInflater.from(context).inflate(R.layout.fruit_item,parent,false);
        final ViewHolder viewHolder = new ViewHolder(view);
        viewHolder.itemView.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                 Fruit fruit = fruits.get(viewHolder.getAdapterPosition());
                 Intent intent = new Intent(context,FruitActivity.class);
                 intent.putExtra("fruit_name",fruit.getName());
                 intent.putExtra("fruit_image",fruit.getIcon());
                 context.startActivity(intent);
            }
        });
        return viewHolder;
    }

    public FruitAdapter(Context context, List<Fruit> fruits) {
        this.context = context;
        this.fruits = fruits;
    }

    @Override
    public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
        Glide.with(context).load(fruits.get(position).getIcon()).into(holder.fruit_icon);
        holder.fruit_name.setText(fruits.get(position).getName());
    }

    @Override
    public int getItemCount() {
        return fruits.size();
    }

    static class ViewHolder extends RecyclerView.ViewHolder{
         private TextView fruit_name;
         private ImageView fruit_icon;
         public ViewHolder(@NonNull View itemView) {
             super(itemView);
             fruit_icon = itemView.findViewById(R.id.fruit_icon);
             fruit_name = itemView.findViewById(R.id.fruit_name);
         }
     }
}

最后为了能让背景图和系统状态栏相融合,还需要借助android:fitsSystemWindows="true"这个属相来实现,我们将activity_fruit.xml布局中ImageView及其所有父布局添加这个属性,activity_fruit.xml最终编辑如下:

<?xml version="1.0" encoding="utf-8"?>
<androidx.coordinatorlayout.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">
    <com.google.android.material.appbar.AppBarLayout
        android:id="@+id/appbar"
        android:layout_width="match_parent"
        android:layout_height="250dp"
        android:fitsSystemWindows="true">
        <com.google.android.material.appbar.CollapsingToolbarLayout
            android:id="@+id/collapsingToolBar"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"
            app:contentScrim="@color/colorPrimary"
            app:layout_scrollFlags="scroll|exitUntilCollapsed"
            android:fitsSystemWindows="true">
              <ImageView
                  android:id="@+id/fruit_image"
                  android:layout_width="match_parent"
                  android:layout_height="match_parent"
                  android:scaleType="centerCrop"
                  app:layout_collapseMode="parallax"
                  android:fitsSystemWindows="true"/>
              <androidx.appcompat.widget.Toolbar
                  android:id="@+id/fruit_toolBar"
                  android:layout_width="match_parent"
                  android:layout_height="?attr/actionBarSize"
                  app:layout_collapseMode="pin"/>
        </com.google.android.material.appbar.CollapsingToolbarLayout>
    </com.google.android.material.appbar.AppBarLayout>

    <androidx.core.widget.NestedScrollView
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        app:layout_behavior="@string/appbar_scrolling_view_behavior">
        <LinearLayout
            android:orientation="vertical"
            android:layout_width="match_parent"
            android:layout_height="wrap_content">
            <com.google.android.material.card.MaterialCardView
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:layout_margin="15dp"
                app:cardCornerRadius="5dp">
                <TextView
                    android:id="@+id/fruit_context"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:layout_margin="10dp"/>
            </com.google.android.material.card.MaterialCardView>
        </LinearLayout>
    </androidx.core.widget.NestedScrollView>

    <com.google.android.material.floatingactionbutton.FloatingActionButton
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_margin="15dp"
        android:src="@drawable/ic_comment"
        app:layout_anchor="@id/appbar"
        app:layout_anchorGravity="bottom|end"/>

</androidx.coordinatorlayout.widget.CoordinatorLayout>

指定好android:fitsSystemWindows属性后,还需要在程序的主题中将状态栏颜色指定为透明色,修改styles.xml文件:
在这里插入图片描述
将android:statusBarColor属性的值指定成@android:color/transparent即可,最后让FruitActivity使用这个主体,修改Manifest.xml文件:
在这里插入图片描述
指定完成后重新运行,效果如下:
在这里插入图片描述

最终效果

至此完成了整个项目,最终效果如下:
在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/weixin_42011443/article/details/107213604