自己做個Android框架-Dialog

代碼地址:

TJFramework - -TJDialog.java

文章索引

  1. List item

序言

任何封裝都不是一蹴而就的,需要有一定的時間和經驗的積累,去挖掘深層次的需求。我看過很多人的封裝,有一種風格是在一個大的封裝裏集成了各種各樣的彈窗,是或否彈窗-進度條彈窗-加載彈窗-等等等,但後來我發現,面對日益多變的設計需求,任何指定UI的封裝都是活不不長久的,所以我從一開始做,就是打算讓每一個彈窗的創建都能自由化、簡單化,能真正對得起Base角色。

彈窗需求設計

  • 簡單、快捷、一目瞭然的調用邏輯
  • 支持自定義頁面
  • 支持彈窗動畫自定義。
  • 支持自動響應關閉、取消、確認等通用事件
  • 支持統一化、能面對任何UI的事件回調機制
  • 支持內存回收,處理內存泄漏問題

彈窗開做

構造方法

這裏的設計是,全程通過佈局文件來初始化彈窗,需要什麼UI,直接新建一個佈局就行了。至於怎麼自動響應關閉、取消、確認等通用事件,我這裏用的是通過在佈局內指定特定的id,即可添加相關的功能。裏入你需要確認按鈕和取消按鈕,只需要分別設置fw_dialog_win_bt_continue和fw_dialog_win_bt_cancel爲其id即可。

public TJDialog(@NonNull Context context, @LayoutRes int layoutId)
public TJDialog(@NonNull Context context, @LayoutRes int layoutId,@StyleRes int styleId)
public TJDialog(@NonNull Context context, @LayoutRes int layoutId, int width, int height)
public TJDialog(@NonNull Context context, @LayoutRes int layoutId, int width, int height,int styleId)
{
	super(context,styleId);
    this.width = width;//支持WRAP_CONTENT、MATCH_PARENT、自定義大小,用於後面設置彈窗的整體寬高
    this.height = height;
    //通過制定的id來尋找對應的組件,所以如果需要一個關閉按鈕,則只需要
    VIEW_WIN_BG_ID = getViewWinBgId();//通過方法獲取id,如有需要可以重寫該方法二次封裝,改變其中默認的id。
    VIEW_WIN_BOX_ID = getViewWinBoxId();
    VIEW_WIN_TITLE_ID = getViewWinTitleId();
    VIEW_WIN_BT_CONTINUE_ID = getViewWinBtContinue();
    VIEW_WIN_BT_CANCEL_ID = getViewWinBtCancel();
    VIEW_WIN_BT_CLOSE_ID = getViewWinBtClose();
    baseView = LayoutInflater.from(context).inflate(layoutId, null);
    setContentView(baseView);
    initView();//初始化組件,設置點擊事件
}    
private void initView()
    {
    	//攔截兩個監聽事件,然後做中轉
        super.setOnShowListener(dialog -> {
            if(onShowListener!=null) {
                onShowListener.onShow(dialog);
            }
            //統一事件監聽器,支持若干個常用事件監聽,和所有自定義按鈕的點擊事件監聽
            if(onTJDialogListener!=null) {
                onTJDialogListener.onShow(dialog,state);
            }
        });
        super.setOnDismissListener(dialog -> {
            if(onDismissListener!=null) {
                onDismissListener.onDismiss(dialog);
            }
            //統一事件監聽器,支持若干個常用事件監聽,和所有自定義按鈕的點擊事件監聽
            if(onTJDialogListener!=null) {
                onTJDialogListener.onDismiss(dialog,state);
            }
        });
        //初始化組件
        winBgView = baseView.findViewById(VIEW_WIN_BG_ID);
        winBoxView = baseView.findViewById(VIEW_WIN_BOX_ID);
        winTitleView = baseView.findViewById(VIEW_WIN_TITLE_ID);
        winBtContinue = baseView.findViewById(VIEW_WIN_BT_CONTINUE_ID);
        winBtCancel = baseView.findViewById(VIEW_WIN_BT_CANCEL_ID);
        winBtClose = baseView.findViewById(VIEW_WIN_BT_CLOSE_ID);
        //初始化點擊事件
        setOnClickListener(winBgView);
        setOnClickListener(winBoxView);
        setOnClickListener(winBtContinue);
        setOnClickListener(winBtCancel);
        setOnClickListener(winBtClose);
        //這個方法是抽象方法,需要重寫設置你想要監聽其點擊事件的組件
        int[] ids = onInitClick();
        if(ids!=null)
        {
            for(int id:ids)
            {
                View view = baseView.findViewById(id);
                setOnClickListener(view);
            }
        }
        //抽象方法,用戶可以重寫初始化自己的組件
        onInitView(baseView);
    }

show()和dismiss()

@Override
    public void show() {
        if(!isShow)//防止重複顯示
        {
            baseView.setVisibility(View.VISIBLE);
            if(winBgView!=null&&isShowWinBgAnim)
            {
            	//顯示背景動畫,可自定義,當然默認的動畫也很美了
                if(animationWinBgEnter==null) {
                    animationWinBgEnter = AnimationUtils.loadAnimation(getContext(), windowAnimEnterId);
                } else {
                    animationWinBgEnter.reset();
                }
                winBgView.startAnimation(animationWinBgEnter);
            }
            if (winBoxView != null&&isShowWinBoxAnim) {
            	//顯示窗體動畫,可自定義,當然默認的動畫也很美了
                if(animationWinBoxEnter==null) {
                    animationWinBoxEnter = AnimationUtils.loadAnimation(getContext(), contentAnimEnterId);
                } else {
                    animationWinBoxEnter.reset();
                }
                winBoxView.startAnimation(animationWinBoxEnter);
            }
            Window window = getWindow();
            if(window!=null)
            {
                window.setWindowAnimations(-1);
            }
            //攔截一些意外的報錯(popupWindows繼承過來的做法)
            try {
                super.show();
                isShow = true;
            }catch (Exception e){
                LogUtil.exception(e);
            }
            //設置彈窗動畫
            WindowManager.LayoutParams layoutParams = getWindow().getAttributes();
            if (layoutParams != null) {
                layoutParams.width = width;
                layoutParams.height = height;
            }
            this.getWindow().setAttributes(layoutParams);
        }
    }
@Override
    public void dismiss() {
        if(isShow)
        {
            isShow = false;
            if(winBoxView!=null&&isShowWinBoxAnim)
            {
            	//執行窗體關閉動畫-默認的是漸變消失
                if(animationWinBoxExit==null) {
                    animationWinBoxExit = AnimationUtils.loadAnimation(getContext(), contentAnimExitId);
                } else {
                    animationWinBoxExit.reset();
                }
                winBoxView.startAnimation(animationWinBoxExit);
            }
            if(winBgView!=null)
            {
            	//執行背景關閉動畫-默認的是漸變消失
                if(animationWinBgExit==null) {
                    animationWinBgExit = AnimationUtils.loadAnimation(getContext(), windowAnimExitId);
                } else {
                    animationWinBgExit.reset();
                }
                winBgView.startAnimation(animationWinBgExit);
                animationWinBgExit.setAnimationListener(new Animation.AnimationListener() {
                    @Override
                    public void onAnimationStart(Animation animation) {

                    }

                    @Override
                    public void onAnimationEnd(Animation animation) {
                    	//在動畫執行完成之後dismiss彈窗。請放心,並不會造成內存泄漏。
                        TJDialog.super.dismiss();
                        baseView.setVisibility(View.INVISIBLE);
                    }

                    @Override
                    public void onAnimationRepeat(Animation animation) {

                    }
                });
            }
            else
            {
                super.dismiss();
            }
        }
    }

destroy()

這裏回收了所有的對象,完全不存在任何內存泄漏的可能。

@Override
    public void destroy() {
        setOnDismissListener(null);
        setOnShowListener(null);
        setOnTJDialogListener(null);
        super.setOnDismissListener(null);
        super.setOnShowListener(null);
        onShowListener = null;
        onDismissListener = null;
        baseHandler.removeMessages(DISMISS);
        baseHandler.removeCallbacksAndMessages(null);
        baseView = null;
        winBgView = null;
        winBoxView= null;
        winTitleView= null;
        winBtContinue= null;
        winBtCancel= null;
        winBtClose= null;
    }

整個類492行代碼,但主體代碼並不多,就上面這些。其實整個封裝的邏輯是很清晰的,並不複雜,但這樣之後確實爲我去掉了很多彈窗調用的煩惱。而且如果有需要的話,二次封裝一些通用的彈窗也是可以的。

個人知識有限,不敢託大,如有遺漏缺陷的地方,歡迎大家指正。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章