新浦京81707con > 注册购买 > 调用私有的,Android的dialog不能横向铺满全屏问题

原标题:调用私有的,Android的dialog不能横向铺满全屏问题

浏览次数:52 时间:2020-03-13

眼下在做三个享用分界面,效果如下。

此情此景:项目中某种原因 Application 应用的宗旨是 android:Theme.Light ,而独立某些页面用的是 Theme.AppCompat 。
案例:主 A 页面大旨 Theme.Light , B 页面宗旨 Theme.AppCompat 。将来两处都会弹出 AlertDailog 框

Dialog是APP开垦中常用的控件,同Activity相符,具备独立的Window窗口,不过Dialog跟Activity依旧有一定差距的,最明显的正是:私下认可情状下Dialog不是全屏的,所以构造达成比不上Activity舒性格很顽强在险阻艰难或巨大压力面前不屈,举例顶上部分对齐,尾巴部分对齐、边距、宽度、中度等。假诺将Dialog定义成全屏的就能够省去过多标题,能够完全依照常用的构造格局来拍卖。网络完成方式有为数不菲,平时情形下也都能奏效,但是大概会有大多困惑,举例:为何有些窗口属性(隐蔽标题)必必要在setContentView早先设置才有效,相反,也某些属性(全屏)要在其后设置才有效。这里挑多少个轻易的达成格局,然后说下原因,由于Android的窗口管理以致View绘制是挺大的一块,这里但是多少深度入。先看落时间效益果与利益:

图片 1效果图.png

图片 2

图片 3

因为在相当多地点都急需用到这几个分享的机能,所以小编想,把这么些效应用三个自定义Dialog封装起来,并将享受的图景通过接口重返给调用者。功效完毕比较轻巧,就不赘述了。此处首要为了写一下,在促成进度中碰着的坑:小编开掘即便将contentView的width属性设置成match_parent,当运营看效能时,也不可能将Dialog横向铺满显示屏

未统一在此以前

全屏Dialog

在郁结非常久,消亡构造文件的难点后。我发觉Dialog是有暗中认可样式的。这里贴出Dialog的结构方法以供参照他事他说加以考察。

贰个施用弹出框两套样式断定非常。
那正是说大家该选用注解方案解决吧?
前提条件:页面 B 代码实现与 theme 核心强耦合
方案1:重写页面 B 改用 Theme.Light 核心
方案2:仿写 Theme.Light 系统机能
方案3:页面 B AlertDailog 选用 Theme.Light 的弹出框样式
简单来说考虑方案1不但职业量大而且还恐怕会损坏原始代码,或者会引入新主题素材。方案2增加产能专业量。方案3工作量小,可行性未知。

全屏Dialog完毕格局

此处对象分为三种,一种是针对古板的Dialog,另一种是照准DialogFragment(推荐),方法也分为三种一种是采替代码实现,另一种是运用大旨样式Theme来促成。

本着Dialog的贯彻况势

public class FullScrreenDialog extends Dialog {
    public FullScrreenDialog(Context context) {
        super(context);
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        <!--关键点1-->
        getWindow().requestFeature(Window.FEATURE_NO_TITLE);
        View view = LayoutInflater.from(getContext()).inflate(R.layout.fragment_full_screen, null);
        <!--关键点2-->
        setContentView(view);
        <!--关键点3-->
        getWindow().setBackgroundDrawable(new ColorDrawable(0x00000000));
        <!--关键点4-->
        getWindow().setLayout(WindowManager.LayoutParams.MATCH_PARENT, WindowManager.LayoutParams.MATCH_PARENT);
    }
}

此间牵扯到多少个点,关键点1要在setContentView在此之前安装,重假设为了合作一些低版本的,不让展现Title部分,关键点2正是常用的setContentView,关键点3根4正是为了全屏对话框做的改过,关键点4必须要放在setContent的末尾,因为只要身处setContent该属性会被冲掉无效,原因再前面说。即便您想封装多少个统一的全屏Dialog,那可以吧关键点1放在构造方法中,把关键点3与4位居onStart中,其实正是至关心珍视即使保证setContentView的试行各种,

public class FullScreenDialog extends Dialog {
    public FullScreenDialog(Context context) {
        super(context);
        getWindow().requestFeature(Window.FEATURE_NO_TITLE);
    }

    @Override
    protected void onStart() {
        getWindow().setBackgroundDrawable(new ColorDrawable(0x00000000));
        getWindow().setLayout(WindowManager.LayoutParams.MATCH_PARENT, WindowManager.LayoutParams.MATCH_PARENT);
    }
}

未来再看下DialogFragment的做法。

针对DialogFragment的兑现方式

Android比较推荐应用DialogFragment实现对话框,它完全能够达成Dialog的具备必要,况且还是能复用Fragment的生命周期管理,被后台杀死后还是能够自动还原。其促成全屏的原理同Dialog相似,只不过是机遇的把握

public class FullScreen DialogFragment extends DialogFragment {

    @Nullable
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        return inflater.inflate(R.layout.fragment_full_screen, container, false);
    }

    @Override
    public void onActivityCreated(Bundle savedInstanceState) {
    <!--关键点1-->
        getDialog().getWindow().requestFeature(Window.FEATURE_NO_TITLE);
        super.onActivityCreated(savedInstanceState);
    <!--关键点2-->
        getDialog().getWindow().setBackgroundDrawable(new ColorDrawable(0x00000000));
        getDialog().getWindow().setLayout(WindowManager.LayoutParams.MATCH_PARENT, WindowManager.LayoutParams.MATCH_PARENT);
    }

}

先看下这里怎么放在onActivityCreated中管理,借使微微跟下DialogFragment的完结源码就能够发觉,其setContentView的火候是在onActivityCreated,看如下代码关键点1

@Override
public void onActivityCreated(Bundle savedInstanceState) {
    super.onActivityCreated(savedInstanceState);
    if (!mShowsDialog) {
        return;
    }
    View view = getView();
    if (view != null) {
        if (view.getParent() != null) {
            throw new IllegalStateException("DialogFragment can not be attached to a container view");
        }
        <!--关键点1-->
        mDialog.setContentView(view);
    }
    ...
}

自然,也全然能够参照他事他说加以考查基类Dialog的落实方式,实际上海重机厂要正是把握 setContentView的调用机会。之后来看第三种方案,利用Theme来兑现。

采纳Theme大旨来促成全拼对话框

先是步在style中定义全屏Dialog样式

<style name="Dialog.FullScreen" parent="Theme.AppCompat.Dialog">
    <item name="android:windowNoTitle">true</item>
    <item name="android:windowBackground">@color/transparent</item>
    <item name="android:windowIsFloating">false</item>
</style>

第二步:设置样式,以DialogFragment为例,只需求在onCreate中setStyle(STYLE_NORMAL, R.style.Dialog_FullScreen卡塔尔就能够。(推荐应用DialogFragment,它复用了Fragment的扬言周期,被杀死后,能够还原重新建立)

public class FragmentFullScreen extends DialogFragment {

    @Override
    public void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setStyle(STYLE_NORMAL, R.style.Dialog_FullScreen);
    }
}

假使是在Dialog中,设置如下代码就可以。

public class FullScreenDialog extends Dialog {
    public FullScreenDialog(Context context) {
        super(context);
        getWindow().requestFeature(Window.FEATURE_NO_TITLE);
      }
}

其实纯代码的坚决守护跟那八个属性对应,那么那四本性情究竟有怎么着效劳,设置的机会为啥又有约束,上边就回顾剖判一下缘故。

Dialog(Context context, int theme, boolean createContextThemeWrapper) { if (createContextThemeWrapper) { if (theme == 0) { TypedValue outValue = new TypedValue(); context.getTheme().resolveAttribute(com.android.internal.R.attr.dialogTheme, outValue, true); theme = outValue.resourceId; } mContext = new ContextThemeWrapper(context, theme); } else { mContext = context; } mWindowManager = (WindowManager)context.getSystemService(Context.WINDOW_SERVICE); Window w = PolicyManager.makeNewWindow; mWindow = w; w.setCallback; w.setOnWindowDismissedCallback; w.setWindowManager(mWindowManager, null, null); w.setGravity(Gravity.CENTER); mListenersHandler = new ListenersHandler; }

用作爱好挑衅的工程师当然采取方案3,大家来理清楚思路。
1.找到 Theme.Light 主题下 AlertDailog 引用的 style
2.在 AlertDailog.builder 创制的时候钦定 style ( Theme 卡塔尔国

全屏Dialog实现原理

针对以下八个属性一步步拆解深入分析。

    <item name="android:windowIsFloating">false</item>
    <item name="android:windowBackground">@color/transparent</item>
    <item name="android:windowNoTitle">true</item>

率先看下第三个属性,android:windowIsFloating,这些性格恐怕是Activity暗许样式同Dialog最大的分别之一,相比一下暗许的Dialog大旨与Activity大旨,两个都以继续Theme,在Theme中

Theme

    <style name="Theme">
            ...
         <item name="windowIsFloating">false</item>
    </style>

只是Dialog的相通都進展了覆盖,而Activity暗许未有覆盖windowIsFloating属性

Base.V7.Theme.AppCompat.Dialog

<style name="Base.V7.Theme.AppCompat.Dialog" parent="Base.Theme.AppCompat">
    ...
    <item name="android:windowIsFloating">true</item>
</style>

也正是说Activity接纳了暗中同意的 <item name="windowIsFloating">false</item>,而Dialog的相同是True,这两侧在开创Window的时候有如何分别吧?步向PhoneWindow.java中,当Window在第二遍创制DecorView的时候是急需依照该属性去创制顶层结构参数的,也正是RootMeasureSpec,Window被新建的时候,WindowManager.LayoutParams默许使用的是MATCH_PARENT,不过如若windowIsFloating 棉被服装置为True,WindowManager.LayoutParams参数中的尺寸就可以被安装成WRAP_CONTENT,具体源码如下:

protected ViewGroup generateLayout(DecorView decor) {
    // Apply data from current theme.
    TypedArray a = getWindowStyle();
    mIsFloating = a.getBoolean(com.android.internal.R.styleable.Window_windowIsFloating, false);
    int flagsToUpdate = (FLAG_LAYOUT_IN_SCREEN|FLAG_LAYOUT_INSET_DECOR)
            & (~getForcedWindowFlags());
     <!--关键点1-->
    if (mIsFloating) {
        setLayout(WRAP_CONTENT, WRAP_CONTENT);
        setFlags(0, flagsToUpdate);
    } else {
        setFlags(FLAG_LAYOUT_IN_SCREEN|FLAG_LAYOUT_INSET_DECOR, flagsToUpdate);
    }
    ...       
}

从关键点1可以看看,即便windowIsFloating被布置为true,就能够透过setLayout(WRAP_CONTENT, WRAP_CONTENT卡塔尔将Window的窗口属性WindowManager.LayoutParams设置为WRAP_CONTENT,这些个性对于根结构MeasureSpec参数的变型起着关键成效

public void setLayout(int width, int height) {
    final WindowManager.LayoutParams attrs = getAttributes();
    attrs.width = width;
    attrs.height = height;
    if (mCallback != null) {
        mCallback.onWindowAttributesChanged(attrs);
    }
}

有关怎么要在setContentView之后设置参数,是因为generateLayout平时是经过setContentView调用的,所以即便提前设置了压根没效果,PhoneWindow仍是依据windowIsFloating来设置WindowManager.LayoutParams。其实View真正突显的点是在Activity resume的时候,让WMS增添View,其实是这里调用WindowManagerGlobal的addView,这里有个很关键的构造参数params,其实传就是WindowManager.LayoutParams l = r.window.getAttributes(卡塔尔(قطر‎;假诺是Dialog暗中同意宗旨,该参数的宽高其实是WRAP_CONTENT,是衡量最先约束参数值的起源,也正是说,八个Window毕竟多大,这些参数是有最终话语权的,具体的View绘制流程那不详述,只看下View 的measureHierarchy,是怎么样利用window参数布局RootMeasureSpec的:

measureHierarchy(final View host, final WindowManager.LayoutParams lp,
        final Resources res, final int desiredWindowWidth, final int desiredWindowHeight) { 
         ...
         <!--desiredWindowWidth一般是屏幕的宽高-->
       childWidthMeasureSpec = getRootMeasureSpec(desiredWindowWidth, lp.width);
     childHeightMeasureSpec = getRootMeasureSpec(desiredWindowHeight, lp.height);
        ... 
     }  

desiredWindowWidth与desiredWindowHeight日常是显示屏的上涨的幅度与中度,而WindowManager.LayoutParams lp正是地点安装的参数,假使是Activity,暗许是ViewGroup.LayoutParams.MATCH_PARENT,而只如若Dialog,正是ViewGroup.LayoutParams.WRAP_CONTENT,而依赖MeasureSpec的私下认可生成法规,如下:

private static int getRootMeasureSpec(int windowSize, int rootDimension) {
    int measureSpec;
    switch (rootDimension) {
    case ViewGroup.LayoutParams.MATCH_PARENT:
        measureSpec = MeasureSpec.makeMeasureSpec(windowSize, MeasureSpec.EXACTLY);
        break;
    case ViewGroup.LayoutParams.WRAP_CONTENT:
        measureSpec = MeasureSpec.makeMeasureSpec(windowSize, MeasureSpec.AT_MOST);
        break;
    default:
        measureSpec = MeasureSpec.makeMeasureSpec(rootDimension, MeasureSpec.EXACTLY);
        break;
    }
    return measureSpec;
}

只假如Dialog,正是会从今现在就能够接纳MeasureSpec.makeMeasureSpec(windowSize, MeasureSpec.AT_MOST卡塔尔国生成RootMeasureSpec,也便是最大是显示屏尺寸,实效正是大家常用的wrap_content,之后会利用该RootMeasureSpec对DecorView举行测绘。

  private void performMeasure(int childWidthMeasureSpec, int childHeightMeasureSpec) {
    Trace.traceBegin(Trace.TRACE_TAG_VIEW, "measure");
    try {
        mView.measure(childWidthMeasureSpec, childHeightMeasureSpec);
    } finally {
        Trace.traceEnd(Trace.TRACE_TAG_VIEW);
    }
}

如上就是暗中同意Dialog无法全屏的最主因之一, 接着看第二属性 android:windowBackground,那些性情假如使用暗中同意值,设置会有丁香紫边框,其实这里根本是暗许背景的难点,默许使用了有padding的InsetDrawable,设置了有的边距,招致地方的状态栏,尾部的导航栏,左右都有一定的边距

<inset xmlns:android="http://schemas.android.com/apk/res/android"
       android:insetLeft="16dp"
       android:insetTop="16dp"
       android:insetRight="16dp"
       android:insetBottom="16dp">
    <shape android:shape="rectangle">
        <corners android:radius="2dp" />
        <solid android:color="@color/background_floating_material_dark" />
    </shape>
</inset>

本文由新浦京81707con发布于注册购买,转载请注明出处:调用私有的,Android的dialog不能横向铺满全屏问题

关键词: Android自定 Android 横向 全屏 铺满

上一篇:安卓组件控件,推荐10个炫酷的开源库

下一篇:没有了