ContentLoadingProgressBar 是 Android 中的一个控件,继承自 ProgressBar。它在 ProgressBar 的基础上添加了一些特殊功能,主要用于在加载内容时表现进度。它的一些主要特点如下:
- 主动隐蔽和表现:ContentLoadingProgressBar 会在内容加载完成后主动隐蔽,并在内容开始加载时主动表现。这减少了手动控制进度条表现和隐蔽的代码量。
- 延迟表现:为了克制在短时间内频仍表现和隐蔽进度条,ContentLoadingProgressBar 提供了一个延迟表现的功能。如果内容加载时间非常短,进度条大概不会表现出来。
- 延迟隐蔽:雷同地,ContentLoadingProgressBar 也提供了延迟隐蔽的功能,以确保进度条在内容加载完成后不会立即消散,从而提供更好的用户体验。
这些功能使 ContentLoadingProgressBar 成为一个更智能、更易用的进度条控件,特殊得当在必要频仍加载内容的应用中使用。
1、ContentLoadingProgressBar 的特性
从注释中可以看出,ContentLoadingProgressBar 在 ProgressBar 的基础上添加了以下特性:
- 在表现之前会等待一段时间来被隐蔽:这意味着在表现之前,ContentLoadingProgressBar 会等待一段时间,如果在这段时间内被隐蔽,那么就不会表现出来。
- 一旦表现,ContentLoadingProgressBar 会在一段时间内保持可见:这确保了进度条不会在短时间内频仍表现和隐蔽,克制了 UI 视图的“闪耀”现象。
这种“闪耀”现象在项目开辟中很常见,比方在举行网络请求之前表现 Loading 对话框,请求完成之后再隐蔽。如果网络请求耗时很短,就会导致对话框在短时间内表现和隐蔽,造成“闪耀”现象。ContentLoadingProgressBar 的这两个特性很好地解决了这个题目。
2、ContentLoadingProgressBar 的实现
ContentLoadingProgressBar 中定义了两个 int 范例的常量 MIN_SHOW_TIME 和 MIN_DELAY,分别表示表现的最短时间和延迟表现的时间,值都是 500ms。mDelayedShow 和 mDelayedHide 是两个 Runnable 使命,分别对应延时表现和延时隐蔽。在控制 ContentLoadingProgressBar 的表现和隐蔽时不能使用 setVisibility() 方法,而是必要使用 show() 和 hide() 方法。
show() 方法
- public void show() {
- mStartTime = -1;
- mPostedHide = false;
- mPostedShow = true;
- removeCallbacks(mDelayedHide);
- if (!mPostedShow) {
- postDelayed(mDelayedShow, MIN_DELAY);
- }
- }
复制代码 show() 方法起首会做一些状态的恢复处理,将 mStartTime 恢复为 -1,mStartTime 纪录了 ContentLoadingProgressBar 开始表现的时间,接着将延时隐蔽使命 mDelayedHide 从使命队列中移除。方法末了会判断 mPostedShow 的值,如果为 false 就调用 postDelayed() 方法延迟 MIN_DELAY(500ms)后执行 mDelayedShow 使命。mPostedShow 用于标记 mDelayedShow 是否已添加到使命队列中,防止使命的重复执行。mDelayedShow 使命的逻辑很简朴,主要就是纪录开始表现的时间并执行 setVisibility(View.VISIBLE) 将 ContentLoadingProgressBar 表现出来。
hide() 方法
- public void hide() {
- mPostedHide = true;
- removeCallbacks(mDelayedShow);
- long diff = System.currentTimeMillis() - mStartTime;
- if (diff >= MIN_SHOW_TIME || mStartTime == -1) {
- setVisibility(View.GONE);
- } else {
- postDelayed(mDelayedHide, MIN_SHOW_TIME - diff);
- }
- }
复制代码 hide() 方法和 show() 方法雷同,起首将延时表现使命 mDelayedShow 从使命队列中移除,因此如果调用 show() 和 hide() 方法之间的间隔时间小于 MIN_DELAY(500ms),mDelayedShow 就不会执行了,ContentLoadingProgressBar 也就不会表现了。接下来会盘算 System.currentTimeMillis() - mStartTime 的值,即此时 ContentLoadingProgressBar 的表现时间,如果此时 mStartTime 的值为 -1(ContentLoadingProgressBar 还没有表现)或者表现时间超过了 MIN_SHOW_TIME(500ms),直接执行 setVisibility(View.GONE) 隐蔽 ContentLoadingProgressBar;反之则阐明 ContentLoadingProgressBar 的表现时间没有到达最短时间 500ms,盘算剩余的时间,延时执行隐蔽使命,保证 ContentLoadingProgressBar 最短可以表现 500ms。这里的 mPostedHide 作用同样是防止延时隐蔽使命的重复执行。mDelayedHide 使命的逻辑也比较简朴,将 mStartTime 恢复为 -1,执行 setVisibility(View.GONE) 隐蔽 ContentLoadingProgressBar。
3、自定义Loading 对话框
ContentLoadingProgressBar 给了我们很好的思路,解决 Loading 对话框“闪耀”题目必要做到以下两点:
- 表现 Loading 对话框之前先等待一段时间。
- 隐蔽 Loading 对话框时判断表现时间是否到达了最短表现时间,如果没有到达就延时执行隐蔽使命。
清楚思路后就可以优化 Loading 对话框了,直接附上完备代码:
- package com.jpc.customwidgetstudy.widget
- import android.app.Activity
- import android.app.AlertDialog
- import android.content.Context
- import android.os.Handler
- import android.os.Looper
- import android.view.LayoutInflater
- import android.view.ViewGroup
- import android.widget.TextView
- import com.jpc.customwidgetstudy.R
- /**
- * 自定义Loading Dialog, 用于显示加载中的状态
- */
- class LoadingDialog(context: Context): AlertDialog(context, R.style.Theme_AppCompat_Dialog){
- companion object{
- // 最短显示时间
- private const val MIN_SHOW_TIME = 500L
- // 最短延迟时间
- private const val MIN_DELAY_TIME = 500L
- }
- private var tvMessage: TextView
- init {
- val parent = (context as? Activity)?.findViewById<ViewGroup>(android.R.id.content)
- val loadView = LayoutInflater.from(context).inflate(R.layout.dialog_loading, parent, false)
- setView(loadView)
- tvMessage = loadView.findViewById(R.id.tv_message)
- }
- // 记录开始时间
- private var mStartTime: Long = -1
- // 防止延时隐藏任务的重复执行
- private var mPostedHide: Boolean = false
- // 防止延时显示任务的重复执行
- private var mPostedShow: Boolean = false
- // 是否已经消失
- private var mDismissed: Boolean = false
- // 主线程Handler
- private val mHandler = Handler(Looper.getMainLooper())
- // 显示
- private val mDelayedShow: Runnable = Runnable {
- mPostedShow = false
- if (!mDismissed){
- mStartTime = System.currentTimeMillis()
- show()
- }
- }
- // 隐藏
- private val mDelayedHide: Runnable = Runnable {
- mPostedHide = false
- mStartTime = -1
- dismiss()
- }
- // 显示Dialog
- fun showDialog(message: String){
- tvMessage.text = message
- mStartTime = -1
- mDismissed = false
- mHandler.removeCallbacks(mDelayedHide)
- mPostedHide = false
- if (!mPostedShow){
- mHandler.postDelayed(mDelayedShow, MIN_DELAY_TIME)
- mPostedShow = true
- }
- }
- // 隐藏Dialog
- fun hideDialog(){
- mDismissed = true
- mHandler.removeCallbacks(mDelayedShow)
- mPostedShow = false
- val diff = System.currentTimeMillis() - mStartTime
- if (diff >= MIN_SHOW_TIME || mStartTime == -1L){
- dismiss()
- }else{
- if (!mPostedHide){
- mHandler.postDelayed(mDelayedHide, MIN_SHOW_TIME - diff)
- mPostedHide = true
- }
- }
- }
- // 从Window移除时移除所有的Callback
- override fun onDetachedFromWindow() {
- super.onDetachedFromWindow()
- mHandler.removeCallbacks(mDelayedHide)
- mHandler.removeCallbacks(mDelayedShow)
- }
- }
复制代码 可以定义Dialog的巨细
- <style name="Theme.AppCompat.Dialog" parent="Theme.AppCompat.Light.Dialog">
- <!-- Customize your dialog theme here -->
- <item name="android:windowBackground">@color/loading_color</item>
- <item name="android:windowMinWidthMajor">30%</item>
- <item name="android:windowMinWidthMinor">30%</item>
- <item name="android:padding">6dp</item>
- </style>
- <!-- Custom ProgressBar style -->
- <style name="CustomProgressBar" parent="Widget.AppCompat.ProgressBar">
- <item name="android:indeterminateTint">@color/colorPrimary</item>
- </style>
复制代码- <?xml version="1.0" encoding="utf-8"?>
- <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- xmlns:app="http://schemas.android.com/apk/res-auto">
- <ProgressBar
- android:id="@+id/progressBar"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- app:layout_constraintBottom_toTopOf="@id/tv_message"
- app:layout_constraintStart_toStartOf="@id/tv_message"
- app:layout_constraintEnd_toEndOf="@id/tv_message"
- style="@style/CustomProgressBar"/>
- <TextView
- android:id="@+id/tv_message"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="加载中..."
- app:layout_constraintTop_toTopOf="parent"
- app:layout_constraintBottom_toBottomOf="parent"
- app:layout_constraintStart_toStartOf="parent"
- app:layout_constraintEnd_toEndOf="parent" />
- </androidx.constraintlayout.widget.ConstraintLayout>
复制代码 结构文件就是一个 ProgressBar 和一个 TextView,用于展示提示信息。控制 Loading 对话框的表现和隐蔽直接使用 showDialog() 和 hideDialog() 方法就可以了。为了简朴示例,这里自定义的 Dialog 直接继承自 AlertDialog,留意要在适当的时机移除延时使命,防止内存走漏。
效果如下:
总结
本文通太过析 ContentLoadingProgressBar 的原理引出了项目开辟中 Loading 对话框的一种优化方式,克制对话框表现和隐蔽间隔时间太短导致的“闪耀”现象。
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。 |