仿照ContentLoadingProgressBar 的特点在Android项目中自定义Loading对话框 ...

打印 上一主题 下一主题

主题 1045|帖子 1045|积分 3135

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() 方法
  1. public void show() {
  2.     mStartTime = -1;
  3.     mPostedHide = false;
  4.     mPostedShow = true;
  5.     removeCallbacks(mDelayedHide);
  6.     if (!mPostedShow) {
  7.         postDelayed(mDelayedShow, MIN_DELAY);
  8.     }
  9. }
复制代码
show() 方法起首会做一些状态的恢复处理,将 mStartTime 恢复为 -1,mStartTime 纪录了 ContentLoadingProgressBar 开始表现的时间,接着将延时隐蔽使命 mDelayedHide 从使命队列中移除。方法末了会判断 mPostedShow 的值,如果为 false 就调用 postDelayed() 方法延迟 MIN_DELAY(500ms)后执行 mDelayedShow 使命。mPostedShow 用于标记 mDelayedShow 是否已添加到使命队列中,防止使命的重复执行。mDelayedShow 使命的逻辑很简朴,主要就是纪录开始表现的时间并执行 setVisibility(View.VISIBLE) 将 ContentLoadingProgressBar 表现出来。
hide() 方法
  1. public void hide() {
  2.     mPostedHide = true;
  3.     removeCallbacks(mDelayedShow);
  4.     long diff = System.currentTimeMillis() - mStartTime;
  5.     if (diff >= MIN_SHOW_TIME || mStartTime == -1) {
  6.         setVisibility(View.GONE);
  7.     } else {
  8.         postDelayed(mDelayedHide, MIN_SHOW_TIME - diff);
  9.     }
  10. }
复制代码
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 对话框了,直接附上完备代码:
  1. package com.jpc.customwidgetstudy.widget
  2. import android.app.Activity
  3. import android.app.AlertDialog
  4. import android.content.Context
  5. import android.os.Handler
  6. import android.os.Looper
  7. import android.view.LayoutInflater
  8. import android.view.ViewGroup
  9. import android.widget.TextView
  10. import com.jpc.customwidgetstudy.R
  11. /**
  12. * 自定义Loading Dialog, 用于显示加载中的状态
  13. */
  14. class LoadingDialog(context: Context): AlertDialog(context, R.style.Theme_AppCompat_Dialog){
  15.     companion object{
  16.         // 最短显示时间
  17.         private const val MIN_SHOW_TIME = 500L
  18.         // 最短延迟时间
  19.         private const val MIN_DELAY_TIME = 500L
  20.     }
  21.     private var tvMessage: TextView
  22.     init {
  23.         val parent = (context as? Activity)?.findViewById<ViewGroup>(android.R.id.content)
  24.         val loadView = LayoutInflater.from(context).inflate(R.layout.dialog_loading, parent, false)
  25.         setView(loadView)
  26.         tvMessage = loadView.findViewById(R.id.tv_message)
  27.     }
  28.     // 记录开始时间
  29.     private var mStartTime: Long = -1
  30.     // 防止延时隐藏任务的重复执行
  31.     private var mPostedHide: Boolean = false
  32.     // 防止延时显示任务的重复执行
  33.     private var mPostedShow: Boolean = false
  34.     // 是否已经消失
  35.     private var mDismissed: Boolean = false
  36.     // 主线程Handler
  37.     private val mHandler = Handler(Looper.getMainLooper())
  38.     // 显示
  39.     private val mDelayedShow: Runnable = Runnable {
  40.         mPostedShow = false
  41.         if (!mDismissed){
  42.             mStartTime = System.currentTimeMillis()
  43.             show()
  44.         }
  45.     }
  46.     // 隐藏
  47.     private val mDelayedHide: Runnable = Runnable {
  48.         mPostedHide = false
  49.         mStartTime = -1
  50.         dismiss()
  51.     }
  52.     // 显示Dialog
  53.     fun showDialog(message: String){
  54.         tvMessage.text = message
  55.         mStartTime = -1
  56.         mDismissed = false
  57.         mHandler.removeCallbacks(mDelayedHide)
  58.         mPostedHide = false
  59.         if (!mPostedShow){
  60.             mHandler.postDelayed(mDelayedShow, MIN_DELAY_TIME)
  61.             mPostedShow = true
  62.         }
  63.     }
  64.     // 隐藏Dialog
  65.     fun hideDialog(){
  66.         mDismissed = true
  67.         mHandler.removeCallbacks(mDelayedShow)
  68.         mPostedShow = false
  69.         val diff = System.currentTimeMillis() - mStartTime
  70.         if (diff >= MIN_SHOW_TIME || mStartTime == -1L){
  71.             dismiss()
  72.         }else{
  73.             if (!mPostedHide){
  74.                 mHandler.postDelayed(mDelayedHide, MIN_SHOW_TIME - diff)
  75.                 mPostedHide = true
  76.             }
  77.         }
  78.     }
  79.     // 从Window移除时移除所有的Callback
  80.     override fun onDetachedFromWindow() {
  81.         super.onDetachedFromWindow()
  82.         mHandler.removeCallbacks(mDelayedHide)
  83.         mHandler.removeCallbacks(mDelayedShow)
  84.     }
  85. }
复制代码
可以定义Dialog的巨细
  1.     <style name="Theme.AppCompat.Dialog" parent="Theme.AppCompat.Light.Dialog">
  2.         <!-- Customize your dialog theme here -->
  3.         <item name="android:windowBackground">@color/loading_color</item>
  4.         <item name="android:windowMinWidthMajor">30%</item>
  5.         <item name="android:windowMinWidthMinor">30%</item>
  6.         <item name="android:padding">6dp</item>
  7.     </style>
  8.     <!-- Custom ProgressBar style -->
  9.     <style name="CustomProgressBar" parent="Widget.AppCompat.ProgressBar">
  10.         <item name="android:indeterminateTint">@color/colorPrimary</item>
  11.     </style>
复制代码
  1. <?xml version="1.0" encoding="utf-8"?>
  2. <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
  3.     android:layout_width="match_parent"
  4.     android:layout_height="match_parent"
  5.     xmlns:app="http://schemas.android.com/apk/res-auto">
  6.     <ProgressBar
  7.         android:id="@+id/progressBar"
  8.         android:layout_width="wrap_content"
  9.         android:layout_height="wrap_content"
  10.         app:layout_constraintBottom_toTopOf="@id/tv_message"
  11.         app:layout_constraintStart_toStartOf="@id/tv_message"
  12.         app:layout_constraintEnd_toEndOf="@id/tv_message"
  13.         style="@style/CustomProgressBar"/>
  14.     <TextView
  15.         android:id="@+id/tv_message"
  16.         android:layout_width="wrap_content"
  17.         android:layout_height="wrap_content"
  18.         android:text="加载中..."
  19.         app:layout_constraintTop_toTopOf="parent"
  20.         app:layout_constraintBottom_toBottomOf="parent"
  21.         app:layout_constraintStart_toStartOf="parent"
  22.         app:layout_constraintEnd_toEndOf="parent" />
  23. </androidx.constraintlayout.widget.ConstraintLayout>
复制代码
结构文件就是一个 ProgressBar 和一个 TextView,用于展示提示信息。控制 Loading 对话框的表现和隐蔽直接使用 showDialog() 和 hideDialog() 方法就可以了。为了简朴示例,这里自定义的 Dialog 直接继承自 AlertDialog,留意要在适当的时机移除延时使命,防止内存走漏。
效果如下:

总结
本文通太过析 ContentLoadingProgressBar 的原理引出了项目开辟中 Loading 对话框的一种优化方式,克制对话框表现和隐蔽间隔时间太短导致的“闪耀”现象。

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

莫张周刘王

论坛元老
这个人很懒什么都没写!
快速回复 返回顶部 返回列表