安卓 Fragment方法add show hide
Android中add()、show()和hide()方法用于高效管理Fragment的显示与隐藏,适用于需要频繁切换且保留状态的界面。add()方法添加Fragment并完整执行生命周期,show()和hide()仅控制视图可见性而不影响生命周期。相比replace()会销毁Fragment,hide/show组合能保留状态但占用更多内存,适合底部Tab切换等场景。示例展示了三个Fragment
本文同步发表于我的微信公众号,微信搜索 程语新视界 即可关注,每个工作日都有文章更新
Android 开发中,add()、show() 和 hide() 是管理 Fragment 显示与隐藏的一组方法,适用于需要频繁切换且保留状态的界面。
| 方法名 | 功能描述 | 对视图和生命周期的影响 | 典型使用场景 |
|---|---|---|---|
add() |
将多个 Fragment 添加到同一容器,叠加视图 | 新添加的 Fragment 会经历完整的生命周期到onResume;添加后视图可见。 后续添加的 Fragment 视图会覆盖在先添加的之上。 |
初始化添加 Fragment,配合 hide/show 实现切换 |
show() |
显示之前通过 hide() 隐藏的 Fragment 视图 |
不会触发 Fragment 的生命周期方法,仅将视图设置为可见。 | 需要再次显示已添加但被隐藏的 Fragment |
hide() |
隐藏已添加的 Fragment 视图,但 Fragment 实例依然由 FragmentManager 管理 | 不会触发 Fragment 的生命周期方法,仅将视图设置为不可见。 | 需要暂时隐藏 Fragment 但保留其状态,以节省开销 |
基本使用
通常会组合使用这些方法。基本模式如下:
-
使用
add()将多个 Fragment 添加到同一容器。 -
通过
hide()隐藏当前不需要显示的 Fragment。 -
通过
show()显示需要显示的 Fragment。
基本使用:
// 假设有两个 Fragment:FragmentA 和 FragmentB
FragmentManager fragmentManager = getSupportFragmentManager();
FragmentTransaction transaction = fragmentManager.beginTransaction();
// 首先添加 FragmentA
transaction.add(R.id.fragment_container, fragmentA, "FragmentA");
// 添加 FragmentB 并隐藏
transaction.add(R.id.fragment_container, fragmentB, "FragmentB");
transaction.hide(fragmentB);
// 提交事务
transaction.commit();
当需要从 FragmentA 切换到 FragmentB 时:
FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
// 隐藏当前的 FragmentA
transaction.hide(fragmentA);
// 显示之前添加的 FragmentB
transaction.show(fragmentB);
// 可选:将事务添加到回退栈,以便按返回键能回到上一个状态
transaction.addToBackStack(null);
transaction.commit();
提示:
-
在切换 Fragment 时,可以先判断目标 Fragment 是否已经添加 (
isAdded()),再决定使用show()还是先add()再show()。 -
使用
hide()和show()时,Fragment 本身的生命周期不会变化,但其视图的可见性会改变。 -
考虑使用
addToBackStack(null)将包含hide()和show()操作的事务加入回退栈,这样按返回键时可以回到上一个显示状态。
与 replace 的对比
replace() 方法的行为与 add()-hide()-show() 模式不同,区别如下:
| 操作方式 | 视图管理 | 生命周期影响 | 状态保留 | 性能特点 |
|---|---|---|---|---|
replace() |
移除容器内所有现有 Fragment,再添加新 Fragment | 旧 Fragment 实例通常会被销毁(视图被移除,并触发 onDestroyView 等),新 Fragment 会创建 | 不保留状态 | 频繁切换可能导致卡顿,但内存占用可能更低 |
hide/show |
隐藏/显示已添加的 Fragment 视图 | 不会因隐藏/显示而触发生命周期变化 | 保留状态 | 切换更流畅,但可能占用更多内存 |
简单来说:
-
使用
replace()每次切换都像是重新开始。 -
使用
hide()和show()切换则像是暂时离开,回来时一切如旧。
因此,如果某个 Fragment 需要频繁切换,且希望在再次显示时保留用户的操作状态(例如表单中输入的内容、列表的滚动位置等),那么使用 hide() 和 show() 是更好的选择。如果某个 Fragment 使用后不再需要,或者对资源敏感,则可以考虑使用 replace()。
实际应用与懒加载
add()-hide()-show() 的方法在一些常见场景下很有用:
-
主界面底部 Tab 切换:这是最典型的应用场景。例如,微信、支付宝等应用底部的导航栏切换不同页面时,就非常适合使用
hide()和show()来保持各个页面的状态,保证切换流畅。 -
需要保留复杂状态的页面:如果页面包含输入框、视频播放器或复杂动画,使用
hide()和show()可以避免切换时状态丢失。
关于懒加载:
需要注意的是,仅仅使用 hide() 和 show() 并不能直接实现完整的懒加载(即在 Fragment 真正对用户可见时才加载数据)。因为通过 add() 添加的 Fragment,即使一开始被 hide(),它的 onCreateView 和 onResume 也可能被执行。如果需要精确控制数据加载时机,可以结合 Fragment 的 onHiddenChanged() 回调方法,或者使用 ViewPager2 与 FragmentStateAdapter 并配合 setUserVisibleHint 方法(setUserVisibleHint 在某些情况下已被弃用)来优化。
完整示例
1. 布局文件 (activity_main.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">
<!-- 底部导航栏 -->
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:background="#f0f0f0">
<Button
android:id="@+id/btn_tab1"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="首页"
android:textColor="#333333" />
<Button
android:id="@+id/btn_tab2"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="消息"
android:textColor="#333333" />
<Button
android:id="@+id/btn_tab3"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="我的"
android:textColor="#333333" />
</LinearLayout>
<!-- Fragment 容器 -->
<FrameLayout
android:id="@+id/fragment_container"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1" />
</LinearLayout>
2. 三个 Fragment 的定义
FragmentA - 首页
public class FragmentA extends Fragment {
private EditText editText; // 用于测试状态保持
private int counter = 0; // 用于测试状态保持
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_a, container, false);
TextView textView = view.findViewById(R.id.text_view);
editText = view.findViewById(R.id.edit_text);
Button button = view.findViewById(R.id.button);
textView.setText("首页 Fragment - 计数器: " + counter);
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
counter++;
textView.setText("首页 Fragment - 计数器: " + counter);
Toast.makeText(getActivity(), "首页计数器: " + counter, Toast.LENGTH_SHORT).show();
}
});
return view;
}
@Override
public void onHiddenChanged(boolean hidden) {
super.onHiddenChanged(hidden);
if (!hidden) {
// Fragment 显示时的回调
Log.d("FragmentA", "FragmentA 现在可见");
} else {
Log.d("FragmentA", "FragmentA 现在隐藏");
}
}
}
fragment_a.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"
android:background="#FFB6C1"
android:padding="16dp">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="首页内容区域"
android:textSize="24sp"
android:textStyle="bold"
android:gravity="center"
android:layout_marginBottom="20dp" />
<TextView
android:id="@+id/text_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="首页 Fragment - 计数器: 0"
android:textSize="18sp"
android:layout_marginBottom="20dp" />
<EditText
android:id="@+id/edit_text"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="在此输入内容测试状态保持"
android:layout_marginBottom="20dp" />
<Button
android:id="@+id/button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="增加计数器" />
</LinearLayout>
FragmentB - 消息
public class FragmentB extends Fragment {
private ListView listView;
private ArrayAdapter<String> adapter;
private ArrayList<String> messageList = new ArrayList<>();
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_b, container, false);
listView = view.findViewById(R.id.list_view);
Button addButton = view.findViewById(R.id.add_button);
// 初始化一些测试数据
if (messageList.isEmpty()) {
for (int i = 1; i <= 5; i++) {
messageList.add("消息 " + i);
}
}
adapter = new ArrayAdapter<>(getActivity(),
android.R.layout.simple_list_item_1, messageList);
listView.setAdapter(adapter);
addButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
String newMessage = "新消息 " + (messageList.size() + 1);
messageList.add(newMessage);
adapter.notifyDataSetChanged();
Toast.makeText(getActivity(), "添加了: " + newMessage, Toast.LENGTH_SHORT).show();
}
});
return view;
}
@Override
public void onHiddenChanged(boolean hidden) {
super.onHiddenChanged(hidden);
if (!hidden) {
Log.d("FragmentB", "FragmentB 现在可见");
// 可以在这里执行数据刷新等操作
}
}
}
fragment_b.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"
android:background="#87CEEB"
android:padding="16dp">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="消息中心"
android:textSize="24sp"
android:textStyle="bold"
android:gravity="center"
android:layout_marginBottom="20dp" />
<ListView
android:id="@+id/list_view"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:layout_marginBottom="10dp" />
<Button
android:id="@+id/add_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="添加新消息" />
</LinearLayout>
FragmentC - 我的
public class FragmentC extends Fragment {
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_c, container, false);
TextView textView = view.findViewById(R.id.text_view);
textView.setText("个人中心页面\n\n这里可以显示用户信息、设置选项等");
return view;
}
@Override
public void onHiddenChanged(boolean hidden) {
super.onHiddenChanged(hidden);
if (!hidden) {
Log.d("FragmentC", "FragmentC 现在可见");
}
}
}
fragment_c.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"
android:background="#98FB98"
android:padding="16dp"
android:gravity="center">
<TextView
android:id="@+id/text_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="个人中心"
android:textSize="20sp"
android:textStyle="bold"
android:gravity="center"
android:lineSpacingMultiplier="1.2" />
</LinearLayout>
3. MainActivity 实现
public class MainActivity extends AppCompatActivity {
private FrameLayout fragmentContainer;
private Button btnTab1, btnTab2, btnTab3;
private FragmentA fragmentA;
private FragmentB fragmentB;
private FragmentC fragmentC;
private Fragment currentFragment; // 当前显示的Fragment
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initViews();
initFragments();
setupClickListeners();
// 默认显示第一个Fragment
switchFragment(fragmentA, "首页");
}
private void initViews() {
fragmentContainer = findViewById(R.id.fragment_container);
btnTab1 = findViewById(R.id.btn_tab1);
btnTab2 = findViewById(R.id.btn_tab2);
btnTab3 = findViewById(R.id.btn_tab3);
}
private void initFragments() {
FragmentManager fragmentManager = getSupportFragmentManager();
// 检查是否已经存在这些Fragment(比如屏幕旋转后)
fragmentA = (FragmentA) fragmentManager.findFragmentByTag("FragmentA");
fragmentB = (FragmentB) fragmentManager.findFragmentByTag("FragmentB");
fragmentC = (FragmentC) fragmentManager.findFragmentByTag("FragmentC");
// 如果不存在,创建新的实例
if (fragmentA == null) {
fragmentA = new FragmentA();
}
if (fragmentB == null) {
fragmentB = new FragmentB();
}
if (fragmentC == null) {
fragmentC = new FragmentC();
}
// 开始事务,添加所有Fragment但只显示一个
FragmentTransaction transaction = fragmentManager.beginTransaction();
if (!fragmentA.isAdded()) {
transaction.add(R.id.fragment_container, fragmentA, "FragmentA");
}
if (!fragmentB.isAdded()) {
transaction.add(R.id.fragment_container, fragmentB, "FragmentB");
transaction.hide(fragmentB); // 初始隐藏
}
if (!fragmentC.isAdded()) {
transaction.add(R.id.fragment_container, fragmentC, "FragmentC");
transaction.hide(fragmentC); // 初始隐藏
}
transaction.commit();
currentFragment = fragmentA; // 设置当前Fragment
}
private void setupClickListeners() {
btnTab1.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
switchFragment(fragmentA, "首页");
updateButtonColors(btnTab1);
}
});
btnTab2.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
switchFragment(fragmentB, "消息");
updateButtonColors(btnTab2);
}
});
btnTab3.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
switchFragment(fragmentC, "我的");
updateButtonColors(btnTab3);
}
});
}
private void switchFragment(Fragment targetFragment, String tag) {
if (targetFragment == null || targetFragment == currentFragment) {
return; // 避免重复切换
}
FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
// 隐藏当前Fragment
if (currentFragment != null) {
transaction.hide(currentFragment);
}
// 显示目标Fragment
if (!targetFragment.isAdded()) {
// 如果Fragment还没有添加,先添加再显示
transaction.add(R.id.fragment_container, targetFragment, tag);
} else {
// 如果已经添加,直接显示
transaction.show(targetFragment);
}
// 添加到回退栈,这样按返回键可以回到上一个Fragment
transaction.addToBackStack(null);
transaction.commit();
currentFragment = targetFragment;
Log.d("FragmentSwitch", "切换到: " + tag);
}
private void updateButtonColors(Button selectedButton) {
// 重置所有按钮颜色
btnTab1.setBackgroundColor(Color.parseColor("#f0f0f0"));
btnTab2.setBackgroundColor(Color.parseColor("#f0f0f0"));
btnTab3.setBackgroundColor(Color.parseColor("#f0f0f0"));
btnTab1.setTextColor(Color.parseColor("#333333"));
btnTab2.setTextColor(Color.parseColor("#333333"));
btnTab3.setTextColor(Color.parseColor("#333333"));
// 设置选中按钮的颜色
selectedButton.setBackgroundColor(Color.parseColor("#4CAF50"));
selectedButton.setTextColor(Color.WHITE);
}
@Override
public void onBackPressed() {
if (getSupportFragmentManager().getBackStackEntryCount() > 0) {
getSupportFragmentManager().popBackStack();
// 需要更新currentFragment和按钮状态
updateCurrentFragmentAfterBack();
} else {
super.onBackPressed();
}
}
private void updateCurrentFragmentAfterBack() {
// 这里需要根据实际情况更新currentFragment
// 简单实现:如果回退栈空了,就显示首页
if (getSupportFragmentManager().getBackStackEntryCount() == 0) {
currentFragment = fragmentA;
updateButtonColors(btnTab1);
}
}
}
火山引擎开发者社区是火山引擎打造的AI技术生态平台,聚焦Agent与大模型开发,提供豆包系列模型(图像/视频/视觉)、智能分析与会话工具,并配套评测集、动手实验室及行业案例库。社区通过技术沙龙、挑战赛等活动促进开发者成长,新用户可领50万Tokens权益,助力构建智能应用。
更多推荐
所有评论(0)