You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

多视图API调用场景下多ProgressBar的通用实现方案求助

嘿,这个问题我之前做项目时也碰到过——用两个ProgressBar切换可见性不仅冗余,还容易在并发请求时出现状态混乱的问题。这里有几个更优雅、通用的方案,完全能适配任意Fragment/Activity甚至自定义视图:

方案1:基于请求计数的单ProgressBar实现

核心思路是维护一个活跃请求计数器:每次发起API调用时计数器+1,请求完成(成功/失败)时计数器-1,根据计数器是否大于0来控制单个ProgressBar的显示/隐藏。这种方式能自动处理并发请求,只有当所有请求都完成时才隐藏加载状态。

示例代码(封装到Base类)

abstract class BaseActivity : AppCompatActivity() {
    private var activeRequests = 0
    private lateinit var globalProgressBar: ProgressBar

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        // 可以是布局中预先定义的全局ProgressBar,也可以动态添加到根视图
        globalProgressBar = findViewById(R.id.global_progress_bar)
    }

    // 对外暴露的请求开始方法
    protected fun markRequestStart() {
        activeRequests++
        updateProgressVisibility()
    }

    // 对外暴露的请求结束方法
    protected fun markRequestFinish() {
        activeRequests = max(0, activeRequests - 1)
        updateProgressVisibility()
    }

    private fun updateProgressVisibility() {
        globalProgressBar.visibility = if (activeRequests > 0) View.VISIBLE else View.GONE
    }
}

页面中调用

// 发起API请求时
markRequestStart()
apiService.fetchUserInfo()
    .enqueue(object : Callback<UserInfo> {
        override fun onResponse(call: Call<UserInfo>, response: Response<UserInfo>) {
            markRequestFinish()
            // 处理返回数据
        }

        override fun onFailure(call: Call<UserInfo>, t: Throwable) {
            markRequestFinish()
            // 处理错误
        }
    })
方案2:ViewModel + LiveData 统一管理加载状态(MVVM架构首选)

把加载状态的逻辑放到ViewModel中,通过LiveData将状态传递给视图层,完美实现视图与业务逻辑的解耦。同样用计数的方式管理多个请求的加载状态。

ViewModel示例

class MainViewModel : ViewModel() {
    private val _loadingCount = MutableLiveData(0)
    // 对外暴露的加载状态,自动映射计数是否大于0
    val isLoading: LiveData<Boolean> = Transformations.map(_loadingCount) { it > 0 }

    fun incrementLoading() {
        _loadingCount.value = _loadingCount.value?.plus(1) ?: 1
    }

    fun decrementLoading() {
        _loadingCount.value = max(0, _loadingCount.value?.minus(1) ?: 0)
    }
}

视图层观察状态

class MainFragment : Fragment() {
    private val viewModel: MainViewModel by viewModels()

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        val progressBar = view.findViewById<ProgressBar>(R.id.progress_bar)
        
        // 观察加载状态,自动更新ProgressBar可见性
        viewModel.isLoading.observe(viewLifecycleOwner) { isLoading ->
            progressBar.visibility = if (isLoading) View.VISIBLE else View.GONE
        }

        // 发起请求示例
        viewModel.incrementLoading()
        apiService.fetchData()
            .enqueue(object : Callback<Data> {
                override fun onResponse(call: Call<Data>, response: Response<Data>) {
                    viewModel.decrementLoading()
                    // 处理结果
                }

                override fun onFailure(call: Call<Data>, t: Throwable) {
                    viewModel.decrementLoading()
                    // 处理错误
                }
            })
    }
}
方案3:封装通用加载控件(适配任意视图)

自定义一个LoadingLayout,内部包含ProgressBar,对外提供startLoading()和stopLoading()方法,底层同样用计数逻辑控制显示。这个控件可以直接包裹任意视图(比如RecyclerView、LinearLayout等),无需在每个页面重复写计数代码。

自定义View示例

class LoadingLayout @JvmOverloads constructor(
    context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0
) : FrameLayout(context, attrs, defStyleAttr) {
    private var loadingCount = 0
    private val internalProgressBar: ProgressBar

    init {
        // 初始化内部ProgressBar,居中显示
        internalProgressBar = ProgressBar(context).apply {
            layoutParams = LayoutParams(
                LayoutParams.WRAP_CONTENT,
                LayoutParams.WRAP_CONTENT,
                Gravity.CENTER
            )
            visibility = View.GONE
            addView(this)
        }
    }

    fun startLoading() {
        loadingCount++
        updateProgressVisibility()
    }

    fun stopLoading() {
        loadingCount = max(0, loadingCount - 1)
        updateProgressVisibility()
    }

    private fun updateProgressVisibility() {
        internalProgressBar.visibility = if (loadingCount > 0) View.VISIBLE else View.GONE
    }
}

布局中使用

<com.yourpackage.LoadingLayout
    android:id="@+id/loading_container"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <!-- 这里放你的页面内容,比如RecyclerView、TextView等 -->
    <androidx.recyclerview.widget.RecyclerView
        android:id="@+id/recycler_view"
        android:layout_width="match_parent"
        android:layout_height="match_parent"/>

</com.yourpackage.LoadingLayout>

代码中调用

val loadingContainer = findViewById<LoadingLayout>(R.id.loading_container)

loadingContainer.startLoading()
apiService.fetchListData()
    .enqueue(object : Callback<List<Item>> {
        override fun onResponse(call: Call<List<Item>>, response: Response<List<Item>>) {
            loadingContainer.stopLoading()
            // 更新列表
        }

        override fun onFailure(call: Call<List<Item>>, t: Throwable) {
            loadingContainer.stopLoading()
            // 显示错误提示
        }
    })
总结推荐
  • 如果你的项目是MVVM架构,优先选方案2,完全符合架构设计原则,状态管理更清晰;
  • 如果是传统MVC/MVP架构,方案1或方案3都很合适,方案1适合统一管理全局加载,方案3更灵活适配局部视图;
  • 所有方案都比双ProgressBar切换的方式更简洁,且能完美处理多API并发的场景。

内容的提问来源于stack exchange,提问作者The90sArtist

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.12 03:51:47