《Training:Creating a Fragment》

本文介绍如何使用Android支持库扩展Fragment类来创建可重用的UI组件,并与Activity进行交互。主要内容包括创建Fragment类、定义其生命周期方法、使用XML布局文件添加Fragment到Activity等。

注:本人非专业翻译人员,编写此博客只是为了自学使用,如有疑问,请参考官方文档



您可以将片段视为活动的模块化部分,它具有自己的生命周期,接收自己的输入事件,以及在活动运行时可以添加或删除的活动(类似于“子活动”,您可以 在不同的活动中重用)。 本课程展示了如何使用支持库扩展Fragment类,以便您的应用程序与运行系统版本低至Android 1.6的设备兼容。


在开始本课之前,您必须将Android项目设置为使用支持库。如果以前没有使用支持库,通过以下的 Support Library Setup document课程中的使用v4库来建立你的的项目。但是,您还可以通过使用与Android 2.1(API级别7)兼容的v7 appcompat库(此库还包含碎片API),将应用程序栏包含在活动中。


一、创建一个碎片 类

要创建碎片,请扩展Fragment类,然后覆盖密钥生命周期方法来插入应用程序逻辑,类似于使用Activity类的方式。


创建片段时的一个区别是您必须使用onCreateView()回调来定义布局。 事实上,这是为了使片段运行所需的唯一回调。 例如,这是一个简单的片段,它指定了自己的布局:

import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.ViewGroup;

public class ArticleFragment extends Fragment {
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
        Bundle savedInstanceState) {
        // Inflate the layout for this fragment
        return inflater.inflate(R.layout.article_view, container, false);
    }
}

就像一个活动一样,作为被从活动中删除或添加,以及活动在其生命周期状态之间转换,碎片应该实现其他的生命周期回调,允许你管理它的状态。例如,当活动的onPause()方法被调用时,活动中的任何片段都会接收到onPause()的调用。


有关碎片生命周期和回调方法的更多信息,请参阅“片段开发人员指南”。



二、使用XML将片段添加到活动

虽然碎片是可重用的,但模块化UI组件,Fragment类的每个实例必须与父FragmentActivity相关联。 您可以通过在活动布局XML文件中定义每个片段来实现此关联。


注意:FragmentActivity是支持库中提供的特殊活动,用于处理早于API级别11的系统版本上的碎片。如果您支持的最低系统版本为11级或更高版本,则可以使用常规活动。


这是一个示例布局文件,当设备屏幕被认为是“大”(由目录名中的大限定符指定)时,会向活动添加两个片段。

res/layout-large/news_articles.xml

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="horizontal"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent">

    <fragment android:name="com.example.android.fragments.HeadlinesFragment"
              android:id="@+id/headlines_fragment"
              android:layout_weight="1"
              android:layout_width="0dp"
              android:layout_height="match_parent" />

    <fragment android:name="com.example.android.fragments.ArticleFragment"
              android:id="@+id/article_fragment"
              android:layout_weight="2"
              android:layout_width="0dp"
              android:layout_height="match_parent" />

</LinearLayout>

提示:有关为不同屏幕尺寸创建布局的详细信息,请参阅支持不同的屏幕尺寸。


然后将布局应用于您的活动:

import android.os.Bundle;
import android.support.v4.app.FragmentActivity;

public class MainActivity extends FragmentActivity {
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.news_articles);
    }
}

如果您使用的是v7 appcompat库,那么您的活动应该是扩展AppCompatActivity,它是FragmentActivity的一个子类。 有关更多信息,请参阅添加应用程序栏)。


注意:通过在布局XML文件中定义片段将碎片添加到活动布局时,不能在运行时删除碎片。 如果您计划在用户交互期间交换您的碎片,则必须在活动首次启动时将碎片添加到活动中,如下一课所示。

<think>我们正在讨论Android中的DirectoryFragment创建新fragment时可能出现的异常问题。虽然用户提供了两个引用,但引用内容似乎与Android开发关系不大: - 引用[1]是关于Hive JDBC操作超时的Java异常,属于大数据处理领域。 - 引用[2]是关于Linux LVM(逻辑卷管理)的操作指南,属于系统管理领域。 因此,我们需要忽略这些引用,专注于AndroidFragment创建的相关问题。在Android开发中,`DirectoryFragment`并不是一个标准组件,可能是指开发者自定义的用于显示目录结构的Fragment。当创建新Fragment时出现异常,通常涉及Fragment生命周期管理、参数传递或事务处理等问题。 ### 核心问题分析 在Android中创建Fragment时常见的异常包括: 1. **`IllegalStateException`**:通常发生在Fragment事务提交后状态不一致时,比如在`onSaveInstanceState()`之后提交事务。 2. **`NullPointerException`**:可能由于Fragment参数未正确传递或视图未初始化导致。 3. **`Fragment.InstantiationException`**:当系统无法实例化Fragment类时抛出,可能因缺少无参构造函数引起。 ### 排查步骤 #### 1. **检查Fragment事务提交时机** - 避免在`onSaveInstanceState()`之后提交事务,否则会抛出`IllegalStateException`。解决方案: - 使用`commitAllowingStateLoss()`替代`commit()`(不推荐,可能丢失状态)。 - 确保在Activity生命周期安全期(如`onResume()`)内提交事务。 - 使用`FragmentManager.isStateSaved()`检查状态: ```java if (!getSupportFragmentManager().isStateSaved()) { transaction.commit(); } ``` #### 2. **验证Fragment参数传递** - 使用`setArguments(Bundle)`传递参数,而非通过构造函数。在Fragment中通过`getArguments()`获取参数。 - 示例: ```java // 创建Fragment实例并传递参数 DirectoryFragment fragment = new DirectoryFragment(); Bundle args = new Bundle(); args.putString("directory_path", "/sdcard/documents"); fragment.setArguments(args); // 在Fragment的onCreate()中读取 String path = getArguments().getString("directory_path"); ``` #### 3. **确保Fragment有无参构造函数** - Android系统在恢复Fragment时会调用默认构造函数。如果自定义Fragment未提供无参构造方法,将抛出`InstantiationException`。 - 解决方案: - 声明一个显式的无参构造函数(即使为空)。 - 避免在构造函数中添加参数逻辑(应使用`setArguments()`)。 #### 4. **处理视图初始化** - 在`onCreateView()`中正确初始化视图,避免在`onCreate()`中访问UI元素(此时视图未创建)。 - 使用`ViewBinding`或`findViewById()`时注意时机: ```java @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { binding = FragmentDirectoryBinding.inflate(inflater, container, false); return binding.getRoot(); } ``` #### 5. **排查资源加载问题** - 如果Fragment依赖外部资源(如目录列表),需检查资源可用性: - 文件路径权限:确保应用有`READ_EXTERNAL_STORAGE`权限(Android 10+需使用Scoped Storage)。 - 异步加载:耗时操作(如扫描目录)应在后台线程执行,避免阻塞主线程。 #### 6. **日志与异常捕获** - 添加详细日志,定位异常位置: ```java try { // 创建并提交Fragment事务 } catch (Exception e) { Log.e("DirectoryFragment", "Error creating fragment", e); } ``` - 使用Android Studio的调试器设置断点,观察Fragment创建流程。 ### 典型异常场景示例 假设日志中出现以下错误: ```java java.lang.IllegalStateException: Fragment already added ``` **原因**:同一Fragment实例被多次添加到Activity。 **解决**:每次创建新Fragment实例,而非复用旧实例: ```java // 错误做法:重复添加同一个实例 if (fragment == null) { fragment = new DirectoryFragment(); } transaction.add(R.id.container, fragment); // 正确做法:每次创建新实例 DirectoryFragment fragment = new DirectoryFragment(); transaction.add(R.id.container, fragment); ``` ### 总结 DirectoryFragment创建异常通常由生命周期管理不当、参数传递错误或系统限制引起。通过以下措施可有效预防: 1. 使用`setArguments()`传递参数。 2. 确保事务在安全生命周期内提交。 3. 提供无参构造函数。 4. 异步加载资源并检查权限。 5. 添加异常捕获和详细日志。 若问题仍存在,请提供具体的异常堆栈信息以便进一步分析[^1]。 §§相关问题§§ 1. 如何在Android中安全地传递数据给Fragment? 2. Fragment事务提交的最佳实践是什么? 3. 如何调试Android中的`IllegalStateException: Fragment already added`错误? 4. Android Scoped Storage对文件访问有哪些影响? 5. 为什么Fragment需要无参构造函数?</think>### Android DirectoryFragment 创建新 Fragment 异常排查指南 在 Android 开发中,`DirectoryFragment` 创建新 Fragment 时出现异常通常涉及生命周期管理、事务提交或参数传递问题。以下是系统化的排查步骤和解决方案: --- #### 1. **检查核心异常日志** 首先分析报错堆栈(如引用[1]中的超时错误): ```java java.sql.SQLException: Sender timed out waiting for receiver fragment instance... at org.apache.hive.jdbc.HiveStatement.waitForOperationToComplete(HiveStatement.java:381) ``` **可能原因**: - 主线程阻塞导致 ANR(应用无响应) - 跨进程通信超时(如使用 `SQLite` 或远程服务) - Fragment 事务未在 UI 线程执行 **解决方案**: ```java // 确保事务在主线程执行 requireActivity().runOnUiThread(() -> { FragmentTransaction transaction = getParentFragmentManager().beginTransaction(); transaction.replace(R.id.container, new TargetFragment()); transaction.commit(); }); ``` --- #### 2. **验证 Fragment 生命周期状态** 当创建新 Fragment 时,需确保宿主 Activity/Fragment 处于活跃状态: ```java // 在 DirectoryFragment 中检查 if (isAdded() && !isDetached()) { // 安全创建新 Fragment } else { Log.w("FragmentState", "Attempted to create fragment from invalid state"); } ``` **常见陷阱**: - 在 `onSaveInstanceState()` 后提交事务 → 使用 `commitAllowingStateLoss()` - 尝试在 `onDestroyView()` 后操作视图 → 导致 `NullPointerException` --- #### 3. **参数传递与序列化问题** 当通过 `setArguments()` 传递数据时: ```java // 创建新 Fragment 并传递参数 Bundle args = new Bundle(); args.putString("directory_path", "/sdcard/documents"); Fragment newFragment = new TargetFragment(); newFragment.setArguments(args); ``` **关键检查点**: 1. 参数是否包含非 `Parcelable` 对象?→ 实现 `Parcelable` 接口 2. 是否在 `onCreate()` 中读取参数?→ 避免在构造函数中访问 `getArguments()` --- #### 4. **资源冲突排查(参考引用[2])** 类似 LVM 的磁盘扩容问题(引用[2]),Android 资源管理需注意: ```markdown 1. **文件系统权限**: - 检查 `READ_EXTERNAL_STORAGE` 权限 - Android 11+ 使用 Scoped Storage 2. **存储空间监控**: ```java StatFs statFs = new StatFs(Environment.getExternalStorageDirectory().getPath()); long availableBytes = statFs.getAvailableBytes(); ``` 当可用空间 <15% 时(类似引用[2]中85%阈值),I/O 性能可能指数级下降 3. **异步加载策略**: ```kotlin lifecycleScope.launch(Dispatchers.IO) { val files = loadDirectoryContents() withContext(Dispatchers.Main) { adapter.submitList(files) } } ``` ``` --- #### 5. 事务管理最佳实践 | 问题类型 | 错误示例 | 正确方案 | |---------|---------|---------| | 重复添加 | `transaction.add()` 同一 Fragment 多次 | 使用 `replace()` 或检查 `fragment.isAdded` | | 状态丢失 | `commit()` 在 `onSaveInstanceState()` 后 | `commitAllowingStateLoss()` + 状态恢复逻辑 | | 嵌套事务 | 未使用 `childFragmentManager` | `getChildFragmentManager().beginTransaction()` | --- #### 6. 高级调试技巧 1. **启用 Fragment 日志**: ```bash adb shell setprop log.tag.FragmentManager VERBOSE ``` 2. **检测内存泄漏**: ```gradle debugImplementation 'com.squareup.leakcanary:leakcanary-android:2.9.1' ``` 3. **使用 Android Profiler** 监控: - 主线程阻塞情况 - 内存峰值是否异常 --- ### 典型解决方案 ```java public void createNewFragment(String path) { if (!isStateSaved()) { // 检查状态是否可保存 TargetFragment fragment = TargetFragment.newInstance(path); getParentFragmentManager().beginTransaction() .setReorderingAllowed(true) // 优化过渡动画 .replace(R.id.fragment_container, fragment) .addToBackStack("directory_stack") .commit(); } } // 静态工厂方法避免构造参数问题 public static TargetFragment newInstance(String path) { TargetFragment frag = new TargetFragment(); Bundle args = new Bundle(); args.putString("path", path); frag.setArguments(args); return frag; } ``` > **关键原则**:始终假设 Fragment 可能被系统销毁重建,通过 `onSaveInstanceState()` 保存关键状态[^1]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值