本文同步发表于我的微信公众号,微信搜索 程语新视界 即可关注,每个工作日都有文章更新

Android 开发中,add()show() 和 hide() 是管理 Fragment 显示与隐藏的一组方法,适用于需要频繁切换且保留状态的界面。

方法名 功能描述 对视图和生命周期的影响 典型使用场景
add() 将多个 Fragment 添加到同一容器,叠加视图 新添加的 Fragment 会经历完整的生命周期到onResume;添加后视图可见
后续添加的 Fragment 视图会覆盖在先添加的之上。
初始化添加 Fragment,配合 hide/show 实现切换
show() 显示之前通过 hide() 隐藏的 Fragment 视图 不会触发 Fragment 的生命周期方法,仅将视图设置为可见 需要再次显示已添加但被隐藏的 Fragment
hide() 隐藏已添加的 Fragment 视图,但 Fragment 实例依然由 FragmentManager 管理 不会触发 Fragment 的生命周期方法,仅将视图设置为不可见 需要暂时隐藏 Fragment 但保留其状态,以节省开销

基本使用

通常会组合使用这些方法。基本模式如下:

  1. 使用 add() 将多个 Fragment 添加到同一容器。

  2. 通过 hide() 隐藏当前不需要显示的 Fragment。

  3. 通过 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);
        }
    }
}

Logo

火山引擎开发者社区是火山引擎打造的AI技术生态平台,聚焦Agent与大模型开发,提供豆包系列模型(图像/视频/视觉)、智能分析与会话工具,并配套评测集、动手实验室及行业案例库。社区通过技术沙龙、挑战赛等活动促进开发者成长,新用户可领50万Tokens权益,助力构建智能应用。

更多推荐