Android新手求助:如何在Fragment的onViewCreated中显示加载进度条?
解决Fragment加载TensorFlow模型时的白屏+显示进度条问题
嘿,作为过来人太懂这种加载模型时白屏的尴尬了!核心问题是TensorFlow模型加载是耗时操作,直接在主线程执行会阻塞UI导致白屏,咱们得把加载逻辑放到后台线程,同时在UI层显示进度提示。下面给你一步步实现的方案:
第一步:调整Fragment布局
先在你的Fragment布局里,把TextureView和进度条(比如Material Design的CircularProgressIndicator)放在同一个父容器(比如FrameLayout)里,默认让进度条显示、TextureView隐藏:
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent"> <!-- 你的TextureView,默认隐藏 --> <TextureView android:id="@+id/textureView" android:layout_width="match_parent" android:layout_height="match_parent" android:visibility="gone"/> <!-- 加载进度条,默认显示 --> <com.google.android.material.progressindicator.CircularProgressIndicator android:id="@+id/loadingProgress" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center" android:visibility="visible"/> </FrameLayout>
第二步:在onViewCreated里实现异步加载+进度控制
这里推荐用Kotlin的协程(Android官方推荐)来做异步操作,既简洁又安全。如果你用Java的话,后面也给你补充Executor的方案。
Kotlin 实现
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) val textureView = view.findViewById<TextureView>(R.id.textureView) val loadingProgress = view.findViewById<CircularProgressIndicator>(R.id.loadingProgress) // 用viewLifecycleOwner的协程,避免Fragment销毁后内存泄漏 viewLifecycleOwner.lifecycleScope.launch(Dispatchers.IO) { try { // 加载你的TensorFlow模型 val model = TensorFlowLiteModel.loadModelFile( requireContext().assets, "your_model.tflite" ) // 加载完成后切回主线程更新UI withContext(Dispatchers.Main) { loadingProgress.visibility = View.GONE textureView.visibility = View.VISIBLE // 这里初始化TextureView的相关逻辑,比如设置SurfaceTextureListener等 setupTextureView(textureView, model) } } catch (e: Exception) { // 加载失败时的处理,比如显示错误提示 withContext(Dispatchers.Main) { loadingProgress.visibility = View.GONE Toast.makeText(requireContext(), "模型加载失败: ${e.message}", Toast.LENGTH_SHORT).show() } } } } // 封装TextureView的初始化逻辑 private fun setupTextureView(textureView: TextureView, model: TensorFlowLiteModel) { textureView.surfaceTextureListener = object : TextureView.SurfaceTextureListener { override fun onSurfaceTextureAvailable(surface: SurfaceTexture, width: Int, height: Int) { // 这里处理TextureView可用后的逻辑,比如绑定相机、运行模型推理等 } override fun onSurfaceTextureSizeChanged(surface: SurfaceTexture, width: Int, height: Int) {} override fun onSurfaceTextureDestroyed(surface: SurfaceTexture): Boolean = true override fun onSurfaceTextureUpdated(surface: SurfaceTexture) {} } }
Java 实现
如果用Java的话,可以用ExecutorService来做后台任务,用Handler切回主线程:
@Override public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { super.onViewCreated(view, savedInstanceState); TextureView textureView = view.findViewById(R.id.textureView); CircularProgressIndicator loadingProgress = view.findViewById(R.id.loadingProgress); Handler mainHandler = new Handler(Looper.getMainLooper()); // 后台线程加载模型 Executors.newSingleThreadExecutor().execute(() -> { try { // 加载TensorFlow模型 TensorFlowLiteModel model = TensorFlowLiteModel.loadModelFile( requireContext().getAssets(), "your_model.tflite" ); // 主线程更新UI mainHandler.post(() -> { loadingProgress.setVisibility(View.GONE); textureView.setVisibility(View.VISIBLE); setupTextureView(textureView, model); }); } catch (Exception e) { mainHandler.post(() -> { loadingProgress.setVisibility(View.GONE); Toast.makeText(requireContext(), "模型加载失败: " + e.getMessage(), Toast.LENGTH_SHORT).show(); }); } }); } private void setupTextureView(TextureView textureView, TensorFlowLiteModel model) { textureView.setSurfaceTextureListener(new TextureView.SurfaceTextureListener() { @Override public void onSurfaceTextureAvailable(@NonNull SurfaceTexture surface, int width, int height) { // 处理TextureView可用后的逻辑 } @Override public void onSurfaceTextureSizeChanged(@NonNull SurfaceTexture surface, int width, int height) {} @Override public boolean onSurfaceTextureDestroyed(@NonNull SurfaceTexture surface) { return true; } @Override public void onSurfaceTextureUpdated(@NonNull SurfaceTexture surface) {} }); }
几个关键提醒
- 绝对不要在主线程加载模型:这是导致白屏的核心原因,耗时操作必须放到后台线程
- 进度条样式自定义:如果觉得默认的进度条不好看,可以换成自定义的加载动画(比如Lottie动画),只要保证布局层级正确即可
- 内存泄漏防护:用
viewLifecycleOwner的协程(Kotlin)或者在Fragment销毁时停止后台任务(Java),避免内存泄漏
内容的提问来源于stack exchange,提问作者Hong
相关产品推荐
相关产品推荐

