Android中Dialog中的Window添加过程解析

Android中Dialog真的只能依赖Activity?Dialog一直作为一种依附在Activity上下文才能存在的窗口视图,那是否可以逃脱Activity的上下文,采用其他Context存在呢?答案是肯定的,Dialog完全可以不依赖Activity上下文存在,这里我们说的只是非Activity的Context,不是不依赖ContextDialog的组成:Dialog是一种承载W...
摘要由CSDN通过智能技术生成
Dialog中的Window添加过程解析

Dialog一直作为一种依附在Activity上下文才能存在的窗口视图,那是否可以逃脱Activity的上下文,采用其他Context存在呢?答案是肯定的,Dialog完全可以不依赖Activity上下文存在,这里我们说的只是非Activity的Context,不是不依赖Context

  • Dialog的组成:
    Dialog是一种承载Window的容器,而Window的唯一实现便是PhoneWindow,Dialog的setContentView就是将布局文件的id传给PhoneWindow,PhoneWindow通过该布局id解析然后创建一个DecorView,这是一个继承FrameLayout的ViewGroup,每个Window都有一个WindowManagerImpl,这里所说的是每个非子window类型的window,因为子window是依附于父window,父子共用一个WindowManagerImpl,普通的Dialog的WindowManagerImpl与Activity是共用的
  • Dialog的创建代码:

    Dialog(@NonNull Context context, @StyleRes int themeResId, boolean createContextThemeWrapper) {
        if (createContextThemeWrapper) {
            if (themeResId == ResourceId.ID_NULL) {
                final TypedValue outValue = new TypedValue();
                context.getTheme().resolveAttribute(R.attr.dialogTheme, outValue, true);
                themeResId = outValue.resourceId;
            }
            mContext = new ContextThemeWrapper(context, themeResId);
        } else {
            mContext = context;
        }

        mWindowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE); // 获取windowmanager
 
        final Window w = new PhoneWindow(mContext);
        mWindow = w;
        w.setCallback(this);
        w.setOnWindowDismissedCallback(this);
        w.setOnWindowSwipeDismissedCallback(() -> {
            if (mCancelable) {
                cancel();
            }
        });
        w.setWindowManager(mWindowManager, null, null);  // phonewindow 与 Windowmanager绑定
        w.setGravity(Gravity.CENTER);

        mListenersHandler = new ListenersHandler(this);
    }

再来看一下setContentView代码:

   Dialog.java
    public void setContentView(@LayoutRes int layoutResID) {
        mWindow.setContentView(layoutResID);
    }
PhoneWindow.java
public void setContentView(int layoutResID) {
        if (mContentParent == null) {
            installDecor();  // 创建DecorView与PhoneWindow绑定
        } else if (!hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
            mContentParent.removeAllViews();
        }
        if (hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
            final Scene newScene = Scene.getSceneForLayout(mContentParent, layoutResID,
                    getContext());
            transitionTo(newScene);
        } else {
            mLayoutInflater.inflate(layoutResID, mContentParent);
        }
        mContentParent.requestApplyInsets();
        final Callback cb = getCallback();
        if (cb != null && !isDestroyed()) {
            cb.onContentChanged();
        }
        mContentParentExplicitlySet = true;
    }

以上也说明Dialog中视图的构建过程,其实就是Dialog持有一个Context、phonewindow、windowmanager,其中Context如果是Activity的话,因为Activity是集成ContextWrapTheme类,所以由Activity上下文构建的Dialog是和Activity的主题一样的。phonewindow持有视图解析后的View结构树DecorView,而WindowManager,如果是子窗口类型,便使用父窗口的windowmanager,如果是系统窗口,将独立创建一个windowmanager,其实每个WindowManager也是一个傀儡,真正执行View操作的的事WindowManagerGlobal,这个每个Application只有一个该单例对象,管理当前app的所有window以及view的跟新操作,可以看一下为什么WindowManager是个傀儡,这个类代码就几十行:

public final class WindowManagerImpl implements WindowManager {
    private final WindowManagerGlobal mGlobal = WindowManagerGlobal.getInstance();
    private final Context mContext;
    private final Window mParentWindow;

    private IBinder mDefaultToken;

    public WindowManagerImpl(Context context) {
        this(context, null);
    }

    private WindowManagerImpl(Context context, Window parentWindow) {
        mContext = context;
        mParentWindow = parentWindow;
    }

    public WindowManagerImpl createLocalWindowManager(Window parentWindow) {
        return new WindowManagerImpl(mContext, parentWindow);
    }

    public WindowManagerImpl createPresentationWindowManager(Context displayContext) {
        return new WindowManagerImpl(displayContext, mParentWindow);
    }

    /**
     * Sets the window token to assign when none is specified by the client or
     * available from the parent window.
     *
     * @param token The default token to assign.
     */
    public void setDefaultToken(IBinder token) {
        mDefaultToken = token;
    }

    @Override
    public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
        applyDefaultToken(params);
        mGlobal.addView(view, params, mContext.getDisplay(), mParentWindow);
    }

    @Override
    public void updateViewLayout(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
        applyDefaultToken(params);
        mGlobal.updateViewLayout(view, params);
    }

    private void applyDefaultToken(@NonNull ViewGroup.LayoutParams params) {
        // Only use the default token if we don't have a parent window.
        if (mDefaultToken != null && mParentWindow == null) {
            if (!(params instanceof WindowManager.LayoutParams)) {
                throw new IllegalArgumentException("Params must be WindowManager.LayoutParams");
            }

            // Only use the default token if we don't already have
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Android,可以通过点击视图获取Dialog的位置信息并对其进行定位。可以使用getFragmentCenterLocationOnScreen()方法来获取Dialog的位置信息。该方法通过获取View的位置信息,计算得到Dialog心位置坐标,并返回一个包含位置信息的Rect对象。 具体实现可以参考以下代码: ``` private Rect getFragmentCenterLocationOnScreen() { int[] location = new int = location + fragment_width/2; location = location + fragment_height/2; rect = new Rect(location + fragment_width, location + fragment_height); return rect; } ``` 这样,通过调用getFragmentCenterLocationOnScreen()方法,可以获取到Dialog的位置信息,并将其用于定位Dialog的位置。 请注意,这只是一种实现的思路,具体的实现方式还需要根据具体的需求和代码结构进行相应的调整和修改。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* [Android-随意变化的气泡布局Dialog根据点击View的位置定位它的位置Dialog可定制方向等](https://download.csdn.net/download/weixin_39840387/11527304)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 33.333333333333336%"] - *2* [Android里设置Dialog位置](https://blog.csdn.net/wujiang_android/article/details/101031471)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 33.333333333333336%"] - *3* [Android自动化测试—操作对话框(Dialog)上面的控件](https://download.csdn.net/download/weixin_38597970/16056345)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 33.333333333333336%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值