ProgressDialog已经过时了,有什么替代品可以使用?

282

我发现ProgressDialog已经被弃用了,除了ProgressBar之外,有什么替代品可以使用。

我正在使用Android Studio版本2.3.3。

ProgressDialog progressDialog=new ProgressDialog(this);
progressDialog.show();

1
可能是[ProgressDialog已被弃用]的重复问题(https://dev59.com/6VcO5IYBdhLWcg3wtz1g)。 - Lalit Jadav
22个回答

231
是的,在 API 级别 26 中它已被弃用。取而代之的是,您可以使用 progressBar。
要以编程方式创建它: 首先获取对根布局的引用
RelativeLayout layout = findViewById(R.id.display);  //specify here Root layout Id

或者

RelativeLayout layout = findViewById(this);

然后添加进度条
progressBar = new ProgressBar(youractivity.this, null, android.R.attr.progressBarStyleLarge);
RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(100, 100);
params.addRule(RelativeLayout.CENTER_IN_PARENT);
layout.addView(progressBar, params);

显示进度条
progressBar.setVisibility(View.VISIBLE);

隐藏进度条
progressBar.setVisibility(View.GONE);

禁用用户交互,只需添加以下代码。
getWindow().setFlags(WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE,
                           WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE);

要恢复用户交互,只需添加以下代码。
getWindow().clearFlags(WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE);

只是为了以后参考,将android.R.attr.progressBarStyleSmall更改为android.R.attr.progressBarStyleHorizontal
下面的代码仅适用于API级别21以上
progressBar.setProgressTintList(ColorStateList.valueOf(Color.RED));

通过xml创建它:
<ProgressBar
        android:id="@+id/progressbar"
        style="?android:attr/progressBarStyleHorizontal"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:indeterminate="true"
        android:max="100"
        android:backgroundTint="@color/white"
        android:layout_below="@+id/framelauout"
        android:indeterminateTint="#1a09d6"
        android:layout_marginTop="-7dp"/>

在您的活动中
progressBar = (ProgressBar) findViewById(R.id.progressbar);

显示/隐藏进度条是一样的
 progressBar.setVisibility(View.VISIBLE); // To show the ProgressBar 
 progressBar.setVisibility(View.INVISIBLE); // To hide the ProgressBar

这是一个示例图像,展示了它的外观。


4
就好像你一直在使用这个功能,然后你想:“耶,最终进度对话框已过时了,我可以向世界展示我的潜力 :D”,开个玩笑,非常感谢你提供的详细答案。 - Mo Hajr
4
但是如何为它设置setMessage()呢? - reverie_ss
7
@David 事实上,阻塞用户界面正是我想要做的,因为在后台任务完成之前,我不希望用户能够与其进行交互。 - Ben Jaguar Marshall
6
您的意思是Google主张用户可以打开空的下拉菜单,看到占位项并按按钮,而后台线程正在准备应用程序的数据?这是糟糕的设计!应该显示一个“请稍等”对话框,最好是动画形式以显示应用程序没有停滞,然后允许用户在应用程序准备好后与UI交互。但如果您的意思是“不要在UI线程上执行重型任务”,那么这是有道理的。当我说“阻塞UI”时,我的意思是防止用户进行交互,而不是在UI线程上做工作。 - Ben Jaguar Marshall
14
现在,我需要写很多行代码才能得到差不多的结果?谷歌真是干得好! - Alek Depler
显示剩余8条评论

61

您可以使用 AlertDialog 来替代 ProgressDialog。请参考以下代码来使用 ProgressDialog。每当您展示进度对话框时,都需要调用此函数。

代码:

import android.content.Context;
import android.graphics.Color;
import android.view.Gravity;
import android.view.ViewGroup;
import android.view.Window;
import android.view.WindowManager;
import android.widget.LinearLayout;
import android.widget.ProgressBar;
import android.widget.TextView;

import androidx.appcompat.app.AlertDialog;

public class ProgressHelper {

    private static AlertDialog dialog = null;

    public static void showDialog(Context context, String message) {
        if(dialog == null){
            int llPadding = 30;
            LinearLayout ll = new LinearLayout(context);
            ll.setOrientation(LinearLayout.HORIZONTAL);
            ll.setPadding(llPadding, llPadding, llPadding, llPadding);
            ll.setGravity(Gravity.CENTER);
            LinearLayout.LayoutParams llParam = new LinearLayout.LayoutParams(
                    LinearLayout.LayoutParams.WRAP_CONTENT,
                    LinearLayout.LayoutParams.WRAP_CONTENT);
            llParam.gravity = Gravity.CENTER;
            ll.setLayoutParams(llParam);

            ProgressBar progressBar = new ProgressBar(context);
            progressBar.setIndeterminate(true);
            progressBar.setPadding(0, 0, llPadding, 0);
            progressBar.setLayoutParams(llParam);

            llParam = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT,
                    ViewGroup.LayoutParams.WRAP_CONTENT);
            llParam.gravity = Gravity.CENTER;
            TextView tvText = new TextView(context);
            tvText.setText(message);
            tvText.setTextColor(Color.parseColor("#000000"));
            tvText.setTextSize(20);
            tvText.setLayoutParams(llParam);

            ll.addView(progressBar);
            ll.addView(tvText);

            AlertDialog.Builder builder = new AlertDialog.Builder(context);
            builder.setCancelable(true);
            builder.setView(ll);

            dialog = builder.create();
            dialog.show();
            Window window = dialog.getWindow();
            if (window != null) {
                WindowManager.LayoutParams layoutParams = new WindowManager.LayoutParams();
                layoutParams.copyFrom(dialog.getWindow().getAttributes());
                layoutParams.width = LinearLayout.LayoutParams.WRAP_CONTENT;
                layoutParams.height = LinearLayout.LayoutParams.WRAP_CONTENT;
                dialog.getWindow().setAttributes(layoutParams);
            }
        }
    }

    public static  boolean isDialogVisible(){
        if(dialog != null){
            return dialog.isShowing();
        }else {
            return false;
        }
    }

    public static  void dismissDialog(){
        if(dialog != null){
            dialog.dismiss();
            dialog = null;
        }
    }
}

输出:

输入图像描述


1
如何关闭它? - Jay Dangar
1
创建一个名为BaseActivity的基类,然后在添加此代码后,还需要添加一个dismiss dialog方法,但是为此,您需要全局定义“AlertDialog dialog”对象。 - Kishan Donga
在你的隐藏对话框方法中,只需调用dialog.dismiss()方法即可。 - Kishan Donga
1
一切都很好,但是在屏幕上的任何位置轻触后,对话框会被关闭。服务调用正在进行中,用户能够关闭它并与UI进行交互,这不是一个好的情况。有什么解决方案吗? - Vivek Kumar
1
@FaisalQayyum 把 AlertDialog 对话框实例设为全局变量,并用于 dismiss(关闭)。 - Kishan Donga
显示剩余4条评论

39
你可以为你的进度条编写一个简单的xml接口,并将其作为视图传递给AlertDialog,然后在任何时候显示或关闭对话框。 progress.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="horizontal"
    android:padding="13dp"
    android:layout_centerHorizontal="true"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content">

    <ProgressBar
        android:id="@+id/loader"
        android:layout_marginEnd="5dp"
        android:layout_width="45dp"
        android:layout_height="45dp" />
    <TextView
        android:layout_width="wrap_content"
        android:text="Loading..."
        android:textAppearance="?android:textAppearanceSmall"
        android:layout_gravity="center_vertical"
        android:id="@+id/loading_msg"
        android:layout_toEndOf="@+id/loader"
        android:layout_height="wrap_content" />

</LinearLayout>

这是显示进度对话框的代码。只需将此代码复制并粘贴到您的片段中即可。

Dialog dialog;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);


        AlertDialog.Builder builder = new AlertDialog.Builder(this);
        builder.setView(R.layout.progress);
        // This should be called once in your Fragment's onViewCreated() or in Activity onCreate() method to avoid dialog duplicates.
        dialog = builder.create();
    }
    
//   This method is used to control the progress dialog.
      private void setDialog(boolean show){
               if (show)dialog.show();
                    else dialog.dismiss();
           }

每当您想显示进度对话框时,只需调用该方法,并将true作为参数传递以显示它,或将false传递以关闭对话框。


2
@Alok Rajasukumaran,看起来你正在使用Activity,只需将其替换为this,这样它就会变成AlertDialog.Builder builder = new AlertDialog.Builder(this);。请确保你复制的是正确的代码。 - devmike01
3
еПѓдї•еИ†йЩ§layout_toEndOfпЉМеЫ†дЄЇеЃГдЄНйАВзФ®дЇОLinearLayoutгАВ - Mark
我一直在尝试通过警告对话框显示进度条,但是我遇到了一些问题,你能否请查看我的问题 - Faisal Qayyum
当我调用setDialog(false)时,对话框不会消失。只有当我触摸屏幕时,它才会消失。 - Darksymphony
我知道现在帮助@Darksymphony已经太晚了,但是setDialog不应该调用builder.create()。这只能发生一次,否则当调用代码想要dismiss()第一个对话框时,第二个对话框将被创建。因此,第一个仍然显示... - Carl Smith
@CarlSmith 你说得对。我提供了代码片段作为进度对话框实现的示例。 - devmike01

32

是的,ProgressDialog已经被弃用了,但Dialog并没有。

您可以将自己的XML文件(包含进度条和加载文本)填充到对话框对象中,然后使用show()dismiss()函数显示或隐藏它。

以下是一个示例(Kotlin):

ProgressDialog类

class ProgressDialog {
companion object {
    fun progressDialog(context: Context): Dialog{
        val dialog = Dialog(context)
        val inflate = LayoutInflater.from(context).inflate(R.layout.progress_dialog, null)
        dialog.setContentView(inflate)
        dialog.setCancelable(false)
        dialog.window!!.setBackgroundDrawable(
                ColorDrawable(Color.TRANSPARENT))
        return dialog
    }
  }
}

XML

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent"
android:background="#fff"
android:padding="13dp"
android:layout_height="wrap_content">
<ProgressBar
    android:id="@+id/progressBar"
    style="?android:attr/progressBarStyle"
    android:layout_width="100dp"
    android:layout_margin="7dp"
    android:layout_height="100dp"/>
<TextView
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_centerVertical="true"
    android:layout_margin="7dp"
    android:layout_toEndOf="@+id/progressBar"
    android:text="Loading..." />
</RelativeLayout>

在你的代码中: 只需执行 var dialog = ProgressDialog.progressDialog(context)

显示进度对话框:dialog.show()

隐藏进度对话框:dialog.dismiss()


1
很棒的解决方案!刚刚在下面发布了ProgressDialog类的另一种替代版本。 - yaugenka

32

ProgressBar 很简单易用,我打算让它和简单的进度对话框一样。第一步是你可以创建一个 XML 布局来展示你想要显示的对话框,比如我们给这个布局命名为:

layout_loading_dialog.xml

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:orientation="horizontal"
    android:padding="20dp">
    <ProgressBar
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_weight="1" />

    <TextView
        android:layout_width="0dp"
        android:layout_height="match_parent"
        android:layout_weight="4"
        android:gravity="center"
        android:text="Please wait! This may take a moment." />
</LinearLayout>

下一步是创建AlertDialog,它将显示带有ProgressBar的此布局。

AlertDialog.Builder builder = new AlertDialog.Builder(context);
builder.setCancelable(false); // if you want user to wait for some process to finish,
builder.setView(R.layout.layout_loading_dialog);
AlertDialog dialog = builder.create();

现在唯一需要做的就是在我们的点击事件中显示和隐藏这个对话框,就像这样

dialog.show(); // to show this dialog
dialog.dismiss(); // to hide this dialog

好了,就这样,它应该能够工作。正如您所见的那样,使用ProgressBar代替ProgressDialog相当简单易行。 现在,您可以在Handler或ASyncTask中显示/关闭此对话框框,具体取决于您的需求。


请将 progress_dialog.show(); 更改为 builder.show();。谢谢。 - Matheus Miranda
2
我已经将AlertDialog对象的名称更改为正确的名称,但在show()/dismiss()上,我没有放置builder.show(),因为这样做我们将不得不执行dialog.dismiss(),这对于喜欢保持代码简单易懂的人来说有点困惑。此外,builder.show()返回AlertDialog对象,我们将不得不保存该引用,如AlertDialog dialog = builder.show();,然后执行dialog.dismiss(),更容易的方法是让builder.create()返回某个AlertDialog对象,然后使用该对象来显示和隐藏对话框。 - Syed Naeem
如果调用线程不是主UI线程,我们可以使用“runOnUiThread(new Runnable(){ @Override public void run(){ dialog.show / dismiss(); } });”(在WebView - shouldoverride方法中使用时很有用)。 - SHS
我一直在尝试通过警告对话框显示进度条,但是我遇到了一些问题,你可以请检查一下我的问题吗? - Faisal Qayyum

31
该类在API 26中已被弃用。ProgressDialog是一种模态对话框,它防止用户与应用程序交互。不要使用此类,而应该使用进度指示器,例如可嵌入应用程序UI的ProgressBar。或者,您可以使用通知来通知用户任务的进度。 link 由于Google的新UI标准,它在Android O上已被弃用。

38
由于谷歌的新用户界面标准,它在Android O中已被弃用。以下是新UI标准的链接:[链接] - Ajay S
61
它被弃用的原因是因为它会阻止用户输入吗?有时我想知道 Google 的工程师吸了什么样的粉。使用一个阻塞对话框比隐藏/禁用输入控件并启用进度条要容易得多。这是 Google 愚蠢的想法。 - TheRealChx101
6
我猜他们更关心防止模态对话框被滥用:有时它们会显示为使用户等待操作结束而出现在情况下,用户根本不需要等待,而是可以继续做其他事情,因此不必要地让他们等待会降低用户体验。尽管如此,在很多情况下,用户没有选择,只能等待操作完成,所以我同意应该有一个非废弃的标准方法来处理这些情况,而不是强制每个开发者都实现自己的方法。 - Fran Marzoa
5
@David,请看一下我评论的点赞数。也请告诉那些人,我认为 Google 的决定更多是出于政治原因。而且我仍在使用对话框。所以你和 Google 都失败了。 - TheRealChx101
3
Android团队独断地认为他们比我更了解我的客户需要的傲慢态度令人难以置信。阻止与UI互动是非常常见的需求,且曾经由“ProgressDialog”完美提供服务。现在我又不得不浪费生命中的另一部分时间重新发明轮子。 - rmirabelle
显示剩余4条评论

17

您不需要导入任何自定义库。

我喜欢使用现代的 AlertDialog ,所以这是Kishan Donga在此页面发布的绝佳答案的Kotlin版本。

Kotlin代码:

fun setProgressDialog(context:Context, message:String):AlertDialog {
    val llPadding = 30
    val ll = LinearLayout(context)
    ll.orientation = LinearLayout.HORIZONTAL
    ll.setPadding(llPadding, llPadding, llPadding, llPadding)
    ll.gravity = Gravity.CENTER
    var llParam = LinearLayout.LayoutParams(
                  LinearLayout.LayoutParams.WRAP_CONTENT,
                  LinearLayout.LayoutParams.WRAP_CONTENT)
    llParam.gravity = Gravity.CENTER
    ll.layoutParams = llParam

    val progressBar = ProgressBar(context)
    progressBar.isIndeterminate = true
    progressBar.setPadding(0, 0, llPadding, 0)
    progressBar.layoutParams = llParam

    llParam = LinearLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT,
                        ViewGroup.LayoutParams.WRAP_CONTENT)
    llParam.gravity = Gravity.CENTER
    val tvText = TextView(context)
    tvText.text = message
    tvText.setTextColor(Color.parseColor("#000000"))
    tvText.textSize = 20.toFloat()
    tvText.layoutParams = llParam

    ll.addView(progressBar)
    ll.addView(tvText)

    val builder = AlertDialog.Builder(context)
    builder.setCancelable(true)
    builder.setView(ll)

    val dialog = builder.create()
    val window = dialog.window
    if (window != null) {
        val layoutParams = WindowManager.LayoutParams()
        layoutParams.copyFrom(dialog.window?.attributes)
        layoutParams.width = LinearLayout.LayoutParams.WRAP_CONTENT
        layoutParams.height = LinearLayout.LayoutParams.WRAP_CONTENT
                dialog.window?.attributes = layoutParams
    }
    return dialog
}

使用方法:

val dialog = setProgressDialog(this, "Loading..")
dialog.show()

输出:

在此输入图片描述


如何在Fragment中使用?Fragment中的上下文是什么? - roghayeh hosseini
1
通常在片段中,您可以获取当前视图,因此上下文应为:view!!.context - Alessandro Ornano

13

如果你真的想违背他们的意愿,你仍然可以使用Sweet Alert Dialog或者自己创建一个。

progress_dialog_layout

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <TableRow
        android:layout_centerInParent="true"
        android:layout_width="match_parent"
        android:layout_height="64dp" >

        <ProgressBar
            android:id="@+id/progressBar2"
            style="?android:attr/progressBarStyle"
            android:layout_width="wrap_content"
            android:layout_height="match_parent" />

        <TextView
            android:gravity="center|left"
            android:id="@+id/textView9"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:textColor="@color/black"
            android:textSize="18sp"
            android:text="Downloading data. Please wait.." />
    </TableRow>
</RelativeLayout>

Java 代码:

AlertDialog b;
AlertDialog.Builder dialogBuilder;

public void ShowProgressDialog() {
dialogBuilder = new AlertDialog.Builder(DataDownloadActivity.this);
LayoutInflater inflater = (LayoutInflater) getSystemService( Context.LAYOUT_INFLATER_SERVICE );
            View dialogView = inflater.inflate(R.layout.progress_dialog_layout, null);
            dialogBuilder.setView(dialogView);
            dialogBuilder.setCancelable(false);
            b = dialogBuilder.create();
            b.show();
        }

        public void HideProgressDialog(){

            b.dismiss();
        }

2
我在生产环境中使用SweetAlertDialog遇到了可怕的经历,非常小心,因为它会从一个窗口泄漏到另一个窗口。 - Jeremy
对我来说,HideProgress 没有任何作用。 - Saeed Neamati
4
为什么要使用一个库来显示进度对话框? - Khemraj Sharma
注意,这里的 TableRow 和使用水平 LinearLayout 是一样的,只要父级不是 TableLayout。https://developer.android.com/reference/android/widget/TableRow - fmaccaroni

7

27
他说:“除了进度条以外”。 - creativecreatorormaybenot

5
如在文档页面中提到的,替代方案是ProgressBar。通过将一个ProgressBar放入一个AlertDialog中,可以复制ProgressDialog的外观。
虽然仍然可以使用ProgressDialog,但Android不希望您使用它,这就是为什么它被弃用的原因。因此,您应该考虑另一种解决问题的方法,例如将ProgressBar嵌入您的Layout中。

@PeterHaddad 在我的项目中,它在 API 28 中“正常”工作,并且与较低的 API 相同。正如弃用建议所示,它已不再受支持,未来可能无法使用。 - DMonkey

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接