前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Android 必知必会 - DialogFragment 实现类似 PopupWindow 效果

Android 必知必会 - DialogFragment 实现类似 PopupWindow 效果

作者头像
他叫自己MR.张
发布2019-07-01 10:51:50
1.6K0
发布2019-07-01 10:51:50
举报
文章被收录于专栏:Android必知必会Android必知必会

版权声明:本文为他叫自己Mr.张的原创文章,转载请注明出处,否则禁止转载。 https://cloud.tencent.com/developer/article/1452937

如果移动端访问不佳,请访问 ==> Github 版

近期有网友根据 Android 必知必会 - DialogFragment 使用总结 做一些业务,但是目标却是用 DialogFragment 实现类似 PopupWindow 效果:

  • 只拦截自身所占空间部分的事件,其余空间的点击事件不处理
  • 可以根据某个 View 定位自身位置

虽然在功能上 PopupWindow 更符合需要,但是使用 DialogFragment 代码更简洁、更方便封装功能模块。

基础知识点

WindowManager.LayoutParams.flags

WindowManager.LayoutParams.flags 修改 Window 的表现行为

代码语言:javascript
复制
  /**
    * Various behavioral options/flags.  Default is none.
    *
    * @see #FLAG_ALLOW_LOCK_WHILE_SCREEN_ON
    * @see #FLAG_DIM_BEHIND
    * @see #FLAG_NOT_FOCUSABLE
    * @see #FLAG_NOT_TOUCHABLE
    * @see #FLAG_NOT_TOUCH_MODAL
    * @see #FLAG_TOUCHABLE_WHEN_WAKING
    * @see #FLAG_KEEP_SCREEN_ON
    * @see #FLAG_LAYOUT_IN_SCREEN
    * @see #FLAG_LAYOUT_NO_LIMITS
    * @see #FLAG_FULLSCREEN
    * @see #FLAG_FORCE_NOT_FULLSCREEN
    * @see #FLAG_SECURE
    * @see #FLAG_SCALED
    * @see #FLAG_IGNORE_CHEEK_PRESSES
    * @see #FLAG_LAYOUT_INSET_DECOR
    * @see #FLAG_ALT_FOCUSABLE_IM
    * @see #FLAG_WATCH_OUTSIDE_TOUCH
    * @see #FLAG_SHOW_WHEN_LOCKED
    * @see #FLAG_SHOW_WALLPAPER
    * @see #FLAG_TURN_SCREEN_ON
    * @see #FLAG_DISMISS_KEYGUARD
    * @see #FLAG_SPLIT_TOUCH
    * @see #FLAG_HARDWARE_ACCELERATED
    * @see #FLAG_LOCAL_FOCUS_MODE
    * @see #FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS
    */

以上可以看到它有很多可选项,加上可以多个相互组合,能满足很多需求,这里重点关注三个属性值:

  • FLAG_NOT_TOUCH_MODAL Window flag: even when this window is focusable (its FLAG_NOT_FOCUSABLE is not set), allow any pointer events outside of the window to be sent to the windows behind it. (API level 1)
  • FLAG_TRANSLUCENT_NAVIGATION Window flag: request a translucent navigation bar with minimal system-provided background protection. (API level 19)
  • FLAG_TRANSLUCENT_STATUS Window flag: request a translucent status bar with minimal system-provided background protection. (API level 19)

更详细的介绍请点击 文档

其中 FLAG_NOT_TOUCH_MODAL 可达到『只拦截自身所占空间部分的事件,其余空间的点击事件不处理』的需求,而 FLAG_TRANSLUCENT_NAVIGATIONFLAG_TRANSLUCENT_STATUS 主要是用来调整使用沉浸式状态栏时显示自身位置不正确的问题。

获取 View 位置的时机

如果需要让 DialogFragment 在 onCreate() 等生命周期函数内直接调用显示到某个 View 的位置处,可能无法正确获取到该 View 的坐标,具体参考 Android必知必会-获取View坐标和长宽的时机 一文。

但是,如果在界面显示给用户后,DialogFragment 的显示交给用户触发的话,就不需要在意这个问题了。

代码实现

TopFragment.java

代码语言:javascript
复制
public class TopFragment extends DialogFragment {

    private static final String EXT_Y = "y value";
    private static final String EXT_BAR = "isTranslucentDecor";
    private int y;
    private boolean isTranslucentDecor;

    public static TopFragment getInstant(int y) {
        return getInstant(y, false);
    }

    public static TopFragment getInstant(int y, boolean isTranslucentDecor) {
        TopFragment fragment = new TopFragment();
        Bundle ext = new Bundle();
        ext.putInt(EXT_Y, y);
        ext.putBoolean(EXT_BAR, isTranslucentDecor);
        fragment.setArguments(ext);
        return fragment;
    }

    @Override
    public void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setStyle(DialogFragment.STYLE_NO_TITLE, R.style.dialogFrag);
        Bundle args = getArguments();
        if (args != null) {
            y = args.getInt(EXT_Y, 0);
            isTranslucentDecor = args.getBoolean(EXT_BAR, false);
        } else {
            y = 0;
            isTranslucentDecor = false;
        }
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        getDialog().setCanceledOnTouchOutside(false);
        View rootView = inflater.inflate(R.layout.fragment_top, container, false);
        //Do something
        final Window window = getDialog().getWindow();
        window.setBackgroundDrawableResource(android.R.color.transparent);
        window.getDecorView().setPadding(0, 0, 0, 0);
        WindowManager.LayoutParams wlp = window.getAttributes();
        wlp.width = WindowManager.LayoutParams.MATCH_PARENT;
        wlp.height = WindowManager.LayoutParams.WRAP_CONTENT;
        wlp.flags = WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL;
        if (isTranslucentDecor) {
            wlp.flags |= WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION;
        }
        wlp.gravity = Gravity.TOP;//必须为 TOP,否则定位不准确
        wlp.y = y;//配合 Gravity.TOP 才能准确定位
        window.setAttributes(wlp);
        //Debug info
        rootView.findViewById(R.id.vvv).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Toast.makeText(getActivity(), "dialogFragment 响应了点击事件", Toast.LENGTH_SHORT).show();
            }
        });
        return rootView;
    }
}

MainActivity.java

代码语言:javascript
复制
@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    findViewById(R.id.bt_menu).setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            TextView title = (TextView) findViewById(R.id.tv_title);
            TopFragment.getInstant(title.getBottom()).show(getSupportFragmentManager(), "tags");
        }
    });
}

注意:如果当前 Activity 使用了沉浸式状态栏,需要使用 TopFragment.getInstant(int y, boolean isTranslucentDecor) 方法,并且 isTranslucentDecor 传值为 true

效果图

未使用沉浸式状态栏、 isTranslucentDecor 传值为 false

使用沉浸式状态栏、 isTranslucentDecor 传值为 false ,位置定位差个状态栏高度

使用沉浸式状态栏、 isTranslucentDecor 传值为 true,位置定位修复

总结

总的来说,这里基本完成了要求的效果,但是定位只能指定其顶部开始的位置,不方便底部定位到某个 View 的上面,因为高度自适应的话,在页面渲染完成前是不能知道它的高度的。当然,你可以使用固定高度布局的方式来实现随意定位。

有什么意见或者问题可以随时联系我,共同探讨学习:

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2017年06月01日,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 基础知识点
    • WindowManager.LayoutParams.flags
      • 获取 View 位置的时机
      • 代码实现
        • 效果图
        • 总结
        领券
        问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档