目录
✦ 2. Java 中设置 Toolbar 为 ActionBar:
🔸 12.4 Snackbar:临时提示信息(替代 Toast)
🔸 12.5 FloatingActionButton:悬浮操作按钮
🔸 12.6 MaterialButton 与 MaterialCardView 简介
第十二章:Material Design 组件实战(Toolbar、BottomNavigation、Snackbar 等)
Google 推出的 Material Design 是现代 Android 应用 UI 设计的标准,旨在提供统一、直观且符合用户期望的界面交互体验。本章我们将系统讲解常用的 Material 组件,包括 Toolbar
、BottomNavigationView
、Snackbar
、FloatingActionButton
等,帮助你打造更专业的 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 个主功能导航,搭配 ViewPager2
或 Fragment
使用效果更佳。
✦ 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>
✅ 实战建议
-
使用 Toolbar + BottomNavigationView 实现页面导航
-
使用 FAB + Snackbar 实现新增与反馈机制
-
自定义 MaterialCardView 风格的列表项或卡片式 UI
-
搭配
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>
总结
- 页面导航:通过
Toolbar
和BottomNavigationView
实现。 - 新增与反馈:通过
FAB
和Snackbar
实现。 - 自定义卡片:使用
MaterialCardView
定义风格化的卡片。 - 多页面切换:通过
ViewPager2
实现流畅的页面切换。