Android自定义ProgressDialog最佳实践

zhyp29 8年前
   <h3>ProgressDialog简介</h3>    <p>ProgressDialog也是在很多App中比较常见一个控件,大多数是使用它作为加载中的状态展示,也有部分App使用它作为升级过程中的一个弹框,可以同步显示下载进度。为什么需要自定义ProgressDialog,因为不同机型不同版本的手机差异性很大而且系统原生的很不美观。这里的ProgressDialog跟上一篇文章自定义AlertDialog最佳实践类似,使用系统源代码并对部分代码进行了更改,同时借助于自定义AlertDialog,因为ProgressDialog需要继承AlertDialog。</p>    <p>ProgressDialog可以展示两种方式的弹框,一种是有明确进度的弹框,另外一种是无明确进度的弹框,默认都是无明确进度的弹框。如果需要显示有明确进度的弹框需要显示设置它的样式为ProgressDialog.STYLE_HORIZONTAL。</p>    <p>ProgressDialog创建方式有两种,一种是直接new的方式创建一个ProgressDialog对象,另外一种就是使用ProgressDialog的show静态方法。但是如果要使用带有明确进度的弹框,必须使用new方式创建,后面会介绍为什么。</p>    <h3>ProgressDialog实现简要分析</h3>    <p>ProgressDialog继承自AlertDialog,支持两种模式弹框,对应两个常量,默认是无明确进度环形进度条。</p>    <pre>  <code class="language-java">public class ProgressDialog extends AlertDialog {      /**  * Creates a ProgressDialog with a circular, spinning progress bar. This is  * the default.  */   public static final int STYLE_SPINNER = 0;      /**  * Creates a ProgressDialog with a horizontal progress bar.  */   public static final int STYLE_HORIZONTAL = 1;      //默认模式   private int mProgressStyle = STYLE_SPINNER;      ...  }  </code></pre>    <p>提供了两个构造方法,其中两个参数的构造方法可以传入自定义主题样式。</p>    <pre>  <code class="language-java">public ProgressDialog(Contextcontext) {   this(context,0);  }     public ProgressDialog(Contextcontext, int theme) {   super(context, theme);   this.mContext = new ContextThemeWrapper(context, theme);   initFormats();  }  </code></pre>    <p>在onCreate中创建View,在该方法中我们可以看到为什么设置了setIndeterminate并不会影响弹框风格,只有设置了setProgressStyle才会影响弹框风格。</p>    <pre>  <code class="language-java">protected void onCreate(BundlesavedInstanceState) {   LayoutInflaterinflater = LayoutInflater.from(mContext);   TypedArray a = mContext.obtainStyledAttributes(null,   R.styleable.AlertDialog, R.attr.alertDialogStyle, 0);   if (mProgressStyle == STYLE_HORIZONTAL) {   mViewUpdateHandler = new Handler() {   @Override   public void handleMessage(Messagemsg) {   super.handleMessage(msg);      /* Update the number and percent */   int progress = mProgress.getProgress();   int max = mProgress.getMax();   if (mProgressNumberFormat != null) {   String format = mProgressNumberFormat;   mProgressNumber.setText(String.format(format, progress,   max));   } else {   mProgressNumber.setText("");   }   if (mProgressPercentFormat != null) {   double percent = (double) progress / (double) max;   SpannableStringtmp = new SpannableString(   mProgressPercentFormat.format(percent));   tmp.setSpan(new StyleSpan(   android.graphics.Typeface.BOLD), 0, tmp   .length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);   mProgressPercent.setText(tmp);   } else {   mProgressPercent.setText("");   }   }   };   Viewview = inflater.inflate(a.getResourceId(   R.styleable.AlertDialog_horizontalProgressLayout,   R.layout.alert_dialog_progress), null);   mProgress = (ProgressBar) view.findViewById(R.id.progress);   mProgressNumber = (TextView) view   .findViewById(R.id.progress_number);   mProgressPercent = (TextView) view   .findViewById(R.id.progress_percent);   setView(view);   } else {   Viewview = inflater.inflate(a.getResourceId(   R.styleable.AlertDialog_progressLayout,   R.layout.progress_dialog), null);   mProgress = (ProgressBar) view.findViewById(R.id.progress);   mMessageView = (TextView) view.findViewById(R.id.message);   setView(view);   }   a.recycle();      setIndeterminate(mIndeterminate);   onProgressChanged();   super.onCreate(savedInstanceState);  }  </code></pre>    <p>在onCreate中创建View的方式只跟mProgressStyle样式有关系,如果mProgressStyle是STYLE_HORIZONTAL就会创建一个横向的进度条,如果这时设置setIndeterminate,呈现出来的就是一个横向无进度的进度条,从这里也可以知道 ProgressDialog实际上是自定义AlertDialog的一种实现方式,就是通过setView方式实现的 。</p>    <p>接下来我们看一下ProgressDialog提供的静态show方法的实现:</p>    <pre>  <code class="language-java">public static ProgressDialogshow(Contextcontext, CharSequencetitle,   CharSequencemessage, boolean indeterminate, boolean cancelable,   OnCancelListenercancelListener) {   ProgressDialogdialog = new ProgressDialog(context,R.style.DialogHoloProgress);   dialog.setTitle(title);   dialog.setMessage(message);   dialog.setIndeterminate(indeterminate);   dialog.setCancelable(cancelable);   dialog.setOnCancelListener(cancelListener);   dialog.show();   return dialog;  }  </code></pre>    <p>从源码中可以看出,在show方法中也是通过new方式创建的ProgressDialog,但是这里的setIndeterminate方法好像没有什么作用,无论设置true还是false都是一个无明确进度的环形进度条。</p>    <p>有时候我们想创建一个带有初始进度的ProgressDialog,但是在弹出来后发现会不起作用,引起该现象的原因就是一个参数mHasStarted,因为在设置setProgress的方法中需要判断该参数是否为true,否则并不会同步显示初始进度,这时候我们需要手动调用一下onStart方法。该参数会在onStart方法中设置为true,在onStop方法中重置为false。</p>    <pre>  <code class="language-java">public void setProgress(int value) {   if (mHasStarted) {   mProgress.setProgress(value);   onProgressChanged();   } else {   mProgressVal = value;   }  }     @Override  public void onStart() {   super.onStart();   mHasStarted = true;  }     @Override  protected void onStop() {   super.onStop();   mHasStarted = false;  }  </code></pre>    <p>如果没有初始进度,ProgressDialog弹出后通过setProgress会自动更新进度,因为父类Dialog的show方法中会调用onStart方法。</p>    <pre>  <code class="language-java">public void show() {      ...      mCanceled = false;      if (!mCreated) {   dispatchOnCreate(null);   }      onStart();      ...     }  </code></pre>    <h3>ProgressDialog简单使用</h3>    <p>因为可以自定义布局文件,所以这里的Demo使用的是从系统中copy出来的,只是对布局文件中的ProgressBar进行了自定义样式设置,同样如果想首先更个性化的设置,也可以自定义布局文件或者对源码进行更改。</p>    <p>创建无明确进度ProgressDialog</p>    <p>先看一下自定义环形无明确进度的ProgressDialog,布局文件很简单,就是一个ProgressBar和一个TextView,在ProgressBar的动画效果跟网易新闻或者哔哩哔哩动画中效果类似,背景是一个纯色环形,在定义一个indeterminateDrawable动画就可以了。</p>    <p>先看一下弹框的布局文件progress_dialog.xml</p>    <pre>  <code class="language-java"><?xmlversion="1.0" encoding="utf-8"?>  <LinearLayoutxmlns:android="http://schemas.android.com/apk/res/android"      android:layout_width="match_parent"      android:layout_height="match_parent"      android:gravity="center"      android:orientation="horizontal"      android:padding="20dip">         <ProgressBar          android:id="@+id/progress"          style="?android:attr/progressBarStyle"          android:layout_width="wrap_content"          android:layout_height="wrap_content"          android:layout_marginEnd="24dip"          android:layout_marginLeft="12dp"          android:background="@drawable/bg_progressbar"          android:indeterminateDrawable="@anim/anim_progressbar"          android:max="10000"/>         <TextView          android:id="@+id/message"          android:textColor="#444"          android:layout_width="wrap_content"          android:layout_height="wrap_content"          android:layout_marginRight="12dp"          android:textSize="16sp"          android:layout_gravity="center_vertical"/>     </LinearLayout>  </code></pre>    <p>其中anim_progressbar动画文件如下:</p>    <pre>  <code class="language-java"><?xmlversion="1.0" encoding="utf-8"?>  <rotatexmlns:android="http://schemas.android.com/apk/res/android"      android:fromDegrees="0"      android:pivotX="50%"        android:pivotY="50%"       android:toDegrees="1080">         <shape          android:innerRadiusRatio="3"          android:shape="ring"          android:thicknessRatio="9"          android:useLevel="false">          <gradient              android:endColor="#ccffffff"              android:centerColor="#e00"              android:startColor="#e00"              android:type="sweep"/>      </shape>     </rotate>  </code></pre>    <p>在创建ProgressDialog的时候可以使用两种方式,一种是new的方式,另外一种就是直接使用静态方法show。</p>    <pre>  <code class="language-java">//方式一  dialog=new ProgressDialog(this,R.style.DialogHoloProgress);  dialog.setMessage("下载中...");  dialog.show();     //方式二  ProgressDialog.show(this, null, "loading...", true,true);  </code></pre>    <p style="text-align:center"><img src="https://simg.open-open.com/show/d98cbef482d4b99b47651e5b2f7545c7.gif"></p>    <p>创建有明确进度ProgressDialog</p>    <p>这里布局文件就不贴出来了,可以下载源代码查看。</p>    <pre>  <code class="language-java">private int progress=0;     //更新进度条进度  private Handlerhandler=new Handler(){   @Override   public void handleMessage(Messagemsg) {   progress++;   dialog.setProgress(progress);   if(progress<100){   SystemClock.sleep(200);   handler.sendEmptyMessage(0);   }   }  };     //常见弹框  dialog=new ProgressDialog(this,R.style.DialogHoloProgress);  dialog.setTitle("title");  dialog.setMessage("下载中...");  dialog.setMax(100);  dialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);  //dialog.onStart();  dialog.show();  handler.sendEmptyMessage(0);  dialog.setOnDismissListener(new OnDismissListener() {   public void onDismiss(DialogInterfacedialog) {   handler.removeCallbacksAndMessages(null);   progress=0;   }  });  </code></pre>    <p style="text-align:center"><img src="https://simg.open-open.com/show/7f63a58fa1f6836c5ecf32a2942b2101.gif"></p>    <h3>小结</h3>    <p>ProgressDialog在开发中并不建议使用太多,因为在用户体验上没有内嵌入布局文件中一个ProgressBar用户体验好,Android官方的建议如下:</p>    <p>If you need to indicate loading or indeterminate progress, you should instead follow the design guidelines for Progress & Activity and use a ProgressBar in your layout.</p>    <p>一般在开发的时候会剥离出几个公共的布局文件如:loading、error和empty,success情况可以直接抽象一个方法,在特定页面引入指定布局文件即可。有时候在某些交互情况下还是直接使用ProgressDialog比较方便,因为它可以独立于Activity或者Fragment存在,不受它们本身布局文件影响,降低了耦合关系。这种情况下可以将ProgressDialog设计成一个全透明的弹框,中间只有一个图标表示状态如loading的icon,一旦执行网络操作后直接弹出该ProgressDialog,这样还可以防止重复点击。</p>    <p> </p>    <p>来自:http://www.sunnyang.com/652.html</p>    <p> </p>