AndroidソースコードのAlertDialog分析

8976 ワード

前言:Androidソースで最もよく使われるBuilderモードは、AlertDialogです.複雑なAlertDialogオブジェクトを構築するためにBuilderを使用します.
まず、使い方を見てみましょう.
    AlertDialog.Builder builder = new AlertDialog.Builder(this);
    builder.setTitle("Title").setMessage("Message").setIcon(R.mipmap.ic_launcher).create().show();
この使用方法から見ると明らかにBuilderモードであり、内部の実装の詳細を外部に隠し、Dialogの各構成部分を
ビルダーで組み立てる.AlertDialogの関連ソースコードを見てみましょう.
public class AlertDialog extends AppCompatDialog implements DialogInterface {
		//  Builder    P    
	    private AlertController mAlert;
		
		protected AlertDialog(Context context) {
			this(context, resolveDialogTheme(context, 0), true);
		}
		
		protected AlertDialog(Context context, boolean cancelable, OnCancelListener cancelListener) {
			super(context, resolveDialogTheme(context, 0));
			setCancelable(cancelable);
			setOnCancelListener(cancelListener);
			mAlert = new AlertController(context, this, getWindow());
		}
	
	    @Override
		public void setTitle(CharSequence title) {
			super.setTitle(title);
			mAlert.setTitle(title);
		}
		 public void setCustomTitle(View customTitleView) {
			mAlert.setCustomTitle(customTitleView);
		}
	
		public void setView(View view) {
			mAlert.setView(view);
		}
		
		void setButtonPanelLayoutHint(int layoutHint) {
			mAlert.setButtonPanelLayoutHint(layoutHint);
		}
		......        Set    。
		
		
		public static class Builder {
			//  Dialog        , :title,Message 
			private final AlertController.AlertParams P;
		
			//   Builder  ,  Builder   ,         P 
			Builder setTitle(CharSequence title) {
				P.mTitle = title;
				return this;
			}
			
		    public Builder setCustomTitle(View customTitleView) {
				P.mCustomTitleView = customTitleView;
				return this;
			}
		
		    public Builder setMessage(int messageId) {
				P.mMessage = P.mContext.getText(messageId);
				return this;
			}
			
			//  AlertDialog     
			public AlertDialog create() {
				//     AlertDialog  
				final AlertDialog dialog = new AlertDialog(P.mContext, mTheme, false);
				// P      Dialog.mAlert   ,          mAlert  。
				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;
			}
		}
	}

上のコードでは、BuilderクラスでAlertDialogのレイアウトパラメータ、例えばtitle、messageなどを設定し、これらのパラメータを存在させます.
AlertParamsでは、AlertParamsには多くのDialogのレイアウトパラメータが含まれています.create()メソッドを呼び出すと、インスタンスが新しい
AlertDialogは、Builderメンバー変数のパラメータをapplyメソッドでAlerDialogのalertオブジェクトに適用します.私たちが
AlertDialogオブジェクトを取得すると、show関数でこのダイアログボックスが表示されます.
Builderのショー方法を見てみましょう.
public AlertDialog show() {
            AlertDialog dialog = create();
            dialog.show();
            return dialog;
    }
	      AlertDialog  show()  :
	public void show() {
        //    
		if (mShowing) {
            if (mDecor != null) {
                if (mWindow.hasFeature(Window.FEATURE_ACTION_BAR)) {
                    mWindow.invalidatePanelMenu(Window.FEATURE_ACTION_BAR);
                }
                mDecor.setVisibility(View.VISIBLE);
            }
            return;
        }

        mCanceled = false;
        //1.    ,   Dialog OnCreate  
        if (!mCreated) {
            dispatchOnCreate(null);
        }
		//2.  Dialog onStart()  
        onStart();
		//        
        mDecor = mWindow.getDecorView();

        if (mActionBar == null && mWindow.hasFeature(Window.FEATURE_ACTION_BAR)) {
            final ApplicationInfo info = mContext.getApplicationInfo();
            mWindow.setDefaultIcon(info.icon);
            mWindow.setDefaultLogo(info.logo);
            mActionBar = new WindowDecorActionBar(this);
        }

        WindowManager.LayoutParams l = mWindow.getAttributes();
        if ((l.softInputMode & WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION) == 0) {
            WindowManager.LayoutParams nl = new WindowManager.LayoutParams();
            nl.copyFrom(l);//       ,      
            nl.softInputMode |= WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION;
            l = nl;
        }

        try {
			// mDecor   WindowManager 
            mWindowManager.addView(mDecor, l);
            mShowing = true;
			//      Dialog   
            sendShowMessage();
        } finally {
        }
    }

show関数では主に以下のことをしました.
(1)dispatchOnCreate関数でAlertDialogのonCreate関数を呼び出す
(2)次にonStart関数を呼び出す
(3)最後にDialogのmDecorをWindowManagerに追加します(ここで一番上のレイアウトを取得する理由が少し分かりません)
したがって、ビューの作成は必ずonCreate()メソッド://DialogのonCreateメソッドは空の実装であるため、コールバックはサブクラスAlertDialogのonCreateメソッドである.
	@Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        mAlert.installContent();
    }
	
	public void installContent() {
		//         
		final int contentView = selectContentView();
        mDialog.setContentView(contentView);
        setupView();
    }
	
	private void setupView() {
		//  Window   setContentView,       Window   setContentView  
		//     AlertController          ,     :alert_dialog.xml
        final View parentPanel = mWindow.findViewById(R.id.parentPanel);
        final View defaultTopPanel = parentPanel.findViewById(R.id.topPanel);
        final View defaultContentPanel = parentPanel.findViewById(R.id.contentPanel);
        final View defaultButtonPanel = parentPanel.findViewById(R.id.buttonPanel);

        // Install custom content before setting up the title or buttons so
        // that we can handle panel overrides.
		//       
        final ViewGroup customPanel = (ViewGroup) parentPanel.findViewById(R.id.customPanel);
        setupCustomContent(customPanel);

        final View customTopPanel = customPanel.findViewById(R.id.topPanel);
        final View customContentPanel = customPanel.findViewById(R.id.contentPanel);
        final View customButtonPanel = customPanel.findViewById(R.id.buttonPanel);

        // Resolve the correct panels and remove the defaults, if needed.
        final ViewGroup topPanel = resolvePanel(customTopPanel, defaultTopPanel);
        final ViewGroup contentPanel = resolvePanel(customContentPanel, defaultContentPanel);
        final ViewGroup buttonPanel = resolvePanel(customButtonPanel, defaultButtonPanel);
		//     
        setupContent(contentPanel);
		//     
        setupButtons(buttonPanel);
		//     
        setupTitle(topPanel);
		//         ,               
        final boolean hasCustomPanel = customPanel != null
                && customPanel.getVisibility() != View.GONE;
        final boolean hasTopPanel = topPanel != null
                && topPanel.getVisibility() != View.GONE;
        final boolean hasButtonPanel = buttonPanel != null
                && buttonPanel.getVisibility() != View.GONE;

        // Only display the text spacer if we don't have buttons.
        if (!hasButtonPanel) {
            if (contentPanel != null) {
                final View spacer = contentPanel.findViewById(R.id.textSpacerNoButtons);
                if (spacer != null) {
                    spacer.setVisibility(View.VISIBLE);
                }
            }
        }

        if (hasTopPanel) {
            // Only clip scrolling content to padding if we have a title.
            if (mScrollView != null) {
                mScrollView.setClipToPadding(true);
            }
        }

        // Update scroll indicators as needed.
        if (!hasCustomPanel) {
            final View content = mListView != null ? mListView : mScrollView;
            if (content != null) {
                final int indicators = (hasTopPanel ? ViewCompat.SCROLL_INDICATOR_TOP : 0)
                        | (hasButtonPanel ? ViewCompat.SCROLL_INDICATOR_BOTTOM : 0);
                setScrollIndicators(contentPanel, content, indicators,
                        ViewCompat.SCROLL_INDICATOR_TOP | ViewCompat.SCROLL_INDICATOR_BOTTOM);
            }
        }
		//   listview    
        final ListView listView = mListView;
        if (listView != null && mAdapter != null) {
            listView.setAdapter(mAdapter);
            final int checkedItem = mCheckedItem;
            if (checkedItem > -1) {
                listView.setItemChecked(checkedItem, true);
                listView.setSelection(checkedItem);
            }
        }
    }

setupViewの役割は、AlertDialogレイアウトの各部分を初期化し、この関数を呼び出すとDialog全体のビューコンテンツが設定されます.これらの領域のビューはmAlertDialogLayoutのサブ要素に属し、Windowオブジェクトはレイアウトツリー全体に関連付けられています.setupViewを呼び出した後、ビューツリー全体のデータが埋め込まれ、show関数を呼び出すと、WindowManagerはWindowオブジェクトのDecorViewをユーザーウィンドウに追加して表示します.まとめ:AlerDialogのBuilderモードではDirectorロールは現れず、正式にはそのためプログラム全体が簡潔になった.デザインモデルは一種の思想であり、強引なカバーを生で運ぶべきではなく、柔軟に使用しなければならない.参考書:『Androidソースデザインモデル解析と実戦』