本篇文章主要介绍以下几个知识点:

  • Toolbar
  • 滑动菜单
  • 悬浮按钮
  • 卡片式布局 
  • 下拉刷新 
  • 可折叠式标题栏

蒙奇·D·路飞

  Material Design 是由谷歌的设计工程师基于优秀的设计原则,结合丰富的创意和科学技术所发明的一套全新的界面设计语言,包含了视觉、运动、互动效果等特性。

  在2015年的 Google I/O 大会上推出了一个 Design Support 库,这个库将 Material Design 中最具代表性的一些控件和效果进行了封装,使开发者能轻松地将自己的应用 Material 化。本篇文章就来学习下 Design Support 这个库,实现以下效果:

Material Design

12.1 Toolbar

  相信大家熟悉控件 ActionBar,由于其设计的原因,被限定只能位于活动的顶部,从而不能实现一些 Material Design 的效果,因此官方已不建议使用 ActionBar 了,而推荐使用 Toolbar。

  Toolbar 不仅继承了 ActionBar 的所有功能,而且灵活性很高。下面来具体学习下。

  首先,修改一下项目的默认主题,指定为不带 ActionBar 的主题,打开 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></resources>

  观察下 AppTheme 中的属性重写,3个属性代表的颜色位置如下:

各属性指定颜色的位置

  把 ActionBar 隐藏起来后,用 ToolBar 来替代 ActionBar,修改布局中的代码如下:

<?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.Toolbarandroid: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>

  上述代码指定一个 xmlns:app 的命名空间是由于很多 Meterial 属性在5.0之前的系统中并不存在,为了能够兼容之前的老系统,应该使用 app:attribute 这样的写法,从而就可以兼容 Android 5.0 以下的系统了。

  接下来修改活动中的代码如下:

public class MaterialDesignActivity extends AppCompatActivity {@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_material_design);Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);setSupportActionBar(toolbar);// 将 Toolbar 的实例传入}
}

  运行效果如下:

Toolbar 的标准界面

  上面效果和之前的标题栏差不多,接下来学习一下 Toolbar 常用的功能,比如修改标题栏上显示的文字内容。这文字内容是在 AndroidManifest.xml 中指定的,如下:

<activity android:name=".chapter12.MaterialDesignActivity"android:label="Fruits"/>

  上面给 activity 增加了一个 android:label 属性,指定在 Toolbar 中显示的文字内容,若没指定的话,默认使用 application 中指定的 label 内容,即应用名称。

  接下来再添加一些 action 按钮使 Toolbar 更加丰富一些。右击 res 目录→New→Directory,创建一个 menu 文件夹。然后右击 menu 文件夹→New→Menu resource file,创建一个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>标签定义 action 按钮android:id 按钮id;android:icon 按钮图标;android:title 按钮文字app:showAsAction 按钮的显示位置:always 永远显示在Toolbar中,若屏幕不够则不显示ifRoom 屏幕空间足够时显示在Toolbar中,不够时显示在菜单中never 永远显示在菜单中(注意:Toolbar中的action只显示图标,菜单中的action只显示文字)--><itemandroid:id="@+id/backup"android:icon="@mipmap/backup"android:title="Backup"app:showAsAction="always"/><itemandroid:id="@+id/delete"android:icon="@mipmap/delete"android:title="Delete"app:showAsAction="ifRoom"/><itemandroid:id="@+id/settings"android:icon="@mipmap/settings"android:title="Settings"app:showAsAction="never"/>
</menu>

  然后修改活动中的代码如下:

public class MaterialDesignActivity extends AppCompatActivity {@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_material_design);Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);setSupportActionBar(toolbar);// 将 Toolbar 的实例传入}@Overridepublic boolean onCreateOptionsMenu(Menu menu) {// 加载菜单getMenuInflater().inflate(R.menu.toolbar,menu);return true;}@Overridepublic boolean onOptionsItemSelected(MenuItem item) {// 设置点击事件switch (item.getItemId()){case R.id.backup:ToastUtils.showShort("点击了备份");break;case R.id.delete:ToastUtils.showShort("点击了删除");break;case R.id.settings:ToastUtils.showShort("点击了设置");break;}return true;}
}

  运行效果如下:

带有 action 按钮的 Toolbar

  好了,关于 Toolbar 的内容就先讲到这。当然 Toolbar 的功能远远不只这些,更多功能以后再挖掘。

12.2 滑动菜单

  滑动菜单可以说是 Meterial Design 中最常见的效果之一,将一些菜单项隐藏起来,而不是放置在主屏幕上,通过滑动的方式将菜单显示出来。

12.2.1 DrawerLayout

  谷歌提供了一个 DrawerLayout 控件,借助这个控件,实现滑动菜单简单又方便。下面介绍下它的用法。

  首先它是一个布局,在布局中允许放入两个直接子控件,第一个子控件是主屏幕中显示的内容,第二个子控件是滑动菜单显示的内容。因此,可以修改布局代码如下所示:

<?xml version="1.0" encoding="utf-8"?>
<android.support.v4.widget.DrawerLayoutxmlns: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"><!--********** 第一个子控件 主屏幕显示 ***********--><FrameLayoutandroid:layout_width="match_parent"android:layout_height="match_parent"><android.support.v7.widget.Toolbarandroid: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:layout_gravity 是必须指定的 --><TextViewandroid:layout_width="match_parent"android:layout_height="match_parent"android:layout_gravity="start"android:text="这是滑动菜单" android:background="#fff"/></android.support.v4.widget.DrawerLayout>

  注意:layout_gravity 这个属性是必须指定的,因为我们需要告诉DrawerLayout滑动菜单是在屏幕的左边还是右边,指定left表示滑动菜单在左边,指定right表示滑动菜单在右边。这里指定的start,表示根据系统语言进行判断,如果系统语言是从左往右,比如英语,汉语,滑动菜单就在左边,反之,如果是阿拉伯语,菜单就在右边。

接下来在 Toolbar 的最左边加入一个导航按钮,提示用户屏幕左侧边缘是可以拖动的,点击按钮将滑动菜单的内容展示出来。修改活动中的代码如下:

public class MaterialDesignActivity extends AppCompatActivity {private DrawerLayout mDrawerLayout;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_material_design);Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);setSupportActionBar(toolbar);// 将 Toolbar 的实例传入mDrawerLayout = (DrawerLayout) findViewById(R.id.drawer_layout);ActionBar actionBar = getSupportActionBar();if (actionBar != null){actionBar.setDisplayHomeAsUpEnabled(true); //让导航按钮显示出来actionBar.setHomeAsUpIndicator(R.mipmap.menu);//设置导航按钮图标}}@Overridepublic boolean onCreateOptionsMenu(Menu menu) {// 加载菜单getMenuInflater().inflate(R.menu.toolbar,menu);return true;}@Overridepublic boolean onOptionsItemSelected(MenuItem item) {// 设置点击事件switch (item.getItemId()){case android.R.id.home:mDrawerLayout.openDrawer(GravityCompat.START);//打开抽屉break;. . .}return true;}
}

  上述代码值得注意的是在 onOptionItemSelected() 方法中对 HomeAsUp 按钮的点击事件进行处理,HomeAsUp 按钮的 id永远是 android.R.id.home。现在重新运行程序,效果如下:

显示滑动菜单界面

12.2.2 NavigationView

  上面已经成功实现了滑动菜单功能,但界面不美观,谷歌给我们提供了一种更好的方法——使用 NavigationView,在滑动菜单页面定制任意布局。

  NavigationView 是 Design Support 库中提供的一个控件,严格按照 Material Design 的要求来设计的,并且将滑动菜单页面的实现变得非常简单。下面一起来学习下。

  首先,将 Design Support 库以及开源项目 CircleImageView(实现图片圆形化,项目主页地址:https://github.com/hdodenhof/CircleImageView )引入到项目中:

compile 'com.android.support:design:24.2.1'
compile 'de.hdodenhof:circleimageview:2.1.0'

  在使用 NavigationView 之前,还需要准备两个东西:在 NavigationView 中用来显示具体菜单项的 menu 和显示头部布局的 headerLayout。

 (1)准备 menu。在 menu 文件夹下创建一个 nav_menu.xml 文件,编写代码如下:

<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"><!--checkableBehavior指定为single表示组中的所有菜单项只能单选 --><group android:checkableBehavior="single"><itemandroid:id="@+id/nav_call"android:icon="@mipmap/call"android:title="Call"/><itemandroid:id="@+id/nav_friends"android:icon="@mipmap/friends"android:title="Friends"/><itemandroid:id="@+id/nav_location"android:icon="@mipmap/location"android:title="Location"/><itemandroid:id="@+id/nav_mail"android:icon="@mipmap/mail"android:title="Mail"/><itemandroid:id="@+id/nav_task"android:icon="@mipmap/task"android:title="Task"/></group>
</menu>

 (2)准备 headerLayout。在 layout 文件夹下创建一个布局文件 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.CircleImageViewandroid:id="@+id/icon_image"android:layout_width="70dp"android:layout_height="70dp"android:src="@mipmap/nav_icon"android:layout_centerInParent="true"/><!--*************** 邮箱 ****************--><TextViewandroid:id="@+id/mail"android:layout_width="match_parent"android:layout_height="wrap_content"android:layout_alignParentBottom="true"android:gravity="center"android:text="KXwonderful@gmail.com"android:textColor="@color/white"android:textSize="14sp"/><!--*************** 用户名 ****************--><TextViewandroid:id="@+id/username"android:layout_width="match_parent"android:layout_height="wrap_content"android:layout_above="@+id/mail"android:gravity="center"android:text="开心wonderful"android:textColor="@color/white"android:textSize="14sp"/></RelativeLayout>

  准备好 menu 和 headerLayout 后,可以使用 NavigationView 了,修改布局代码,将之前的 TextView 替换成 NavigationView,如下:

<android.support.v4.widget.DrawerLayoutxmlns: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"><!--******** 第一个子控件 主屏幕显示 ********--><FrameLayoutandroid:layout_width="match_parent"android:layout_height="match_parent"><android.support.v7.widget.Toolbarandroid: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:layout_gravity 是必须指定的 --><android.support.design.widget.NavigationViewandroid: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>

  最后,修改活动中的代码,添加处理菜单项的点击事件,如下:

public class MaterialDesignActivity extends AppCompatActivity {private DrawerLayout mDrawerLayout;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_material_design);Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);setSupportActionBar(toolbar);// 将 Toolbar 的实例传入mDrawerLayout = (DrawerLayout) findViewById(R.id.drawer_layout);NavigationView navView = (NavigationView) findViewById(R.id.nav_view);ActionBar actionBar = getSupportActionBar();if (actionBar != null){actionBar.setDisplayHomeAsUpEnabled(true); //让导航按钮显示出来actionBar.setHomeAsUpIndicator(R.mipmap.menu);//设置导航按钮图标}navView.setCheckedItem(R.id.nav_call);//设置默认选中项navView.setNavigationItemSelectedListener(new NavigationView.OnNavigationItemSelectedListener() {@Overridepublic boolean onNavigationItemSelected(@NonNull MenuItem item) {mDrawerLayout.closeDrawers();//关闭抽屉ToastUtils.showShort("点击了"+item.getTitle());return true;}});}. . .
}

  现在重新运行下程序,效果如下:

NavigationView 界面

  上面效果相对之前的美观了许多,但不要满足现状,跟紧脚步,继续学习。

12.3 悬浮按钮和可交互提示

  立体设计是 Material Design 中一条非常重要的思想,即应用程序的界面不仅仅是一个平面,而应该是有立体效果的。

12.3.1 FloatingActionButton

  FloatingActionButton 是 Design Support 库中提供的一个控件,可以比较轻松地实现悬浮按钮地效果。它默认使用 colorAccent 作为按钮的颜色。下面来具体实现下。

  首先,在主屏幕布局加入一个 FloatingActionButton,修改布局代码如下:

<android.support.v4.widget.DrawerLayoutxmlns: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"><!--******** 第一个子控件 主屏幕显示 ********--><FrameLayoutandroid:layout_width="match_parent"android:layout_height="match_parent"><android.support.v7.widget.Toolbarandroid: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:elevation属性是给FloatingActionButton指定高度,值越大,投影范围越大,效果越淡,反之 --><android.support.design.widget.FloatingActionButtonandroid:id="@+id/fab"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_gravity="bottom|end"android:layout_margin="16dp"android:src="@mipmap/done"app:elevation="8dp"/></FrameLayout>. . .</android.support.v4.widget.DrawerLayout>

  接着,设置 FloatingActionButton 的点击事件,在活动中添加代码如下:

public class MaterialDesignActivity extends AppCompatActivity {private DrawerLayout mDrawerLayout;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_material_design);. . .FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab);fab.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {ToastUtils.showShort("点击了悬浮按钮");}});}. . .
}

  现在重新运行下程序,效果如下:

FloatingActionButton 的效果

12.3.2 Snackbar

  接下来学习下 Design Support 库提供的比 Toast 更加先进的提示工具——Snackbar。

  当然,Snackbar 并不是 Toast 的替代品,它们两者之间有着不同的应用场景。Snackbar 在提示当中加入了一个可交互按钮,点击时可以执行一些额外的操作。

  Snackbar 的用法非常简单,修改活动中的代码如下:

public class MaterialDesignActivity extends AppCompatActivity {private DrawerLayout mDrawerLayout;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_material_design);. . .FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab);fab.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {//ToastUtils.showShort("点击了悬浮按钮");Snackbar.make(v,"删除数据",Snackbar.LENGTH_SHORT).setAction("取消", new View.OnClickListener() {@Overridepublic void onClick(View v) {ToastUtils.showShort("数据恢复");}}).show();}});}. . .
}

  注意:第一个参数传入一个view,只要当前界面布局的任意一个View都可以,Snaker会使用这个View来自动查找最外层的布局,用于展示Snackbar.

现在重新运行下程序,效果如下:

Snackbar 的效果

  可以看到,Snackbar 从屏幕底部出现,上面有设置的提示文字和设置的取消按钮。但存在个 bug,这个 Snackbar 把悬浮按钮给遮挡住了,解决这个 bug 就要借助 CoordinatorLayout 了。

12.3.3 CoordinatorLayout

  CoordinatorLayout 是 Design Support 库提供的一个加强版的 FrameLayout 布局,它可以监听其所有子控件的各种事件,然后自动帮我们做出最为合理的响应。

  CoordinatorLayout 的使用也非常简单,只需将原来的 FrameLayout 替换一下就可以了。修改布局代码如下:

<android.support.v4.widget.DrawerLayoutxmlns: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.CoordinatorLayoutandroid:layout_width="match_parent"android:layout_height="match_parent"><android.support.v7.widget.Toolbarandroid: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.FloatingActionButtonandroid:id="@+id/fab"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_gravity="bottom|end"android:layout_margin="16dp"android:src="@mipmap/done"app:elevation="8dp"/></android.support.design.widget.CoordinatorLayout>. . .</android.support.v4.widget.DrawerLayout>

  现在重新运行下程序,效果如下:

CoordinatorLayout 自动将悬浮按钮上移

  可以看到悬浮按钮自动向上移,从而不会被遮住。

  上面 Snackbar 好像并不是 CoordinatorLayout 的子控件,却也能被监听到,是因为在 Snackbar 中传入的第一个参数是用来指定 Snackbar 是基于哪个 View 来触发的,上面传入的是 FloatingActionButton 本身,而 FloatingActionButton 是 CoordinatorLayout 的子控件。

12.4 卡片式布局

  卡片式布局是 Material Design 中提出的一个新概念,它可以让页面中的元素看起来就像在卡片中一样,并且还能拥有圆角和投影。

12.4.1 CardView

  CardView 是 appcompat-v7 库提供的用于实现卡片式布局效果的重要控件,它也是一个 FrameLayout,只是额外提供了圆角和阴影等效果,有立体感。

  CardView 的基本用法非常简单,如下:

<!-- app:cardCornerRadius 指定卡片圆角的弧度,值越大弧度越大app:elevation 指定卡片的高度,值越大投影范围越大,效果越淡-->
<android.support.v7.widget.CardViewandroid:layout_width="wrap_content"android:layout_height="wrap_content"app:cardCornerRadius="4dp"app:elevation="5dp"><TextViewandroid:id="@+id/info_text"android:layout_width="wrap_content"android:layout_height="wrap_content" />
</android.support.v7.widget.CardView>

  上面代码在 CardView 中放了一个 TextView,这个 TextView 就会显示在一张卡片上。


  接下来,综合运用第3章的知识,用RecyclerView 来填充项目的主界面部分,实现 OnePiece(海贼王) 列表。

  首先,添加如下要用到的依赖库:

compile 'com.android.support:recyclerview-v7:24.2.1'
compile 'com.android.support:cardview-v7:24.2.1'
compile 'com.github.bumptech.glide:glide:3.7.0'

  上面的 Glide 库是一个图片加载库,其项目主页地址是:https://github.com/bumptech/glide 。

  接着,在布局文件中添加 RecyclerView,如下:

<android.support.v4.widget.DrawerLayoutxmlns: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.CoordinatorLayoutandroid:layout_width="match_parent"android:layout_height="match_parent"><android.support.v7.widget.Toolbarandroid:id="@+id/toolbar". . . /><android.support.v7.widget.RecyclerViewandroid:id="@+id/rv_one_piece"android:layout_width="match_parent"android:layout_height="match_parent"/><android.support.design.widget.FloatingActionButtonandroid:id="@+id/fab". . . /></android.support.design.widget.CoordinatorLayout>. . .</android.support.v4.widget.DrawerLayout>

  接着,定义一个实体类 Partner,如下:

public class Partner {private String name;  // 伙伴名称private int imageId; // 伙伴对应头像的资源idprivate int profileId; // 人物简介的资源idpublic Partner(String name, int imageId, int profileId) {this.name = name;this.imageId = imageId;this.profileId = profileId;}public String getName(){return name;}public int getImageId(){return imageId;}public int getProfileId() {return profileId;}
}

  然后为 RecyclerView 的子项指定一个自定义布局,新建 partner_item.xml,使用 CardView 作为最外层布局,如下:

<?xml version="1.0" encoding="utf-8"?>
<android.support.v7.widget.CardViewxmlns: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"><LinearLayoutandroid:layout_width="match_parent"android:layout_height="wrap_content"android:orientation="vertical"><!--******** 显示头像********--><ImageViewandroid:id="@+id/partner_image"android:layout_width="match_parent"android:layout_height="100dp"android:scaleType="centerCrop"/><!--******** 显示名称********--><TextViewandroid:id="@+id/partner_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>

  接下来为 RecyclerView 准备一个适配器,新建 PartnerAdapter 类如下:

public class PartnerAdapter extends RecyclerView.Adapter<PartnerAdapter.ViewHolder>{private Context mContext;private List<Partner> mPartnerList;static class ViewHolder extends RecyclerView.ViewHolder{CardView cardView;ImageView partnerImage;TextView partnerName;public ViewHolder(View itemView) {super(itemView);cardView = (CardView) itemView;partnerImage = (ImageView) itemView.findViewById(R.id.partner_image);partnerName = (TextView) itemView.findViewById(R.id.partner_name);}}public PartnerAdapter(List<Partner> partnerList){mPartnerList = partnerList;}@Overridepublic ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {if (mContext == null){mContext = parent.getContext();}View view = LayoutInflater.from(mContext).inflate(R.layout.partner_item,parent,false);return new ViewHolder(view);}@Overridepublic void onBindViewHolder(ViewHolder holder, int position) {Partner partner = mPartnerList.get(position);holder.partnerName.setText(partner.getName());Glide.with(mContext).load(partner.getImageId()).into(holder.partnerImage);}@Overridepublic int getItemCount() {return mPartnerList.size();}
}

  上述代码中用到 Glide 加载本地图片,它会帮我们把图片压缩,因此不用担心内存溢出。

  适配器准备好了,最后修改活动中的代码如下:

public class MaterialDesignActivity extends AppCompatActivity {private DrawerLayout mDrawerLayout;private Partner[] partners = {new Partner("路飞",R.mipmap.partner_luffy,R.string.partner_luffy),new Partner("索隆",R.mipmap.partner_zoro,R.string.partner_zoro),new Partner("山治",R.mipmap.partner_sanji,R.string.partner_sanji),new Partner("艾斯",R.mipmap.partner_ace,R.string.partner_ace),new Partner("罗",R.mipmap.partner_law,R.string.partner_law),new Partner("娜美",R.mipmap.partner_nami,R.string.partner_nami),new Partner("罗宾",R.mipmap.partner_robin,R.string.partner_robin),new Partner("薇薇",R.mipmap.partner_vivi,R.string.partner_vivi),new Partner("蕾贝卡",R.mipmap.partner_rebecca,R.string.partner_rebecca),new Partner("汉库克",R.mipmap.partner_hancock,R.string.partner_hancock)};private List<Partner> partnerList = new ArrayList<>();private PartnerAdapter adapter;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_material_design);. . .initPartner();RecyclerView recyclerView = (RecyclerView) findViewById(R.id.rv_one_piece);GridLayoutManager layoutManager = new GridLayoutManager(this,2);recyclerView.setLayoutManager(layoutManager);adapter = new PartnerAdapter(partnerList);recyclerView.setAdapter(adapter);}/*** 初始化数据,随机挑选50条数据*/private void initPartner() {partnerList.clear();for (int i = 0;i < 50 ;i++){Random random = new Random();int index = random.nextInt(partners.length);partnerList.add(partners[index]);}}. . .
}

  现在重新运行下程序,效果如下:

卡片式布局效果

  可以看到,精美的图片成功展示出来了,但 Toolbar 却被挡住了,这是由于 RecyclerView 和 Toolbar 都是放置在 CoordinatorLayout 中,而 CoordinatorLayout 是一个增强版的 FrameLayout,其控件在不进行明确定位时默认放在布局的左上角,从而产生了遮挡的现象。解决这个 bug 就要借助到另外一个工具了——AppBarLayout。

12.4.2 AppBarLayout

  AppBarLayout 是 Design Support 库提供的一个垂直方向的 LinearLayout,它在内部做了很多滚动事件的封装,并应用了一些 Meterial Design 的设计理念。

  只需两步就可以解决前面的遮挡问题,第一步是将 Toolbar 嵌套到 AppBarLayout 中,第二步给 RecyclerView 指定一个布局行为。修改布局代码如下:

<android.support.v4.widget.DrawerLayout. . .><!--******** 第一个子控件 主屏幕显示 ********--><android.support.design.widget.CoordinatorLayoutandroid:layout_width="match_parent"android:layout_height="match_parent"><android.support.design.widget.AppBarLayoutandroid:layout_width="match_parent"android:layout_height="wrap_content"><android.support.v7.widget.Toolbarandroid: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.RecyclerViewandroid:id="@+id/rv_one_piece"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>

  现在重新运行下程序,效果如下:

解决遮挡问题

  当 AppBarLayout 接收到滚动事件时,它内部的子控件其实是可以指定如何取影响这些事件的,下面就来进一步优化,使当 RecyclerView 向上滚动时,Toolbar 隐藏,向下滚动时显示。修改布局代码如下所示:

<android.support.v4.widget.DrawerLayout. . .><!--******** 第一个子控件 主屏幕显示 ********--><android.support.design.widget.CoordinatorLayoutandroid:layout_width="match_parent"android:layout_height="match_parent"><android.support.design.widget.AppBarLayoutandroid:layout_width="match_parent"android:layout_height="wrap_content"><android.support.v7.widget.Toolbarandroid: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"/></android.support.design.widget.AppBarLayout><android.support.v7.widget.RecyclerView. . ./>. . .</android.support.design.widget.CoordinatorLayout>. . .</android.support.v4.widget.DrawerLayout>

  在 Toolbar 中添加了一个 app:layout_scrollFlags 属性,并指定成 scroll|enterAlways|snap。其中 scroll 指当 RecyclerView 向上滚动时,Toolbar 会跟着一起向上滚动并隐藏;enterAlways 指向下滚动时一起向下滚动并显示;snap 指当 Toolbar 还没有完全隐藏或显示时,会根据当前滚动的距离自动选择隐藏还是显示。

  现在重新运行下程序,效果如下:

自动显示或隐藏 Toolbar

12.5 下拉刷新

  在 Meterial Design 中,SwipeRefreshLayout 是用于实现下拉刷新的核心类,它由 support-v4 库提供,把要实现下拉刷新功能的控件放置到 SwipeRefreshLayout 中,就能让这个控件支持下拉刷新。

  SwipeRefreshLayout 用法比较简单,修改布局,在 RecyclerView 的外面嵌套一层 SwipeRefreshLayout,如下:

<android.support.v4.widget.DrawerLayout. . .><!--******** 第一个子控件 主屏幕显示 ********--><android.support.design.widget.CoordinatorLayoutandroid:layout_width="match_parent"android:layout_height="match_parent"><android.support.design.widget.AppBarLayoutandroid:layout_width="match_parent"android:layout_height="wrap_content"><android.support.v7.widget.Toolbar. . ./></android.support.design.widget.AppBarLayout><android.support.v4.widget.SwipeRefreshLayoutandroid: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.RecyclerViewandroid:id="@+id/rv_one_piece"android:layout_width="match_parent"android:layout_height="match_parent" /></android.support.v4.widget.SwipeRefreshLayout>. . .</android.support.design.widget.CoordinatorLayout>. . .</android.support.v4.widget.DrawerLayout>

  上述代码值得注意的是,由于 RecyclerView 变成了 SwipeRefreshLayout 的子控件,因此之前用 app:layout_behavior 声明布局行为要移到 SwipeRefreshLayout 中才行。

  接着还要在代码中处理具体的刷新逻辑,在活动中添加如下代码:

public class MaterialDesignActivity extends AppCompatActivity {. . .private SwipeRefreshLayout swipeRefresh;// 刷新@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_material_design);. . .// 下拉刷新swipeRefresh = (SwipeRefreshLayout) findViewById(R.id.swipe_refresh);swipeRefresh.setColorSchemeResources(R.color.colorPrimary);//设置刷新进度条颜色swipeRefresh.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {@Overridepublic void onRefresh() {// 处理刷新逻辑refreshPartner();}});}/*** 下拉刷新数据(为简单起见没和网络交互)*/private void refreshPartner() {new Thread(new Runnable() {@Overridepublic void run() {try {Thread.sleep(2000);} catch (InterruptedException e) {e.printStackTrace();}runOnUiThread(new Runnable() {@Overridepublic void run() {initPartner();//重新生成数据adapter.notifyDataSetChanged();//通知数据变化swipeRefresh.setRefreshing(false);//隐藏刷新进度条}});}}).start();}. . .
}

  现在重新运行下程序,效果如下:

实现下拉刷新效果

12.6 可折叠式标题栏

  作为本章的尾声,最后来实现一个震撼的 Material Design 效果——可折叠式标题栏。

12.6.1 CollapsingToolbarLayout

  CollapsingToolbarLayout 是 Design Support 库提供的一个作用于 Toolbar 基础之上的布局,它可让 Toolbar 的效果变得更加丰富。

  不过,CollapsingToolbarLayout 是不能独立存在的,只能作为 AppBarLayout 的直接子布局来使用。而 AppBarLayout 又必须是 CoordinatorLayout 的子布局。话不多说,开始吧。

  首先创建一个额外的活动 PartnerActivity 来作为海贼的简介界面,编写其对应的布局文件 activity_partner.xml 如下:

<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.CoordinatorLayoutxmlns: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.AppBarLayoutandroid:id="@+id/appBar"android:layout_width="match_parent"android:layout_height="250dp"><!-- android:theme 指定主题,我们并不陌生,只不过这里需要实现更高级的toogbar效果,因此需要经这个主题的指定提到上一层来。app:contentScrim 指定CollapsingToolbarLayout在趋于折叠以及折叠之后的背景色,其实折叠后就是一个普通的toobar,那么背景色可定应该是colorPrimary了。scroll    表示CollapsingToolbarLayout会随着水平内容详情的滚动一起滚动。   exitUntilCollapsed 指CollapsingToolbarLayout随着滚动完成折叠后就保留在界面上,不再移出界面--><android.support.design.widget.CollapsingToolbarLayoutandroid: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"><!-- app:layout_collapseMode 指定当前控件在在CollapsingToolbarLayout折叠过程中的折叠模式parallax 指折叠过程中会产生一定的错位偏移,这种模式的视觉效果会更好pin 指在折叠过程中位置始终保持不变--><ImageViewandroid:id="@+id/partner_image_view"android:layout_width="match_parent"android:layout_height="match_parent"android:scaleType="centerCrop"app:layout_collapseMode="parallax"/><android.support.v7.widget.Toolbarandroid:id="@+id/toolbar"android:layout_width="match_parent"android:layout_height="?attr/actionBarSize"app:layout_collapseMode="pin"/></android.support.design.widget.CollapsingToolbarLayout></android.support.design.widget.AppBarLayout><!--******************* 伙伴的简介内容 ****************--><!--NestedScrollView 在 ScrollView 基础上增加了嵌套响应滚动事件的功能,内部只能放一个直接子布局 -->
  <android.support.v4.widget.NestedScrollViewandroid:layout_width="match_parent"android:layout_height="match_parent"app:layout_behavior="@string/appbar_scrolling_view_behavior"><LinearLayoutandroid:layout_width="match_parent"android:layout_height="wrap_content"android:orientation="vertical"><android.support.v7.widget.CardViewandroid:layout_width="match_parent"android:layout_height="wrap_content"android:layout_marginBottom="15dp"android:layout_marginStart="15dp"android:layout_marginEnd="15dp"android:layout_marginTop="35dp"app:cardCornerRadius="4dp"><TextViewandroid:id="@+id/partner_profile"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_margin="10dp"android:lineSpacingMultiplier="2"/></android.support.v7.widget.CardView></LinearLayout></android.support.v4.widget.NestedScrollView><!--******************* 悬浮按钮 ****************--><!-- app:layout_anchor 指定一个瞄点--><android.support.design.widget.FloatingActionButtonandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_margin="16dp"android:src="@mipmap/comment"app:layout_anchor="@id/appBar"app:layout_anchorGravity="bottom|end"/></android.support.design.widget.CoordinatorLayout>

    注意:NestedScrollView类似于ScrollView使用方法,直接子View只能有一个,并且layout_width=”match_parent” 才能实现滚动效果。

编写完布局 activity_partner.xml 后,开始编写功能逻辑,修改活动 PartnerActivity 的代码如下:

public class PartnerActivity extends AppCompatActivity {public static final String PARTNER_NAME = "partner_name";  public static final String PARTNER_IMAGE_ID = "partner_image_id";public static final String PARTNER_PROFILE_ID = "partner_profile_id";@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_partner);Intent intent = getIntent();String partnerName = intent.getStringExtra(PARTNER_NAME); //海贼名称int partnerImageId = intent.getIntExtra(PARTNER_IMAGE_ID,R.mipmap.partner_luffy);//海贼图片idint partnerProfileId = intent.getIntExtra(PARTNER_PROFILE_ID,R.string.partner_luffy);//海贼资料idToolbar toolbar = (Toolbar) findViewById(R.id.toolbar);CollapsingToolbarLayout collapsingToolbar = (CollapsingToolbarLayout) findViewById(R.id.collapsing_toolbar);ImageView partnerImageView = (ImageView) findViewById(R.id.partner_image_view);TextView partnerProfile = (TextView) findViewById(R.id.partner_profile);setSupportActionBar(toolbar);ActionBar actionBar = getSupportActionBar();if (actionBar != null){actionBar.setDisplayHomeAsUpEnabled(true);}collapsingToolbar.setTitle(partnerName); //设置标题Glide.with(this).load(partnerImageId).into(partnerImageView);//设置图片partnerProfile.setText(getString(partnerProfileId));//设置内容}@Overridepublic boolean onOptionsItemSelected(MenuItem item) {switch (item.getItemId()){case android.R.id.home:// 返回上一个活动finish();return true;}return super.onOptionsItemSelected(item);}
}

  最后,为实现点击 RecyclerView 跳转到海贼详情界面,还需修改适配器 PartnerAdapter 的代码,添加点击事件如下:

public class PartnerAdapter extends RecyclerView.Adapter<PartnerAdapter.ViewHolder>{. . .@Overridepublic ViewHolder onCreateViewHolder(final ViewGroup parent, int viewType) {if (mContext == null){mContext = parent.getContext();}View view = LayoutInflater.from(mContext).inflate(R.layout.partner_item,parent,false);final ViewHolder holder = new ViewHolder(view);holder.cardView.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {int position = holder.getAdapterPosition();Partner partner = mPartnerList.get(position);Intent intent = new Intent(mContext,PartnerActivity.class);intent.putExtra(PartnerActivity.PARTNER_NAME,partner.getName());intent.putExtra(PartnerActivity.PARTNER_IMAGE_ID,partner.getImageId());intent.putExtra(PartnerActivity.PARTNER_PROFILE_ID,partner.getProfileId());mContext.startActivity(intent);}});return holder;}. . .
}

  好了,现在重新运行下程序,效果如下:

海贼详情展示效果

  上面展示的界面虽说已经很华丽了,但海贼背景图片和系统的状态栏总感觉有些不搭。下面就进一步提升一下。

12.6.2 充分利用系统状态栏控件

  为改善上面的不搭,接下来将海贼王背景图和状态栏融合到一起。这边提供两个方法:

  • 方案 1  

  借助 android:fitsSystemWindows 这个属性实现。将 ImageView 布局结构中的所有父控件都设置上这个属性并且指定为 True,修改 activity_partner.xml 如下:

<android.support.design.widget.CoordinatorLayout. . .android:fitsSystemWindows="true"><!--******************* 标题栏的界面 ****************--><android.support.design.widget.AppBarLayout. . .android:fitsSystemWindows="true"><android.support.design.widget.CollapsingToolbarLayout. . .android:fitsSystemWindows="true"><ImageViewandroid:id="@+id/partner_image_view"android:layout_width="match_parent"android:layout_height="match_parent"android:scaleType="centerCrop"app:layout_collapseMode="parallax"android:fitsSystemWindows="true"/>. . .</android.support.design.widget.CollapsingToolbarLayout></android.support.design.widget.AppBarLayout>. . .
</android.support.design.widget.CoordinatorLayout>

  然后还要在程序的主题中将状态栏颜色指定为透明色才行,即在主题中将属性android:statusBarColor 的值指定为 @android:color/transparent 就可以了,但问题是这个属性在 Android 5.0 系统开始才有的,因此需要准备两个同名不同内容的主题来实现。

  针对5.0及以上的系统,在 res 目录下创建一个 values-v21 目录,然后在此目录创建一个 styles.xml 文件如下:

<?xml version="1.0" encoding="utf-8"?>
<resources><style name="PartnerActivityTheme" parent="AppTheme"><item name="android:statusBarColor">@android:color/transparent</item></style>
</resources>

  针对5.0以下的系统,还需要在 value/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><!-- 5.0以下使用主题 --><style name="PartnerActivityTheme" parent="AppTheme"></style>
</resources>

  最后修改 AndroidManifest.xml 中的代码,让 PartnerActivity 使用这个主题,如下:

<activity android:name=".chapter12.PartnerActivity"android:theme="@style/PartnerActivityTheme">
</activity>

  这样就大功告成了,只要在 5.0 及以上系统运行程序,效果如下:

背景图和状态栏融合的效果

  • 方案 2

  方案1虽然实现了融合效果,但在低于5.0的系统上还是不搭,方案2就来稍微改善一下。

  方案2也要在 values-v21 目录下的 styles.xml 中新建一个主题AppTheme.NoActionBar,如下:

<style name="AppTheme.NoActionBar"><item name="windowActionBar">false</item><item name="windowNoTitle">true</item><item name="android:windowDrawsSystemBarBackgrounds">true</item><item name="android:statusBarColor">@android:color/transparent</item>
</style>

  在 value/styles.xml 中也新建一个主题 AppTheme.NoActionBar,如下:

<style name="AppTheme.NoActionBar" ><item name="windowActionBar">false</item><item name="windowNoTitle">true</item>
</style>

  接下来修改 AndroidManifest.xml 中的代码,让 PartnerActivity 使用这个主题,如下:

 <activityandroid:name=".chapter12.PartnerActivity"android:theme="@style/AppTheme.NoActionBar"></activity>

  最后在 PartnerActivity 中添加如下代码:

public class PartnerActivity extends AppCompatActivity {. . .@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_partner);translucentStatusBar();. . .}/*** 状态栏着色*/private void translucentStatusBar() {if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {//5.0及以上View decorView = getWindow().getDecorView();int option = View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | View.SYSTEM_UI_FLAG_LAYOUT_STABLE;decorView.setSystemUiVisibility(option);getWindow().setStatusBarColor(Color.TRANSPARENT);} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {//4.4到5.0WindowManager.LayoutParams localLayoutParams = getWindow().getAttributes();localLayoutParams.flags = (WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS | localLayoutParams.flags);}}. . .
}

  现在只要在 4.4 及以上系统运行程序就能有如下效果了:

背景图和状态栏

  好了,本篇文章就介绍到这。代码传送门:
  https://github.com/KXwonderful/MyFirstCode

  更多关于 Meterial Design 的内容可以参考官方文章:
  https://material.google.com

安卓开发——MaterialDesign实战相关推荐

  1. Android 9,安卓开发项目实战

    以机器学习为核心,打造更为智能的手机 Android 9 赋予手机强大的学习能力:系统能够根据用户在使用过程中展露的习惯与偏好,进行自我学习与适应 -- 从强劲续航到人性化应用推荐,Android 9 ...

  2. 安卓开发实战,用HMS MLKit华为机器学习服务开发一个拍照翻译小程序

    文章目录 引子 想象中的旅游 实际中的旅游 太难了 拍照翻译帮你忙 文本识别 翻译 拍照翻译APP开发实战 1 开发准备 1.1 在项目级gradle里添加华为maven仓 1.2 在应用级的buil ...

  3. 安卓开发的深度技术实战详解

    文章目录 一.安卓开发的深度技术 二.总结 三.协程 四.数据库 总结 一.安卓开发的深度技术 Kotlin 语言 Kotlin 语言是一种功能强大.安全.简洁且互操作性良好的编程语言,由 JetBr ...

  4. 安卓开发实战(1)之程序员入门代码,Hello,world!

    系列文章目录 文章目录 系列文章目录 前言 一.新建一个安卓项目 二.创建安卓虚拟器 1.点击右上角后,点击create device,选择自己需要的版本即可 2.点击运行 总结,自己在运行中出现的问 ...

  5. android简单app实例_Android安卓小项目实战视频教程集锦

    Android安卓小项目实战视频教程,点击进入视频教程: 一.安卓项目视频教程: 1蓝牙聊天APP介绍-分步骤介绍一个简单安卓蓝牙APP的开发过程 - 西瓜视频 2蓝牙聊天开发流程-分步骤介绍一个简单 ...

  6. 安卓开发中的 “Android高手” ,需要具备哪些技术?

    前言 **成为一名安卓开发者很容易,但是要成为一名 "Android 高手"却不那么容易:**这需要付出很多的努力,耐心,奉献和毅力才能做到 那么一个 Android 开发高手,需 ...

  7. 我是如何学习安卓开发的

    我的安卓学习之路 我的安卓之路主要有四个阶段: 入门 实践 准备面试 工作 1.入门 2014 年,学习 MFC 中途放弃的我,偶然间看到 Mars 前辈的安卓视频,看了几天写了个简单的应用,觉得安卓 ...

  8. 安卓开发app版本更新

    安卓开发实战之app之版本更新升级(DownloadManager和http下载)完整实现 转载 wx610a246613cb02021-08-05 17:02:56博主文章分类:14 其他随笔©著作 ...

  9. 最新《微专业Android安卓开发工程师课程》

    1.Android应用界面开发 资源下载 001 Android开发简介.flv 002 开始第一个应用.flv 003 Activity你必须知道的那些事(上).flv 004 Activity你必 ...

  10. python全栈工程师 pdf_python全栈工程师项目开发实例实战入门教程百度云

    python全栈工程师项目开发实例实战入门教程百度云 课程目录: 开学典礼 pycharm的基本使用 Python基本语法 数值类型数据及运算 字符串的基本操作 字符串的常用方法 列表的基本使用 列表 ...

最新文章

  1. DataGrid中,读取数据库中的图片并绑定数据列或磁盘目录中的图片,用输出流方式...
  2. Unix实用工具教程:《sed与awk》修订第三版清晰版
  3. 使用进程池模拟多进程爬取url获取数据,使用进程绑定的回调函数去处理数据...
  4. 2021年春季学期-信号与系统-第十四次作业参考答案-第六小题参考答案
  5. 大火系列: Rust入门篇 mut
  6. ubuntu php mysql5.6_Ubuntu 安装 Mysql 5.6 数据库
  7. Dubbo-go 源码笔记(一)Server 端开启服务过程
  8. 第二届战神杯线上编程挑战赛月赛第一题:回文数
  9. ServerBootstrap的启动流程
  10. windows xp 下的putty不能使用小键盘的问题
  11. RDD、DataFrame和DataSet
  12. 严格对角占优矩阵特征值_盖尔金圆定理及严格对角占优矩阵(SDD)
  13. 软件产品三步曲(内容、可用性、视觉)
  14. 201111-W-网络技术-基础理论与应用说明
  15. Java二叉树基础操作常见代码例题
  16. c语言开发excel带比例的饼图,教大家Excel饼图中既显示百分比又显示数量的方法...
  17. 最小二乘法-线性拟合
  18. Win10 桌面回收站右键没有“清空回收站”选项怎么解决
  19. 使用电脑工具--Mydesk
  20. 简单的UI框架 | 一、UI界面的搭建

热门文章

  1. POJ1008:玛雅日历
  2. fbx 骨架_骨架修剪
  3. CSS flex属性深入理解
  4. android 10.0 Camera2 去掉后置摄像头 仅支持前置摄像头功能
  5. 雷锋实验室: 伦敦奥运会手机应用盘点
  6. Web前端鼠标变小手CSS和JS(Vue)两种实现
  7. MFC程序阻止别的软件隐藏窗口,或拦截WM_SHOWWINDOW消息
  8. python报错ImportError: cannot import name ‘Image‘ from ‘PIL‘ 的时候大多数情况下是由于PIL版本和当前python的版本出现了不兼容
  9. UE4-4.26蓝图功能实现:按键控制物体旋转的“延伸”(点名系统Get all actors of class)
  10. int范围内数字的英文读法