《Android 应用开发基础教程》——第十二章:Material Design 组件实战(Toolbar、BottomNavigation、Snackbar 等)

目录

第十二章:Material Design 组件实战(Toolbar、BottomNavigation、Snackbar 等)

🔹 12.1 准备工作:引入 Material 库

🔸 12.2 Toolbar:自定义 App 顶部标题栏

✦ 1. XML 中添加 Toolbar:

✦ 2. Java 中设置 Toolbar 为 ActionBar:

✦ 3. 设置返回按钮 & 点击事件:

🔸 12.3 BottomNavigationView:底部导航栏

✦ 1. 布局 XML:

✦ 2. 菜单资源文件(res/menu/bottom_nav_menu.xml):

✦ 3. 设置点击事件监听:

🔸 12.4 Snackbar:临时提示信息(替代 Toast)

✦ 带按钮的 Snackbar:

🔸 12.5 FloatingActionButton:悬浮操作按钮

✦ 1. XML 中添加:

✦ 2. Java 中监听点击事件:

🔸 12.6 MaterialButton 与 MaterialCardView 简介

✅ 实战建议

习题答案

项目结构

1. MainActivity.java

2. CustomAdapter.java

3. HomeFragment.java

4. CustomCardAdapter.java

5. activity_main.xml

6. fragment_home.xml

7. item_card.xml

总结


第十二章:Material Design 组件实战(Toolbar、BottomNavigation、Snackbar 等)


Google 推出的 Material Design 是现代 Android 应用 UI 设计的标准,旨在提供统一、直观且符合用户期望的界面交互体验。本章我们将系统讲解常用的 Material 组件,包括 ToolbarBottomNavigationViewSnackbarFloatingActionButton 等,帮助你打造更专业的 App 界面。


🔹 12.1 准备工作:引入 Material 库

build.gradle(:app) 中添加依赖:

implementation 'com.google.android.material:material:1.12.0'


🔸 12.2 Toolbar:自定义 App 顶部标题栏

✦ 1. XML 中添加 Toolbar:

<com.google.android.material.appbar.MaterialToolbar
    android:id="@+id/toolbar"
    android:layout_width="match_parent"
    android:layout_height="?attr/actionBarSize"
    android:background="?attr/colorPrimary"
    android:title="我的应用"
    android:titleTextColor="@android:color/white"/>

✦ 2. Java 中设置 Toolbar 为 ActionBar:

Toolbar toolbar = findViewById(R.id.toolbar);
setSupportActionBar(toolbar);

✦ 3. 设置返回按钮 & 点击事件:

getSupportActionBar().setDisplayHomeAsUpEnabled(true);
toolbar.setNavigationOnClickListener(v -> finish());


🔸 12.3 BottomNavigationView:底部导航栏

适合用于 3~5 个主功能导航,搭配 ViewPager2Fragment 使用效果更佳。

✦ 1. 布局 XML:

<com.google.android.material.bottomnavigation.BottomNavigationView
    android:id="@+id/bottom_nav"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layout_gravity="bottom"
    app:menu="@menu/bottom_nav_menu"/>

✦ 2. 菜单资源文件(res/menu/bottom_nav_menu.xml):

<menu xmlns:android="http://schemas.android.com/apk/res/android">
    <item
        android:id="@+id/nav_home"
        android:icon="@drawable/ic_home"
        android:title="首页" />
    <item
        android:id="@+id/nav_dashboard"
        android:icon="@drawable/ic_dashboard"
        android:title="发现" />
    <item
        android:id="@+id/nav_profile"
        android:icon="@drawable/ic_profile"
        android:title="我的" />
</menu>

✦ 3. 设置点击事件监听:

BottomNavigationView bottomNav = findViewById(R.id.bottom_nav);
bottomNav.setOnItemSelectedListener(item -> {
    switch (item.getItemId()) {
        case R.id.nav_home:
            // 切换 Fragment
            return true;
        case R.id.nav_dashboard:
            return true;
        case R.id.nav_profile:
            return true;
    }
    return false;
});


🔸 12.4 Snackbar:临时提示信息(替代 Toast)

Snackbar 是可交互的提示信息,常用于用户操作反馈。

Snackbar.make(view, "操作成功", Snackbar.LENGTH_SHORT).show();

✦ 带按钮的 Snackbar:

Snackbar.make(view, "删除了1项", Snackbar.LENGTH_LONG)
        .setAction("撤销", v -> {
            // 执行撤销操作
        })
        .show();

🔸 12.5 FloatingActionButton:悬浮操作按钮

通常用于“新增”、“发布”等主要操作。

✦ 1. XML 中添加:

<com.google.android.material.floatingactionbutton.FloatingActionButton
    android:id="@+id/fab"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_gravity="end|bottom"
    android:layout_margin="16dp"
    android:src="@drawable/ic_add"
    app:backgroundTint="@color/teal_700"/>

✦ 2. Java 中监听点击事件:

FloatingActionButton fab = findViewById(R.id.fab);
fab.setOnClickListener(v -> {
    Snackbar.make(v, "新增功能点击", Snackbar.LENGTH_SHORT).show();
});


🔸 12.6 MaterialButton 与 MaterialCardView 简介

Material 设计风格的按钮与卡片布局:

<com.google.android.material.button.MaterialButton
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="确认" />

<com.google.android.material.card.MaterialCardView
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layout_margin="8dp"
    app:cardCornerRadius="16dp"
    app:cardElevation="4dp">
    
    <!-- 内嵌内容 -->
</com.google.android.material.card.MaterialCardView>


✅ 实战建议

  1. 使用 Toolbar + BottomNavigationView 实现页面导航

  2. 使用 FAB + Snackbar 实现新增与反馈机制

  3. 自定义 MaterialCardView 风格的列表项或卡片式 UI

  4. 搭配 ViewPager2 实现多页面切换体验


📢 下一章预告:

第十三章:权限管理机制与运行时权限请求(以拍照/存储为例)


习题答案

项目结构

MainActivity.java
HomeFragment.java
DashboardFragment.java
NotificationsFragment.java
CustomAdapter.java
activity_main.xml
fragment_home.xml
fragment_dashboard.xml
fragment_notifications.xml
item_card.xml

1. MainActivity.java

package com.example.demo;

import android.os.Bundle;
import android.view.View;
import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import androidx.fragment.app.Fragment;
import androidx.viewpager2.widget.ViewPager2;
import com.google.android.material.bottomnavigation.BottomNavigationView;
import com.google.android.material.floatingactionbutton.FloatingActionButton;
import com.google.android.material.snackbar.Snackbar;

public class MainActivity extends AppCompatActivity {

    private ViewPager2 viewPager;
    private BottomNavigationView bottomNavigationView;
    private FloatingActionButton fab;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        // 初始化 Toolbar
        setSupportActionBar(findViewById(R.id.toolbar));

        // 初始化 ViewPager2 和 Fragment 切换
        viewPager = findViewById(R.id.viewPager);
        CustomAdapter adapter = new CustomAdapter(this);
        viewPager.setAdapter(adapter);

        // 初始化 BottomNavigationView
        bottomNavigationView = findViewById(R.id.bottomNavigationView);
        bottomNavigationView.setOnNavigationItemSelectedListener(item -> {
            switch (item.getItemId()) {
                case R.id.menu_home:
                    viewPager.setCurrentItem(0);
                    return true;
                case R.id.menu_dashboard:
                    viewPager.setCurrentItem(1);
                    return true;
                case R.id.menu_notifications:
                    viewPager.setCurrentItem(2);
                    return true;
            }
            return false;
        });

        // 监听 ViewPager2 页面切换
        viewPager.registerOnPageChangeCallback(new ViewPager2.OnPageChangeCallback() {
            @Override
            public void onPageSelected(int position) {
                super.onPageSelected(position);
                switch (position) {
                    case 0:
                        bottomNavigationView.setSelectedItemId(R.id.menu_home);
                        break;
                    case 1:
                        bottomNavigationView.setSelectedItemId(R.id.menu_dashboard);
                        break;
                    case 2:
                        bottomNavigationView.setSelectedItemId(R.id.menu_notifications);
                        break;
                }
            }
        });

        // 初始化 FAB
        fab = findViewById(R.id.fab);
        fab.setOnClickListener(v -> {
            // 显示 Snackbar 提供反馈
            Snackbar.make(v, "新增成功!", Snackbar.LENGTH_SHORT)
                    .setAction("撤销", view -> {
                        Toast.makeText(MainActivity.this, "已撤销", Toast.LENGTH_SHORT).show();
                    })
                    .show();
        });
    }
}

2. CustomAdapter.java

package com.example.demo;

import androidx.annotation.NonNull;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentActivity;
import androidx.viewpager2.adapter.FragmentStateAdapter;

public class CustomAdapter extends FragmentStateAdapter {

    public CustomAdapter(@NonNull FragmentActivity fragmentActivity) {
        super(fragmentActivity);
    }

    @NonNull
    @Override
    public Fragment createFragment(int position) {
        switch (position) {
            case 0:
                return new HomeFragment();
            case 1:
                return new DashboardFragment();
            case 2:
                return new NotificationsFragment();
            default:
                return new HomeFragment();
        }
    }

    @Override
    public int getItemCount() {
        return 3; // 三个页面
    }
}

3. HomeFragment.java

package com.example.demo;

import android.os.Bundle;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import java.util.ArrayList;
import java.util.List;

public class HomeFragment extends Fragment {

    private RecyclerView recyclerView;
    private List<String> itemList;

    @Nullable
    @Override
    public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        View view = inflater.inflate(R.layout.fragment_home, container, false);

        recyclerView = view.findViewById(R.id.recyclerView);
        recyclerView.setLayoutManager(new LinearLayoutManager(getContext()));

        // 模拟数据
        itemList = new ArrayList<>();
        for (int i = 1; i <= 10; i++) {
            itemList.add("卡片 " + i);
        }

        CustomCardAdapter adapter = new CustomCardAdapter(itemList);
        recyclerView.setAdapter(adapter);

        return view;
    }
}

4. CustomCardAdapter.java

package com.example.demo;

import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
import java.util.List;

public class CustomCardAdapter extends RecyclerView.Adapter<CustomCardAdapter.ViewHolder> {

    private List<String> itemList;

    public CustomCardAdapter(List<String> itemList) {
        this.itemList = itemList;
    }

    @NonNull
    @Override
    public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        View view = LayoutInflater.from(parent.getContext())
                .inflate(R.layout.item_card, parent, false);
        return new ViewHolder(view);
    }

    @Override
    public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
        holder.textView.setText(itemList.get(position));
    }

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

    public static class ViewHolder extends RecyclerView.ViewHolder {
        TextView textView;

        public ViewHolder(@NonNull View itemView) {
            super(itemView);
            textView = itemView.findViewById(R.id.textView);
        }
    }
}

5. activity_main.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">

    <!-- Toolbar -->
    <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="?attr/actionBarSize"
            android:background="?attr/colorPrimary"
            app:title="我的应用" />
    </com.google.android.material.appbar.AppBarLayout>

    <!-- ViewPager2 -->
    <androidx.viewpager2.widget.ViewPager2
        android:id="@+id/viewPager"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        app:layout_behavior="@string/appbar_scrolling_view_behavior" />

    <!-- BottomNavigationView -->
    <com.google.android.material.bottomnavigation.BottomNavigationView
        android:id="@+id/bottomNavigationView"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_gravity="bottom"
        app:menu="@menu/bottom_nav_menu" />

    <!-- FAB -->
    <com.google.android.material.floatingactionbutton.FloatingActionButton
        android:id="@+id/fab"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="end|bottom"
        android:layout_margin="16dp"
        android:src="@drawable/ic_add"
        app:backgroundTint="@color/teal_200" />
</androidx.coordinatorlayout.widget.CoordinatorLayout>

6. fragment_home.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <androidx.recyclerview.widget.RecyclerView
        android:id="@+id/recyclerView"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:padding="8dp" />
</LinearLayout>

7. item_card.xml

<?xml version="1.0" encoding="utf-8"?>
<com.google.android.material.card.MaterialCardView 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="8dp"
    app:cardCornerRadius="16dp"
    app:cardElevation="4dp"
    app:strokeColor="@color/black"
    app:strokeWidth="1dp">

    <TextView
        android:id="@+id/textView"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:padding="16dp"
        android:text="卡片内容"
        android:textSize="16sp"
        android:gravity="center" />
</com.google.android.material.card.MaterialCardView>

总结

  1. 页面导航:通过 Toolbar 和 BottomNavigationView 实现。
  2. 新增与反馈:通过 FAB 和 Snackbar 实现。
  3. 自定义卡片:使用 MaterialCardView 定义风格化的卡片。
  4. 多页面切换:通过 ViewPager2 实现流畅的页面切换。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

WenJGo

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值