博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
所见即所得 dialog
阅读量:6454 次
发布时间:2019-06-23

本文共 7111 字,大约阅读时间需要 23 分钟。

我们平时在做普通页面的时候,当 app 运行起来时,所看到的界面,往往就是我们预览 xml 布局文件所看到的那样,即所见即所得。可是如果这些布局文件是放在 dialog 里展示的,情况就不一样了,往往要煞费苦心,才能得到我们想要的效果。

本文分享如何定义一个 BaseDialogFragment 来实现所见即所得的效果。文末还附有处理 dialog 中嵌套 Fragment,status bar 相关问题实践方案。

首先我们创建一个 DialogFragment

public class MyDialogFragment extends DialogFragment {    public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {        return inflater.inflate(R.layout.fragment_dialog, container, false);    }}复制代码
复制代码

我们期待的结果是 dialog 充满整个屏幕,并且 Hello Dialog 这几个字居中显示,但实际的结果是:

我们在根布局设置的 layout 是 match_parent, 显示出来的结果却是 wrap_content

我们知道,一个 dialog 对应着一个 window, 而 window 有一个神奇的属性:isFloating。当 isFloating 为 true 时,dialog contentView 的 宽高被重置为 wrap_content,否者重置为 match_parent

让我们为 dialog 自定义主题,来改变这个值:

复制代码

在 MyDialogFragment 中应用这个主题

@Overridepublic void onCreate(@Nullable Bundle savedInstanceState) {    super.onCreate(savedInstanceState);    setStyle(DialogFragment.STYLE_NORMAL, R.style.FullScreenDialog);}复制代码

跑起来看看:

果然实现全屏了,但是有两个问题,第一,状态栏变黑色了,第二,'Hello Dialog' 不见了。

第一个问题我们延后解决,先让我们来解决第二个问题。

目前,支持库中存在一个错误,导致样式无法正常显示。 可以通过使用 Activity 的 inflater 来解决这个问题,更改 onCreateView 方法:

public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {    return getActivity().getLayoutInflater().inflate(R.layout.fragment_dialog, container, false);}复制代码

现在,Dialog 的样式能正常显示了,具体细节请参看

现在让我们更改根布局的 margin, 留出一些空间来显示遮罩:

复制代码

跑起来看看,结果是令人失望的:

layout_height 不是 200dp, 而是 match_parent, 这是和 isFloating 这个属性密切相关的。

现在我们想到的一个解决方案是,在 LinearLayout 外再套一层 FrameLayout

复制代码

现在,我们得到了预期效果:

但是点击遮罩,dialog 并没有消失,因为这个 dialog 实际上是全屏的,并没有 outside 可以点击。

现在开始封装我们的 BaseDialogFragment, 来解决以下问题:

  1. 不需要在正常的布局外再套一层 FrameLayout
  2. 点击遮罩,Dialog 可以消失
  3. 解决黑色状态栏的问题

定义 DialogFrameLayout,用来处理点击遮罩的问题

public class DialogFrameLayout extends FrameLayout {    interface OnTouchOutsideListener {        void onTouchOutside();    }    GestureDetector gestureDetector = null;    OnTouchOutsideListener onTouchOutsideListener;    public void setOnTouchOutsideListener(OnTouchOutsideListener onTouchOutsideListener) {        this.onTouchOutsideListener = onTouchOutsideListener;    }    public DialogFrameLayout(@NonNull Context context) {        super(context);        commonInit(context);    }    private void commonInit(@NonNull Context context) {        gestureDetector = new GestureDetector(context, new GestureDetector.SimpleOnGestureListener() {            @Override            public boolean onDown(MotionEvent e) {                return true;            }            @Override            public boolean onSingleTapUp(MotionEvent e) {                Rect rect = new Rect();                getHitRect(rect);                int count = getChildCount();                for (int i = count - 1; i > -1; i--) {                    View child = getChildAt(i);                    Rect outRect = new Rect();                    child.getHitRect(outRect);                    if (outRect.contains((int) e.getX(), (int) e.getY())) {                        return false;                    }                }                if (onTouchOutsideListener != null) {                    onTouchOutsideListener.onTouchOutside();                }                return true;            }        });    }    @Override    public boolean onTouchEvent(MotionEvent event) {        return gestureDetector.onTouchEvent(event);    }}复制代码

定义 DialogLayoutInflater, 让我们可以不再需要额外的 FrameLayout

public class DialogLayoutInflater extends LayoutInflater {    private LayoutInflater layoutInflater;    private DialogFrameLayout.OnTouchOutsideListener listener;    public DialogLayoutInflater(Context context, LayoutInflater layoutInflater, DialogFrameLayout.OnTouchOutsideListener listener) {        super(context);        this.layoutInflater = layoutInflater;        this.listener = listener;    }    @Override    public LayoutInflater cloneInContext(Context context) {        return layoutInflater.cloneInContext(context);    }    @Override    public View inflate(int resource, @Nullable ViewGroup root, boolean attachToRoot) {        DialogFrameLayout dialogFrameLayout = new DialogFrameLayout(getContext());        dialogFrameLayout.setOnTouchOutsideListener(listener);        dialogFrameLayout.setLayoutParams(new ViewGroup.LayoutParams(-1, -1));        layoutInflater.inflate(resource, dialogFrameLayout, true);        return dialogFrameLayout;    }}复制代码

编写 BaseDialogFragment, 把一切连接起来:

public class BaseDialogFragment extends DialogFragment {    @NonNull    @Override    public LayoutInflater onGetLayoutInflater(@Nullable Bundle savedInstanceState) {        setStyle(DialogFragment.STYLE_NORMAL, R.style.FullScreenDialog);        super.onGetLayoutInflater(savedInstanceState);        // 换成 Activity 的 inflater, 解决 fragment 样式 bug        LayoutInflater layoutInflater = getActivity().getLayoutInflater();        if (!getDialog().getWindow().isFloating()) {            setupDialog();            layoutInflater = new DialogLayoutInflater(requireContext(), layoutInflater,                    new DialogFrameLayout.OnTouchOutsideListener() {                        @Override                        public void onTouchOutside() {                            if (isCancelable()) {                                dismiss();                            }                        }                    });        }        return layoutInflater;    }    protected void setupDialog() {        Window window = getDialog().getWindow();        // 解决黑色状态栏的问题        AppUtils.setStatusBarTranslucent(window, true);        AppUtils.setStatusBarColor(window, Color.TRANSPARENT, false);        window.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE);        getDialog().setOnKeyListener(new DialogInterface.OnKeyListener() {            @Override            public boolean onKey(DialogInterface dialogInterface, int keyCode, KeyEvent event) {                if (event.getAction() == KeyEvent.ACTION_UP && keyCode == KeyEvent.KEYCODE_BACK) {                    if (isCancelable()) {                        dismiss();                    }                    return true;                }                return false;            }        });    }}复制代码

就这样,一个 BaseDialogFragment 封装好了,MyDialogFragment 继承 BaseDialogFragment, 即可实现所见即所得。

public class MyDialogFragment extends BaseDialogFragment {    @Nullable    @Override    public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {        // 注意,这里不再需要 getActivity().getLayoutInflater(), 因为 BaseDialogFragment 已经返回了正确的 inflater        return inflater.inflate(R.layout.fragment_dialog, container, false);    }}复制代码

布局文件也不再需要在外面再套个 FrameLayout

复制代码

一切正如期待的那样,一切都变得简单,只要关注布局就可以了。不过我们可以走得更远:

当 Fragment 根布局有 layout_gravity="bottom" 属性时,自动附加 slide 动画:

状态栏花样变幻以及 Fragment 嵌套

详情请查看 。该库不仅处理了 Dialog 的问题,还处理了 Fragment 嵌套,嵌套 Fragment 懒加载,右滑返回,沉浸式状态栏,Toolbar 等一系列问题,让你可以专注于业务,而无需为导航等应用级 UI 问题操心。

转载地址:http://bxfzo.baihongyu.com/

你可能感兴趣的文章
iphone xcode 错误提示 Xcode encountered an internal logic error.
查看>>
lsyncd —— 多机器实时同步文件神器
查看>>
Python 文件操作
查看>>
SpringCloud学习成长之路二 服务客户端(rest+ribbon)
查看>>
CDH5.5.6下R、RHive、RJava、RHadoop安装测试
查看>>
57、唤醒正在睡眠的线程
查看>>
文字输出
查看>>
选择指定文本值、input子元素
查看>>
34、Java集合框架List,Map,Set等全面介绍(转载)
查看>>
xml字符串转xml对象,xml对象转json对象
查看>>
JNI_1
查看>>
脚本 vbscript遍历文件夹改名
查看>>
使用PHP+Sphinx建立高效的站内搜索引擎的方法
查看>>
Java三大特征之继承(二)
查看>>
python装饰器
查看>>
dubbo-zookeeper(续)
查看>>
(转)C#委托的介绍(delegate、Action、Func、predicate)
查看>>
PyCharm Tips 常用操作帮助
查看>>
IOS App 实现通过URL 超链接进行跳转
查看>>
CSS font-family 属性
查看>>