0%

Android 详解 Material Design

1. 什么是 Material Design

  • 定义:Material Design 是 Google 在 推出的一套全新的界面设计语言。它基于传统优秀的设计原则、结合丰富的创意和科技,包含了视觉、运动、交互效果等特性

  • 发展

    • 2014 年 Google I/O 大会, Google 推出 Material Design
    • Android 5.0系统开始,Google 将所有内置应用都使用 Material Design 风格进行设计
    • 2015 年 Google I/O 大会,Google 推出了一个 Design Support 库,这个库将 Material Design 中最具代表性的一些控件和效果进行了封装,使得开发者可以轻松地将自己的应用 Material
  • 官方:https://material.google.com

2. 怎样理解 Toolbar

  • ActionBarToolbar 的对比

    • 每个活动最顶部的那个标题栏就是 ActionBar,由于自身设计的原因,ActionBar 只能位于活动的顶部,不能实现一些 Material Design 的效果。官方不再建议使用 ActionBar,而是更加推荐使用 Toolbar
    • Toolbar 不仅继承了 ActionBar 的所有功能,而且灵活性很高,和可以配合其他控件来完成一些 Material Design 的效果
    • Fragment 中最好不要直接使用 Toolbar 或者 ActionBar,复用的时候可能会出现意想不到的效果
  • 主题 Theme.AppCompat.NoActionBarTheme.AppCompat.Light.NoActionBar 的区别

    • AndroidManifest.xml 文件的 application 标签下,有一个 theme 属性,这个属性指定了一个 AppTheme 的主题,资源文件中这个 AppTheme 主题的 parent 默认是 Theme.AppCompat.Light.DarkActionBar
    • Theme.AppCompat.NoActionBar:表示深色主题,它会将界面的主题颜色设为深色,陪衬颜色设为淡色
    • Theme.AppCompt.Light.NoActionBar:表示淡色主题,它会将界面的主题颜色设为淡色,陪衬颜色设为深色
  • AppTheme 中的几个属性分别什么意思

    • colorPrimary:应用标题栏的颜色
    • colorPrimaryDark:系统状态栏的颜色
    • textColorPrimary:应用标题栏中文字的颜色
    • windowBackground:应用界面背景颜色
    • navigationBarColor:应用底部导航栏颜色
    • colorAccent:不只是用来指定一个按钮的颜色(比如FAB),更多表达一个强调的意思,比如一些控件的选中状态也会使用 colorAccent 的颜色
  • 解释一下 activity_main.xml 布局文件中 Toobar 标签主要属性的作用

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    // 布局文件中指定一个 xmlns:app 的命名空间是因为:
    // Material Design 是在 Android 5.0 系统中才出现的,很多的 Material 属性在 5.0 之前的系统中并不存在
    // 为了兼容之前的老系统,就不能使用 android:attribute 这样的写法了,而是应该使用 app:attribute
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent" >

    <android.support.v7.widget.Toolbar // Toolbar 控件是 appcompat-v7 库提供的
    android:id="@+id/toolbar"
    android:layout_width="match_parent"
    android:layout_height="?attr/actionBarSize" // 高度设置为 actionBar 的高度
    android:background="?arrr/colorPrimary" // 背景色

    // 前提是在 style.xml 中将程序的主题指定了淡色主题
    // 所以 Toolbar 上面的各种元素就会自动使用深色系,这是为了和主体颜色区分开
    // 但是这个效果很差,之前使用 ActionBar 时文字都是白色的,现在变成黑色会很难看
    // 为了能让 Toolbar 单独使用深色主题,而不受 styles.xml 中程序主题影响,这里使用 android:theme 属性进行单独制定
    // 但这也单独制定后又有新问题,如果 Toolbar 中有菜单项,那么弹出的菜单项也会变成深色主题,这样就再次变得很难看
    // 所以这里使用了 app:popupTheme 属性,将单独弹出的菜单项指定成了淡色主题
    android:theme="@style/ThemeOverlay.AppCompat.DarkActionBar" // 即,影响标题栏内文字的颜色
    app:popupTheme="@style/ThemeOverlay.AppCompat.Light" /> // 即,影响标题栏内菜单项的颜色
    </FrameLayout>

    // 使用 Toolbar
    Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
    setSupportActionBar(toolbar);
  • 解释下面 action 按钮布局文件 toolbar.xmlitem 标签主要属性的作用

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    <menu xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"> // 使用 app 命名空间兼容低版本系统
    <item // 使用 <item> 标签定义 action 按钮,Toolbar 中的 action 按钮只会显示图标、菜单中的 action 按钮只会显示文字
    android:id="@+id/backup"
    android:icon="@drawable/ic_backup"
    android:title="Backup"
    app:showAsAction="always" /> // always 表示永远显示在 Toolbar 中,如果屏幕空间不够则不显示

    <item // 使用 <item> 标签定义 action 按钮,Toolbar 中的 action 按钮只会显示图标、菜单中的 action 按钮只会显示文字
    android:id="@+id/delete"
    android:icon="@drawable/ic_delete"
    android:title="Delete"
    app:showAsAction="ifRoom" /> // ifRoom 表示屏幕空间足够的情况下显示在 Toolbar 中,不够的话就显示在菜单中

    <item // 使用 <item> 标签定义 action 按钮,Toolbar 中的 action 按钮只会显示图标、菜单中的 action 按钮只会显示文字
    android:id="@+id/settings"
    android:icon="@drawable/ic_settings"
    android:title="Settings"
    app:showAsAction="never" /> // never 表示不显示在 Toolbar 中,只显示在菜单中
    </menu>

    // 使用 toolbar.xml
    public boolean onCreateOptionsMenu(Menu menu) {
    getMenuInflater().inflate(R.menu.toolbar, menu);
    return true;
    }

3. 使用 DrawerLayoutNavigationView 实现一个滑动菜单 Demo

  • MainActivity

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    public class MainActivity extends AppCompatActivity {
    private Toolbar mToolbar;
    private DrawerLayout mDrawerLayout;
    private NavigationView mNavView; //使用 NavigationView 需要在 app/build.gradle 文件的 dependencies 闭包中添加依赖

    @Override
    protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    mToolbar = (Toolbar) findViewById(R.id.toolbar);
    mDrawableLayout = (DrawerLayout) findViewById(R.id.drawer_layout);
    mNavView = (NavigationView) findVieById(R.id.nav_view);

    ActionBar actionBar = getSupportActionBar(); // 这里这个 ActionBar 实例的具体实现是由 Toolbar 来完成的
    if(actionBar != null) { // 在 Toolbar 的最左边加上一个导航按钮,实际上这个按钮叫作 HomeAsUp 按钮,默认图标是一个返回的箭头
    actionBar.setDisplayHomeAsUpEnabled(true); // 让导航按钮显示出来
    actionBar.setHomeAsUpIndicator(R.drawable.ic_menu); // 设置导航按钮图标
    }

    mNavView.setCheckedItem(R.id.nav_call); // 将 Call 菜单项设置为默认选中
    mNavView.setNavigationItemSelectedListener(new NavigationView.OnNavigationItemSelectedListener() { // 设置菜单项选中的监听器
    @Override
    public boolean onNavigationItemSelected(MenuItem item) {
    mDrawerLayout.closeDrawers(); // 将滑动菜单关闭
    return true;
    }
    });
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
    switch (item.getItemId()) {
    case android.R.id.home: // HomeAsUp 按钮的 id 永远都是 android.R.id.home
    // 将滑动菜单展示出来,方法要求传入一个 Gravity 参数,这里为了保证行为和 XML 中定义的一致,传入 GravityCompat.START
    mDrawerLayout.openDrawer(GravityCompat.START);
    break;
    default:
    break;
    }
    return true;
    }
    }
  • activity_main.xml

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    <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"

    // 这个属性必须要指定,我们需要告诉 DrawerLayout 滑动菜单是在屏幕的左边还是右边
    // 指定 left 表示滑动菜单在左边,指定 right 表示滑动菜单在右边
    // 指定 start 表示会根据系统语言进行判断,如果系统语言是从左往右的,比如英语、汉语,滑动菜单就在左边;如果系统语言是从右往左的,比如阿拉伯语,滑动菜单就在右边
    android:layout_gravity="start"

    // 在 nav_menu.xml 布局文件标签 <menu> 中嵌套了一个 <group> 标签,然后将 group 的 checkableBehavior 属性指定为 single
    // group 表示一个组,checkableBehavior 指定为 single 表示组中所有菜单项只能单选
    app:menu="@menu/nav_menu"
    app:headerLayout="@layout/nav_header" /> // 将 nav_header.xml 文件的高度设为 180dp,这是一个 NavigationView 比较适合的高度

    </android.support.v4.widget.DrawerLayout>

4. View 的 elevation 属性的作用是

  • elevation 属性可以给 View 指定一个高度值,高度值越大,投影范围也越大,但是投影效果越淡;高度值越小,投影范围也越小,但投影效果越浓
  • FloatingActionButton 中有这个属性的默认值,可以看到投影/阴影效果

5. SnackbarToast 的区别

  • Snackbar 不是 Toast 的替代品,它们两者之间有着不同的应用场景
  • Toast: 告诉用户现在发生了什么事情,但同时用户只能被动接收这个事情,用户不能做出选择
  • Snackbar: 对交互逻辑进行了扩展,不管是出现还是消失都是带有动画效果的,而且允许在提示当中加入一个可交互按钮,当用户点击按钮的时候可以执行一些额外的逻辑操作

6. 使用 Snackbar 实现一个简单的 Demo

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab);
fab.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
// 第一个参数需要传入一个 View,只要是当前页面布局的任意一个 View 都可以,Snackbar 会使用这个 View 来自动查找最外层的布局,用于展示 Snackbar
// 第二个参数是 Snackbar 中显示的内容
// 第三个参数是 Snackbar 的显示时长
Snackbar.make(view, "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();
}
});

7. 怎样理解 CoordinatorLayout

  • CoordinatorLayout 是一个加强版的 FrameLayout
  • CoordinatorLayout 可以监听其所有子控件的各种事件,然后自动帮我们做出最为合理的响应,比如防止子控件之间的遮挡、子控件之间动画同步等

8. 怎样理解 CardView

  • CardView 是用于实现卡片式布局效果的重要控件,由 appcompat-v7 提供
  • CardView 也是一个 FrameLayout,只是额外提供了圆角阴影等效果

9. 怎样理解 AppBarLayout

  • AppBarLayout 实际上是一个垂直方向的 LinearLayout
  • AppBarLayout 在内部做了很多滚动事件的封装,并应用了一些 Material Design 的设计理念

10. 使用 CoordinatorLayoutAppBarLayout 等实现一个滚动流畅的 Demo

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
<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"
androdi:layout_height="match_parent" >

<android.support.design.widget.CoordinatorLayout
android:layout_width="match_parent"
android:layout_height="match_parent" >

<android.support.design.widget.AppBarLayout
android:layout_width="match_parent"
android:layout_height="wrap_content" >

<android.support.v7.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="?attr/colorPrimary"
android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"
app:popupTheme="@style/ThemeOverlay.AppCompat.Light"

// AppBarLayout 接收到滚动事件的时候,内部的子控件可以指定如何去影响这些事件
// scroll 表示当 RecyclerView 向上滚动的时候,Toolbar 会跟着一起向上滚动并实现隐藏
// enterAlways 表示当 RecyclerView 向下滚动的时候,Toolbar 会跟着一起向下滚动并重新显示
// snap 表示当 Toolbar 还没有完全隐藏或显示的时候,会根据当前滚动的距离自动选择是隐藏还是显示
app:layout_scrollFlags="scroll|enterAlways|snap" />
</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"

// RecyclerView 滚动的时候将滚动事件通知给 AppBarLayout
// 如果 RecyclerView 外面嵌套的有父布局,比如 SwipeRefreshLayout,则 app:layout_behavior 声明的布局行为也要移到父布局中才行
app:layout_behavior="@string/appbar_scrolling_view_behavior" />

...

</android.support.design.widget.CoordinatorLayout>

...

</android.support.v4.widget.DrawerLayout>

11. 怎样理解 SwipeRefreshLayout

  • SwipeRefreshLayout 是用于实现下拉刷新功能的核心类,由 support-v4 提供

  • 在布局文件中,如果在 RecyclerView 的外面嵌套了一层 SwipeRefreshLayout,这样 RecyclerView 就自动拥有下拉刷新功能了

  • 需要注意的是,由于 RecyclerView 现在变成了 SwipeRefreshLayout 的子控件,因此之前使用 app:layout_behavior 声明的布局行为现在也要移到 SwipeRefreshLayout 中才行

  • Demo

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    public class MainActivity extends AppCompatActivity {
    private SwipeRefreshLayout mSwipeRefresh;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    mSwipeRefresh = (SwipeRefreshLayout) findViewById(R.id.swipe_refresh);
    mSwipeRefresh.setColorSchemeResources(R.color.colorPrimary); // 设置下拉刷新进度条颜色
    // 设置下拉刷新监听器
    mSwipeRefresh.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {
    @Override
    pubic void onRefresh() {
    refreshFruits();
    }
    });
    }

    private void refreshFruits() {
    new Thread(new Runnable() {
    @Override
    public void run() {
    try {
    Thread.sleep(2000);
    } catch(InterruptedException e) {
    e.printStackTrace();
    }

    // 将线程切回主线程
    runOnUiThread(new Runnable() {
    @Override
    public void run() {
    initFruits();
    adapter.notifyDataSetChanged(); // 通知数据发生了变化
    swipeRefresh.setRefreshing(false); // 刷新事件结束,并隐藏刷新进度条
    }
    });
    }
    }).start();
    }
    }

12. 怎样理解 CollapsingToolbarLayout

  • CollapsingToolbarLayout 是一个作用于 Toolbar 基础之上的布局,也是由 Design Support 库提供的,可以实现可折叠式标题栏的效果

  • CollapsingToolbarLayout 是不能独立存在的,它在设计的时候就被限定只能作为 AppBarLayout 的直接子布局来使用,而 AppBarLayout 又必须是 CoordinatorLayout 的子布局

  • Demo

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    <android.support.design.widget.CoordinatorLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto" //Material Design 开发时会用到 xmlns:app 命名空间
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:fitsSystemWindows="true" >

    // 标题栏部分
    <android.support.design.widget.AppBarLayout
    android:id="@+id/appBar"
    android:layout_width="match_parent"
    android:layout_height="250dp" // 高度指定为 250dp,视觉效果较好
    android:fitsSystemWindows="true" >

    // 调用 CollapsingToolbarLayout 的 setTitle() 方法设置页面标题
    <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" // 这里要实现更加高级的 Toolbar 效果,因此需要将这个主题的指定从 Toolbar 提到上一层来
    android:fitsSystemWindows="true"
    app:contentScrim="?attr/colorPrimary" // 指定 CollapsingToolbarLayout 在趋于折叠状态以及折叠之后的背景色

    // 同上,也从 Toolbar 中移到上一层
    // scroll 表示 CollapsingToolbarLayout 会随着内容详情的滚动一起滚动
    // exitUntilCollapsed 表示当 CollapsingToolbarLayout 随着滚动完成折叠之后就保留在页面上,不再移除屏幕
    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"
    android:fitsSystemWindows="true" // 表示会为系统状态栏留出空间

    // 用于指定当前控件在 CollapsingToolbarLayout 折叠过程中的折叠模式
    app:layout_collapseMode="parallax" /> // parallax 表示会在折叠的过程中产生一定的错位偏移,这种模式的视觉效果会很好

    <android.support.v7.widget.Toolbar
    android:id="@+id/toolbar"
    android:layout_width="match_parent"
    android:layout_height="?attr/actionBarSize"
    app:layout_collapseMode="pin" /> // pin 表示在折叠过程中位置始终保持不变
    </android.support.design.widget.CollapsingToolbarLayout>

    </android.support.design.widget.AppBarLayout>

    // 内容详情部分
    // NestedScrollView 在 ScrollView 的基础之上增加了嵌套响应滚动事件的功能
    // 由于 CoordinatorLayout 本身已经可以响应滚动事件了,因此在它的内部就需要使用 NestedScrollView 或 RecyclerView 这样的布局
    // 不管是 ScrollView 还是 NestedScrollView,它们的内部都只允许存在一个直接子布局,否则 AS 会有提示
    <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_heigh="match_parent"
    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" // 圆角
    app:elevation="5dp" > // 阴影

    <TextView
    android:id="@+id/fruit_content_text"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_margin="10dp" />
    </android.support.v7.widget.CardView>

    </LinearLayout>

    </android.support.v4.widget.NestedScrollView>

    <android.support.design.widget.FloatingActionButton
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_margin="16dp"
    android:src="@drawable/ic_comment"
    app:layout_anchor="@id/appBar" // 指定将 AppBarLayout 作为一个锚点,这样悬浮按钮就会出现在标题栏的区域内
    app:layout_anchorGravity="bottom|end" /> // 将悬浮按钮定位在标题栏区域的右下角

    </android.support.design.widget.CoordinatorLayout>

13. 怎样实现沉浸式状态栏

  • Android 5.0 系统之后支持沉浸式系统状态栏,有两种实现方式

  • 方式一(xml 文件实现

    • 第一步:必须将 ImageView 布局结构以及所有父布局都设置 fitsSystemWindows 属性为 true
    • 第二步:必须在应用的主题中将状态栏颜色指定成透明色。可以创建一个 values-21 目录,目录下创建一个 style.xml 文件,对属性 android:statusBarColor 的值指定成 @android:color/transparent 即可。因为 values-21 目录是只有 Android 5.0 即以上的系统才会去读取,所以这么声明式没有问题的
    • 第三步,因为 Android 5.0 之前的系统无法识别新建的主题,所以对原主题文件进行修改,把 <style> 标签内去掉 android:statusBarColor 属性的指定,然后对 Activity 应用这个主题就可以了
  • 方式二(代码实现

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    public class WeatherActivity extends AppCompatActivity {
    ...
    @Override
    protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    if(Build.VERSION.SDK_INT >= 21) {
    View decorView = getWindow().getDecorView();
    // 表示活动的布局会显示在状态栏上面
    decorView.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | View.SYSTEM_UI_FLAG_LAYOUT_STABLE);
    // 设置状态栏为透明色
    getWindow().setStatusBarColor(Color.TRANSPARENT);
    }
    setContentView(R.layout.activity_weather);
    ...
    }
    ...
    }
-------------------- 本文结束感谢您的阅读 --------------------