| |
|
开发:
C++知识库
Java知识库
JavaScript
Python
PHP知识库
人工智能
区块链
大数据
移动开发
嵌入式
开发工具
数据结构与算法
开发测试
游戏开发
网络协议
系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程 数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁 |
-> 移动开发 -> Android PopupWindow -> 正文阅读 |
|
[移动开发]Android PopupWindow |
1.PopupWindow PopupWindow类用来实现一个弹出框,可以使用任意布局的View作为其内容,这个弹出框是悬浮在当前activity之上的。 2.用法 点击按钮弹出PopupWindow: private void showPopupWindow(View view) { ? ? //自定义布局,作为PopupWindow显示的内容 ? ? View contentView = LayoutInflater.from( mContext).inflate(R.layout.pop_window, null); ? ? final PopupWindow popupWindow = new PopupWindow(contentView,LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT, true); ? ? popupWindow.setTouchable(true); ? ? popupWindow.setTouchInterceptor(new OnTouchListener() { ? ? ? ?@Override ? ? ? ?public boolean onTouch(View v, MotionEvent event) { ? ? ? ? ? ? Log.i("mengdd", "onTouch : "); ? ? ? ? ? ? return false; //这里如果返回true的话,touch事件将被拦截,拦截后 PopupWindow的onTouchEvent不被调用,这样点击外部区域无法dismiss ? ? ? ?} ? ?}); ? ? ? // 如果不设置PopupWindow的背景,无论是点击外部区域还是Back键都无法dismiss弹框 ? ? ?popupWindow.setBackgroundDrawable( getResources().getDrawable(R.drawable.selectmenu_bg_downward)); ? ? ?// 设置好参数之后再show ? ? ?popupWindow.showAsDropDown(view); } 第一次实现的时候遇到了问题,就是弹出框不会在按下Back键的时候消失,点击弹框外区域也没有正常消失,搜索了一下,都说只要设置背景就好了。然后就找了个图片,果然弹框能正常dismiss了(见注释)。 ? 3.源码分析 显示提供了两种形式: ①showAtLocation()显示在指定位置,有两个方法重载: public void showAtLocation(View parent, int gravity, int x, int y) { ? ? mParentRootView = new WeakReference<>(parent.getRootView()); ? ? showAtLocation(parent.getWindowToken(), gravity, x, y); } public void showAtLocation(IBinder token, int gravity, int x, int y)? { ? ? if(isShowing() || m content view == null) { ? ? ? ? return; ? ? } ? ? TransitionManager.endTransitions( mDecorView); ? ? detachFromAnchor(); ? ? mIsShowing = true; ? ? mIsDropdown = false; ? ? mGravity = gravity; ? ? final WindowManager.LayoutParams p = createPopupLayoutParams(token); ? ? preparePopup(p); ? ? p.x = x; ? ? p.y = y; ? ? invokePopup(p); } ②showAsDropDown()显示在一个参照物View的周围,有三个方法重载: public void showAsDropDown(View anchor) { ? ? showAsDropDown(anchor, 0, 0); } public void showAsDropDown(View anchor, int xoff, int yoff) { ? ??showAsDropDown(anchor, xoff, yoff, DEFAULT_ANCHORED_GRAVITY); } public void showAsDropDown(View anchor, int xoff, int yoff, int gravity) {? ? ? ? if(isShowing() || m content view == null) { ? ? ? ? return; ? ? } ? ? TransitionManager.endTransitions( mDecorView); ? ? attachToAnchor(anchor, xoff, yoff, gravity); ? ? mIsShowing = true; ? ? mIsDropdown = true; ? ? final WindowManager.LayoutParams p = createPopupLayoutParams(anchor.getApplicationWindowToken()); ? ? preparePopup(p); ? ?final boolean aboveAnchor = findDropDownPosition( anchor, p, xoff, yoff, p.width, p.height, gravity, mAllowScrollingAnchorParent); ? ? updateAboveAnchor(aboveAnchor); ? ? p.accessibilityIdOfAnchor = (anchor != null) ? anchor.getAccessibilityViewId() : -1; ? ? invokePopup(p); } 可以看出来,弹出的方法中首先都需要preparePopup() ,最后再invokePopup() 。 prepare的方法中可以看到有无背景的分别: ? /**Prepare the popup by embedding in into a new ViewGroup if the?background drawable is not null. If embedding is required, the layout?parameters' height is mnodified to take into account the background's?padding. ? * @param p the layout parameters of the popup's content view ? */ ?private void preparePopup( WindowManager.LayoutParams p) { ? ? ?if (mContentView == null || mContext == null || mWindowManager == null) { ? ? ? throw new IllegalStateException("You must specify a valid content view by calling setContentView() before attempting to show the popup."); ? ??} ? ??if (mBackground != null) { ? ? ? ? mBackgroundView =createBackgroundView( mContentView); ? ? ? ? mBackgroundView.setBackground( mBackground); ? ? ? } else { ? ? ? ? mBackgroundView = mContentView; ? ? ?} ? ? ? mDecorView = createDecorView( mBackgroundView); ? ? mDecorView.setIsRootNamespace(true); ? ? mBackgroundView.setElevation(mElevation); ? ? p.setSurfaceInsets(mBackgroundView, true, true); ? ? mPopupViewInitialLayoutDirectionInherited =(mContentView.getRawLayoutDirection() == View.LAYOUT_DIRECTION_INHERIT); } /*Wrap a content view in a PopupViewContainer. @params contentView : the content view to wrap @return: a PopupViewContainer that wraps the content view*/ private PopupBackgroundView createBackgroundView(View contentView) { ? ? final ViewGroup.LayoutParams layoutParams = mContentView.getLayoutParams(); ? ? final int height; ? ? if(layoutParams != null && layoutParams.height == WRAP_CONTENT) { ? ? ? ? height =?WRAP_CONTENT; ? ? } else { ? ? ? ? height = MATCH_PARENT; ? ? } ? ? final PopupBackgroundView backgroundView = new PopupBackgroundView(mContent); ? ? final PopupBackgroundView.LayoutParams listParams = new PopupBackgroundView.LayoutParams(MATCH_PARENT, height); ? ? BackgroundView.addView(contentView, listParams); ? ? return backgroundView; } 背景是否为空对Touch事件的影响: 如果有背景,则会在contentView外面包一层PopupViewContainer之后作为mPopupView,如果没有背景,则直接用contentView作为mPopupView。 而这个PopupViewContainer是一个内部私有类,它继承了FrameLayout,在其中重写了Key和Touch事件的分发处理:? @Override public boolean dispatchKeyEvent(KeyEvent event) { ? ? ?if (event.getKeyCode() == KeyEvent.KEYCODE_BACK) { ? ? ? ? if (getKeyDispatcherState() == null) { ? ? ? ? ? ? return super.dispatchKeyEvent(event); ? ? ? ?} ? ? ? if (event.getAction() == KeyEvent.ACTION_DOWN && event.getRepeatCount() == 0) { ? ? ? ? ?KeyEvent.DispatcherState state = getKeyDispatcherState(); ? ? ? ? ?if (state != null) { ? ? ? ? ? state.startTracking(event, this); ? ? ? ? ?} ? ? ? ? ?return true; ? ? ? } else if (event.getAction() == KeyEvent.ACTION_UP) { ? ? ? ? ?KeyEvent.DispatcherState state = getKeyDispatcherState(); ? ? ? ? ?if (state != null && state.isTracking(event) && !event.isCanceled()) { ? ? ? ? ? ? ? dismiss(); ? ? ? ? ? ? ? return true; ? ? ? ? ?} ? ? ? } ? ? ? return super.dispatchKeyEvent(event); ? ? } else { ? ? ? ? return super.dispatchKeyEvent(event); ? ? } } @Override public boolean dispatchTouchEvent(MotionEvent ev) { ? ? ?if (mTouchInterceptor != null && mTouchInterceptor.onTouch(this, ev)) { ? ? ? ? ??return true; ? ? ?} ? ? ?return super.dispatchTouchEvent(ev); } @Override public boolean onTouchEvent(MotionEvent event) { ? ? ?final int x = (int) event.getX(); ? ? ?final int y = (int) event.getY(); ? ? ?if ((event.getAction() == MotionEvent.ACTION_DOWN) && ((x < 0) || (x >= getWidth()) || (y < 0) || (y >= getHeight()))) { ? ? ? ? ?dismiss(); ? ? ? ? ?return true; ? ? ?} else if (event.getAction() == MotionEvent.ACTION_OUTSIDE) { ? ? ? ? ?dismiss(); ? ? ? ? ?return true; ? ? ?} else { ? ? ? ? ?return super.onTouchEvent(event); ? ? ?} } 由于PopupView本身并没有重写Key和Touch事件的处理,所以如果没有包这个外层容器类,点击Back键或者外部区域是不会导致弹框消失的。 补充Case: 弹窗不消失,但是事件向下传递 如上所述: 设置了PopupWindow的background,点击Back键或者点击弹窗的外部区域,弹窗就会dismiss。相反,如果不设置PopupWindow的background,那么点击back键和点击弹窗的外部区域,弹窗是不会消失的. 那么,如果我想要一个效果,点击外部区域,弹窗不消失,但是点击事件会向下面的activity传递,比如下面是一个WebView,我想点击里面的链接等. 研究了半天,说是要给Window设置一个Flag,WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL 看了源码,这个Flag的设置与否是由一个叫mNotTouchModal的字段控制,但是设置该字段的set方法被标记为@hide。 所以要通过反射的方法调用:? /** ? * Set whether this window is touch modal or if outside touches will be sent to other windows behind it. ? */ ?public static void setPopupWindowTouchModal( PopupWindow popupWindow, boolean touchModal) { ? ? ?if (null == popupWindow) { ? ? ? ? ? return; ? ? ?} ? ? ?Method method; ? ? ?try { ? ? ? ? method = PopupWindow.class.getDecl aredMethod("setTouchModal",?boolean.class); ? ? ? ?method.setAccessible(true); ? ? ? ?method.invoke(popupWindow, touchModal); ? ? }?catch (Exception e) { ? ? ? ? e.printStackTrace(); ? ? } } 然后在程序中: UIUtils.setPopupWindowTouchModal(popupWindow, false); 该popupWindow外部的事件就可以传递给下面的Activity了。 ? |
|
移动开发 最新文章 |
Vue3装载axios和element-ui |
android adb cmd |
【xcode】Xcode常用快捷键与技巧 |
Android开发中的线程池使用 |
Java 和 Android 的 Base64 |
Android 测试文字编码格式 |
微信小程序支付 |
安卓权限记录 |
知乎之自动养号 |
【Android Jetpack】DataStore |
|
上一篇文章 下一篇文章 查看所有文章 |
|
开发:
C++知识库
Java知识库
JavaScript
Python
PHP知识库
人工智能
区块链
大数据
移动开发
嵌入式
开发工具
数据结构与算法
开发测试
游戏开发
网络协议
系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程 数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁 |
360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 | -2024/11/25 1:21:18- |
|
网站联系: qq:121756557 email:121756557@qq.com IT数码 |