Android项目全局通用ProgressBar实现方案求助(附自定义代码)
通用全局ProgressBar实现方案(Android Java)
我来给你分享两种非常实用的通用ProgressBar实现方案,解决你之前重复布局、显示隐藏控制麻烦的问题,附上完整的Java代码示例:
方案一:全局单例工具类(灵活无侵入)
这种方案不需要修改现有页面的继承关系,通过单例工具类动态添加加载框到窗口,所有Activity/Fragment都能直接调用。
第一步:完善自定义ProgressBar布局
先把你未完成的布局补全,命名为layout_progress_loading.xml:
<?xml version="1.0" encoding="utf-8"?> <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:background="#80000000" android:gravity="center"> <ProgressBar android:id="@+id/progress_bar" android:layout_width="wrap_content" android:layout_height="wrap_content" android:indeterminate="true" android:indeterminateTint="@color/white" /> <!-- 可选:添加加载提示文字 --> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginTop="16dp" android:text="加载中..." android:textColor="@color/white" android:textSize="14sp" /> </FrameLayout>
第二步:实现单例工具类
创建ProgressLoadingUtil.java,负责加载框的显示和隐藏:
import android.app.Activity; import android.content.Context; import android.view.LayoutInflater; import android.view.View; import android.view.WindowManager; public class ProgressLoadingUtil { private static ProgressLoadingUtil instance; private View loadingView; private WindowManager windowManager; private WindowManager.LayoutParams layoutParams; private ProgressLoadingUtil() { // 私有构造,保证单例 } public static ProgressLoadingUtil getInstance() { if (instance == null) { synchronized (ProgressLoadingUtil.class) { if (instance == null) { instance = new ProgressLoadingUtil(); } } } return instance; } // 显示加载框 public void showLoading(Context context) { // 校验Context有效性,避免内存泄漏 if (context == null || (context instanceof Activity && ((Activity) context).isFinishing())) { return; } // 避免重复添加 if (loadingView != null && loadingView.getParent() != null) { return; } windowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE); loadingView = LayoutInflater.from(context).inflate(R.layout.layout_progress_loading, null); // 适配Android版本的窗口参数 layoutParams = new WindowManager.LayoutParams(); layoutParams.width = WindowManager.LayoutParams.MATCH_PARENT; layoutParams.height = WindowManager.LayoutParams.MATCH_PARENT; if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) { layoutParams.type = WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY; } else { layoutParams.type = WindowManager.LayoutParams.TYPE_PHONE; } layoutParams.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL; layoutParams.format = android.graphics.PixelFormat.TRANSLUCENT; windowManager.addView(loadingView, layoutParams); } // 隐藏加载框 public void hideLoading() { if (windowManager != null && loadingView != null && loadingView.getParent() != null) { try { windowManager.removeView(loadingView); } catch (IllegalArgumentException e) { e.printStackTrace(); } finally { loadingView = null; windowManager = null; } } } }
第三步:在页面中使用
不管是Activity还是Fragment,直接调用即可:
// 显示加载框(Activity中) ProgressLoadingUtil.getInstance().showLoading(this); // 显示加载框(Fragment中) ProgressLoadingUtil.getInstance().showLoading(requireContext()); // 隐藏加载框(比如网络请求完成后) ProgressLoadingUtil.getInstance().hideLoading();
注意事项
- 记得在页面销毁时调用
hideLoading(),避免内存泄漏; - Android 8.0+如果使用
TYPE_APPLICATION_OVERLAY,需要在Manifest中添加权限:<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />,如果只是在自己应用内显示,也可以改用TYPE_APPLICATION(无需额外权限)。
方案二:BaseActivity/BaseFragment封装(统一管理生命周期)
如果你的项目已经有Base类,这种方案更省心,自动管理加载框的生命周期,不用手动清理。
第一步:实现BaseActivity
创建BaseActivity.java,封装加载框的显示和隐藏逻辑:
import android.os.Bundle; import androidx.appcompat.app.AppCompatActivity; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; public abstract class BaseActivity extends AppCompatActivity { private View loadingView; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(getLayoutId()); initView(); } // 子类实现:返回页面布局ID protected abstract int getLayoutId(); // 子类实现:初始化页面控件 protected abstract void initView(); // 显示加载框 protected void showLoading() { if (loadingView == null) { loadingView = LayoutInflater.from(this).inflate(R.layout.layout_progress_loading, null); // 获取页面根布局,添加加载框 ViewGroup rootView = findViewById(android.R.id.content); rootView.addView(loadingView); } loadingView.setVisibility(View.VISIBLE); } // 隐藏加载框 protected void hideLoading() { if (loadingView != null) { loadingView.setVisibility(View.GONE); } } @Override protected void onDestroy() { super.onDestroy(); // 页面销毁时移除加载框,避免内存泄漏 if (loadingView != null && loadingView.getParent() != null) { ((ViewGroup) loadingView.getParent()).removeView(loadingView); loadingView = null; } } }
第二步:实现BaseFragment
同理,创建BaseFragment.java:
import android.os.Bundle; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.fragment.app.Fragment; public abstract class BaseFragment extends Fragment { private View loadingView; private View rootView; @Nullable @Override public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { rootView = inflater.inflate(getLayoutId(), container, false); initView(rootView); return rootView; } protected abstract int getLayoutId(); protected abstract void initView(View rootView); protected void showLoading() { if (loadingView == null && getContext() != null) { loadingView = LayoutInflater.from(getContext()).inflate(R.layout.layout_progress_loading, null); if (rootView instanceof ViewGroup) { ((ViewGroup) rootView).addView(loadingView); } } if (loadingView != null) { loadingView.setVisibility(View.VISIBLE); } } protected void hideLoading() { if (loadingView != null) { loadingView.setVisibility(View.GONE); } } @Override public void onDestroyView() { super.onDestroyView(); if (loadingView != null && loadingView.getParent() != null) { ((ViewGroup) loadingView.getParent()).removeView(loadingView); loadingView = null; } rootView = null; } }
第三步:子类页面使用
Activity继承BaseActivity后直接调用:
public class MainActivity extends BaseActivity { @Override protected int getLayoutId() { return R.layout.activity_main; } @Override protected void initView() { // 显示加载框 showLoading(); // 模拟网络请求,3秒后隐藏 new android.os.Handler().postDelayed(() -> hideLoading(), 3000); } }
Fragment继承BaseFragment同理:
public class HomeFragment extends BaseFragment { @Override protected int getLayoutId() { return R.layout.fragment_home; } @Override protected void initView(View rootView) { showLoading(); // 业务逻辑... } }
方案对比与选择
- 单例工具类:适合没有统一Base类的项目,灵活无侵入,但需要手动管理加载框的生命周期,避免内存泄漏;
- Base类封装:适合有统一Base类的项目,自动管理生命周期,代码更简洁,减少重复调用。
你可以根据自己项目的架构选择合适的方案,两种都能完美实现所有页面通用ProgressBar的需求。
内容的提问来源于stack exchange,提问作者Pixxu Waku
相关产品推荐
相关产品推荐

