Android自定義ProgressDialog最佳實踐

zhyp29 7年前發布 | 9K 次閱讀 安卓開發 Android開發 移動開發

ProgressDialog簡介

ProgressDialog也是在很多App中比較常見一個控件,大多數是使用它作為加載中的狀態展示,也有部分App使用它作為升級過程中的一個彈框,可以同步顯示下載進度。為什么需要自定義ProgressDialog,因為不同機型不同版本的手機差異性很大而且系統原生的很不美觀。這里的ProgressDialog跟上一篇文章自定義AlertDialog最佳實踐類似,使用系統源代碼并對部分代碼進行了更改,同時借助于自定義AlertDialog,因為ProgressDialog需要繼承AlertDialog。

ProgressDialog可以展示兩種方式的彈框,一種是有明確進度的彈框,另外一種是無明確進度的彈框,默認都是無明確進度的彈框。如果需要顯示有明確進度的彈框需要顯示設置它的樣式為ProgressDialog.STYLE_HORIZONTAL。

ProgressDialog創建方式有兩種,一種是直接new的方式創建一個ProgressDialog對象,另外一種就是使用ProgressDialog的show靜態方法。但是如果要使用帶有明確進度的彈框,必須使用new方式創建,后面會介紹為什么。

ProgressDialog實現簡要分析

ProgressDialog繼承自AlertDialog,支持兩種模式彈框,對應兩個常量,默認是無明確進度環形進度條。

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>

    提供了兩個構造方法,其中兩個參數的構造方法可以傳入自定義主題樣式。

    public ProgressDialog(Contextcontext) {
    this(context,0);
    }
     
    public ProgressDialog(Contextcontext, int theme) {
    super(context, theme);
    this.mContext = new ContextThemeWrapper(context, theme);
    initFormats();
    }
    

    在onCreate中創建View,在該方法中我們可以看到為什么設置了setIndeterminate并不會影響彈框風格,只有設置了setProgressStyle才會影響彈框風格。

    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>

    在onCreate中創建View的方式只跟mProgressStyle樣式有關系,如果mProgressStyle是STYLE_HORIZONTAL就會創建一個橫向的進度條,如果這時設置setIndeterminate,呈現出來的就是一個橫向無進度的進度條,從這里也可以知道 ProgressDialog實際上是自定義AlertDialog的一種實現方式,就是通過setView方式實現的 。

    接下來我們看一下ProgressDialog提供的靜態show方法的實現:

    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;
    }
    

    從源碼中可以看出,在show方法中也是通過new方式創建的ProgressDialog,但是這里的setIndeterminate方法好像沒有什么作用,無論設置true還是false都是一個無明確進度的環形進度條。

    有時候我們想創建一個帶有初始進度的ProgressDialog,但是在彈出來后發現會不起作用,引起該現象的原因就是一個參數mHasStarted,因為在設置setProgress的方法中需要判斷該參數是否為true,否則并不會同步顯示初始進度,這時候我們需要手動調用一下onStart方法。該參數會在onStart方法中設置為true,在onStop方法中重置為false。

    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;
    }
    

    如果沒有初始進度,ProgressDialog彈出后通過setProgress會自動更新進度,因為父類Dialog的show方法中會調用onStart方法。

    public void show() {
     
    ...
     
    mCanceled = false;

    if (!mCreated) { dispatchOnCreate(null); }   onStart();

    ...

} </code></pre>

ProgressDialog簡單使用

因為可以自定義布局文件,所以這里的Demo使用的是從系統中copy出來的,只是對布局文件中的ProgressBar進行了自定義樣式設置,同樣如果想首先更個性化的設置,也可以自定義布局文件或者對源碼進行更改。

創建無明確進度ProgressDialog

先看一下自定義環形無明確進度的ProgressDialog,布局文件很簡單,就是一個ProgressBar和一個TextView,在ProgressBar的動畫效果跟網易新聞或者嗶哩嗶哩動畫中效果類似,背景是一個純色環形,在定義一個indeterminateDrawable動畫就可以了。

先看一下彈框的布局文件progress_dialog.xml

<?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>

其中anim_progressbar動畫文件如下:

<?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>

在創建ProgressDialog的時候可以使用兩種方式,一種是new的方式,另外一種就是直接使用靜態方法show。

//方式一
dialog=new ProgressDialog(this,R.style.DialogHoloProgress);
dialog.setMessage("下載中...");
dialog.show();
 
//方式二
ProgressDialog.show(this, null, "loading...", true,true);

創建有明確進度ProgressDialog

這里布局文件就不貼出來了,可以下載源代碼查看。

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;
 }
});

小結

ProgressDialog在開發中并不建議使用太多,因為在用戶體驗上沒有內嵌入布局文件中一個ProgressBar用戶體驗好,Android官方的建議如下:

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.

一般在開發的時候會剝離出幾個公共的布局文件如:loading、error和empty,success情況可以直接抽象一個方法,在特定頁面引入指定布局文件即可。有時候在某些交互情況下還是直接使用ProgressDialog比較方便,因為它可以獨立于Activity或者Fragment存在,不受它們本身布局文件影響,降低了耦合關系。這種情況下可以將ProgressDialog設計成一個全透明的彈框,中間只有一個圖標表示狀態如loading的icon,一旦執行網絡操作后直接彈出該ProgressDialog,這樣還可以防止重復點擊。

 

來自:http://www.sunnyang.com/652.html

 

 本文由用戶 zhyp29 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!