1. 程式人生 > >Builder設計模式構建萬能Dialog

Builder設計模式構建萬能Dialog

首先看下AlertDialog解析圖


首先要會用AlertDialog的使用

new AlertDialog.Builder(this)
    .setIcon(R.mipmap.ic_launcher)
    .setTitle("訊息")
    .setPositiveButton("確定", new DialogInterface.OnClickListener() {

        @Override
public void onClick(DialogInterface dialog, int which) {

        }
    })
    .setNegativeButton("取消"
, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { } }) .create().show();
對原始碼解析我們可以得到上圖的結果,接下來就是自定義一個Dialog,因為我在我的程式碼中寫了很多註解我就不解釋了

第一步自定義一個AlertDialog

public class AlertDialog extends Dialog {
    AlertController mAlert
; public AlertDialog(@NonNull Context context, @StyleRes int themeResId) { super(context, themeResId); mAlert = new AlertController(this, getWindow()); } public void setText(int viewId, CharSequence text) { mAlert.setText(viewId, text); } public void setOnClickListener
(int viewId, View.OnClickListener listener) { mAlert.setOnClickListener(viewId, listener); } /** * 減少findviewById的次數 */ public <T extends View> T getView(int viewId) { return mAlert.getView(viewId); } public static class Builder { private final AlertController.AlertParams P; public Builder(@NonNull Context context) { this(context, R.style.dialog); } public Builder(@NonNull Context context, @StyleRes int themeResId) { P = new AlertController.AlertParams(context, themeResId); } public AlertDialog create() { final AlertDialog dialog = new AlertDialog(P.mContext, P.themeResId); P.apply(dialog.mAlert); dialog.setCancelable(P.mCancelable); if (P.mCancelable) { dialog.setCanceledOnTouchOutside(true); } dialog.setOnCancelListener(P.mOnCancelListener); dialog.setOnDismissListener(P.mOnDismissListener); if (P.mOnKeyListener != null) { dialog.setOnKeyListener(P.mOnKeyListener); } return dialog; } /** * 設定View */ public AlertDialog.Builder setContentView(int layoutResId) { P.mView = null; P.mViewLayoutResId = layoutResId; return this; } public AlertDialog.Builder setContentView(View view) { P.mView = view; P.mViewLayoutResId = 0; return this; } /** * 存放自定義View中的文字 */ public AlertDialog.Builder setText(int layoutId, CharSequence text) { P.mTextArray.put(layoutId, text); return this; } /** * 存放自定義View中按鈕點選事件 */ public AlertDialog.Builder setOnClickListener(int layoutId, View.OnClickListener listener) { P.mClickArray.put(layoutId, listener); return this; } //配置一些萬能引數 public AlertDialog.Builder fullWidth() { P.mWidth = ViewGroup.LayoutParams.MATCH_PARENT; return this; } //設定動畫 public AlertDialog.Builder fromButtom(boolean isAnimation) { if (isAnimation) { P.mAnimation = R.style.dialog_from_bottom_anim; } P.mGravity = Gravity.BOTTOM; return this; } public AlertDialog.Builder setWidthAndHeight(int width, int heigth) { P.mWidth = width; P.mHeigth = heigth; return this; } //設定預設動畫 public AlertDialog.Builder AddDefaultAnimation() { P.mAnimation = R.style.dialog_scale_animation; return this; } //設定動畫 public AlertDialog.Builder setAnimation(int styleAnimation) { P.mAnimation = styleAnimation; return this; } public AlertDialog show() { final AlertDialog dialog = create(); dialog.show(); return dialog; } } }
第二步:自定義一個AlertContorller
class AlertController {
    private Window mWindow;
    private AlertDialog mAlertDialog;
    private ViewHelper mViewHelper;
    public AlertController(AlertDialog alertDialog, Window window) {
        this.mWindow = window;
        this.mAlertDialog = alertDialog;
}

    public void setViewHelper(ViewHelper mViewHelper) {
        this.mViewHelper = mViewHelper;
}

    public Window getWindow() {
        return mWindow;
}

    public AlertDialog getDialog() {
        return mAlertDialog;
}

    public void setText(int viewId, CharSequence text) {
        mViewHelper.setText(viewId,text);
}

    public void setOnClickListener(int viewId, View.OnClickListener listener) {
        mViewHelper.setOnClickListener(viewId,listener);
}


    /**
     * 減少findviewById的次數
     */
public <T extends View> T getView(int viewId) {
     return mViewHelper.getView(viewId);
}

    public static class AlertParams {
        public Context mContext;
        public int themeResId;
        public boolean mCancelable = true;//點選空白是否能夠取消
public DialogInterface.OnCancelListener mOnCancelListener;//dialog Cancel監聽
public DialogInterface.OnDismissListener mOnDismissListener;//dialog Dismiss監聽
public DialogInterface.OnKeyListener mOnKeyListener;//按鍵監聽
        //View載入佈局
public View mView;
//id載入佈局
public int mViewLayoutResId;
//存放自定義view中的文字
SparseArray<CharSequence> mTextArray = new SparseArray<>();
//存放自定義view中的點選事件
SparseArray<View.OnClickListener> mClickArray = new SparseArray<>();
        public int mWidth = ViewGroup.LayoutParams.WRAP_CONTENT;
        public int mGravity = Gravity.CENTER;
        public int mAnimation;
        public int mHeigth = ViewGroup.LayoutParams.WRAP_CONTENT;
        public AlertParams(Context context, int themeResId) {
            this.mContext = context;
            this.themeResId = themeResId;
}

        /**
         * 繫結和設定引數
         *
         * @param mAlert
*/
public void apply(AlertController mAlert) {
            //設定引數
            //1.設定佈局
ViewHelper viewHelper = null;
            if (mViewLayoutResId != 0) {
                viewHelper = new ViewHelper(mContext, mViewLayoutResId);
}
            if (mView != null) {
                viewHelper = new ViewHelper();
viewHelper.setContentView(mView);
}
            if (viewHelper == null) {
                throw new IllegalArgumentException("請設定佈局setContentView()");
}
            //給dialog設定佈局
mAlert.getDialog().setContentView(viewHelper.getContentView());
//2.設定文字
int textArraysize = mTextArray.size();
            for (int i = 0; i < textArraysize; i++) {
                viewHelper.setText(mTextArray.keyAt(i), mTextArray.valueAt(i));
}
            //3.設定點選事件
int clickSize = mClickArray.size();
            for (int i = 0; i < clickSize; i++) {
                viewHelper.setOnClickListener(mClickArray.keyAt(i), mClickArray.valueAt(i));
}

            mAlert.setViewHelper(viewHelper);
//4.配置自定義效果 全屏  從底部彈出 預設動畫
Window window = mAlert.getWindow();
//設定位置
window.setGravity(mGravity);
//設定動畫
if (mAnimation != 0) {
                window.setWindowAnimations(mAnimation);
}
            //設定寬和高
WindowManager.LayoutParams params = window.getAttributes();
params.width = mWidth;
params.height = mHeigth;
window.setAttributes(params);
}
    }
}
第三步:自定義一個ViewHelper
public class ViewHelper {
    private View mContentView;//佈局View
    //減少findviewById
private SparseArray<WeakReference<View>> mViews;
    public ViewHelper(Context context, int viewLayoutResId) {
        this();//此方法不可少,呼叫的是無參ViewHelper
mContentView = LayoutInflater.from(context).inflate(viewLayoutResId, null);
}

    public ViewHelper() {
        mViews = new SparseArray<>();
}

    public void setContentView(View contentView) {
        this.mContentView = contentView;
}

    public void setText(int viewId, CharSequence text) {
        TextView tv = getView(viewId);
        if (tv != null) {
           tv.setText(text);
}
    }

    public void setOnClickListener(int viewId, View.OnClickListener listener) {
        View view = getView(viewId);
        if(view!=null){
            view.setOnClickListener(listener);
}
    }

    public View getContentView() {
        return mContentView;
}

    /**
     * 減少findviewById的次數
     */
public <T extends View> T getView(int viewId) {
        WeakReference<View> weakReference = mViews.get(viewId);
View view = null;
        if(weakReference!=null){
            view=weakReference.get();
}
        if (view == null) {
            view = mContentView.findViewById(viewId);
            if (view != null) {
                mViews.put(viewId, new WeakReference<>(view));
}
        }
        return (T) view;
}
}
最後這裡寫下我自己使用的樣式和佈局

①dialog的樣式

<style name="dialog" parent="@android:style/Theme.Dialog">
<!--沒有邊框-->
<item name="android:windowFrame">@null</item>
<!--是否懸浮在activity之上-->
<item name="android:windowIsFloating">true</item>
<!--沒有標題-->
<item name="android:windowIsTranslucent">true</item>
<!--背景透明-->
<item name="android:background">@android:color/transparent</item>
<!--模糊-->
<item name="android:backgroundDimEnabled">true</item>
<!--沒有標題-->
<item name="android:windowNoTitle">true</item>
</style>
②進入時的動畫
<style name="dialog_from_bottom_anim">
    <item name="android:windowEnterAnimation">@anim/dialog_from_bottom_anim_in</item>
    <item name="android:windowExitAnimation">@anim/dialog_from_bottom_anim_out</item>
</style>

③預設縮放動畫

<style name="dialog_scale_anim">
    <item name="android:windowEnterAnimation">@anim/dialog_scale_anim_in</item>
    <item name="android:windowExitAnimation">@anim/dialog_scale_anim_out</item>
</style>

dialog_from_bottom_anim_in:

<set xmlns:android="http://schemas.android.com/apk/res/android">
    <translate
android:duration="400"
android:fromXDelta="0"
android:fromYDelta="1000"
android:toYDelta="0"
android:toXDelta="0" />
</set>
dialog_from_bottom_anim_out:
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
    <translate
android:duration="400"
android:fromXDelta="0"
android:fromYDelta="0"
android:toYDelta="1000"/>
</set>

dialog_scale_anim_in:

<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android" >
    <scale
android:duration="135"
android:fromXScale="0.8"
android:fromYScale="0.8"
android:pivotX="50%"
android:pivotY="50%"
android:toXScale="1.05"
android:toYScale="1.05" />
    <scale
android:duration="105"
android:fromXScale="1.05"
android:fromYScale="1.05"
android:pivotX="50%"
android:pivotY="50%"
android:startOffset="135"
android:toXScale="0.95"
android:toYScale="0.95" />
    <scale
android:duration="60"
android:fromXScale="0.95"
android:fromYScale="0.95"
android:pivotX="50%"
android:pivotY="50%"
android:startOffset="240"
android:toXScale="1.0"
android:toYScale="1.0" />
    <alpha
android:duration="90"
android:fromAlpha="0.0"
android:interpolator="@android:anim/accelerate_interpolator"
android:toAlpha="1.0" />
</set>
dialog_scale_anim_out:
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android" >
    <scale
android:duration="150"
android:fromXScale="1.0"
android:fromYScale="1.0"
android:pivotX="50%"
android:pivotY="50%"
android:toXScale="0.6"
android:toYScale="0.6" />
    <alpha
android:duration="150"
android:fromAlpha="1.0"
android:interpolator="@android:anim/accelerate_interpolator"
android:toAlpha="0.0" /> 
</set>

dialog_test:

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/root_view"
android:layout_width="fill_parent"
android:layout_height="wrap_content">
    <LinearLayout
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:background="@color/comment_dialog_bg"
android:orientation="vertical"
android:padding="10.0dip">
        <EditText
android:id="@+id/comment_editor"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:background="@drawable/bg_detail_comment_editor"
android:hint="@string/ss_share_hint"
android:maxHeight="120.0dip"
android:padding="9.0dip"
android:textColor="@color/comment_dialog_content_text"
android:textSize="16.0sp" />
        <LinearLayout
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_marginTop="10.0dip"
android:gravity="center_vertical">
            <TextView
android:id="@+id/share_label"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:layout_marginRight="10.0dip"
android:text="@string/comment_dialog_share_label"
android:textColor="@color/comment_dialog_share_text"
android:textSize="13.0sp" />
            <LinearLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1">
                <ImageView
android:id="@+id/account_icon_weibo"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/account_icon_weibo" />
                <ImageView
android:id="@+id/account_icon_tencent"
android:layout_width="wrap_content"
android:layout_marginLeft="10dp"
android:layout_height="wrap_content"
android:src="@drawable/account_icon_tencent" />
            </LinearLayout>
            <LinearLayout
android:id="@+id/platform_layout"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_toRightOf="@id/share_label"
android:orientation="horizontal" />
            <TextView
android:id="@+id/submit_btn"
android:layout_width="50.0dip"
android:layout_height="25.0dip"
android:layout_alignParentRight="true"
android:layout_centerVertical="true"
android:background="@drawable/bg_comment_submit"
android:gravity="center"
android:text="@string/comment_dialog_send"
android:textColor="@color/comment_dialog_submit_text"
android:textSize="13.0sp" />
            <TextView
android:id="@+id/text_limit"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:layout_marginRight="10.0dip"
android:layout_toLeftOf="@id/submit_btn"
android:textSize="13.0sp"
android:visibility="invisible" />
        </LinearLayout>
    </LinearLayout>
</FrameLayout>

使用很簡單:
findViewById(R.id.ceshi).setOnClickListener(new View.OnClickListener() {
    @Override
public void onClick(View v) {
        AlertDialog dialog = new AlertDialog.Builder(MainActivity.this)
                .setContentView(R.layout.dialog_test)
                .fullWidth()
                .fromButtom(true)
                .show();
dialog.setOnClickListener(R.id.send, new View.OnClickListener() {
            @Override
public void onClick(View v) {
                Toast.makeText(MainActivity.this, "點選了傳送", Toast.LENGTH_SHORT).show();
}
        });
}
});

效果圖如下: