こんにちは。お久しぶりです。
原稿とか原稿とかメルマガとかで、ご無沙汰してたのですが、ぼちぼち再開します。
原稿の行方についてはそのうちご報告できると思います。(早くはじめにを書けよ、というry)
---
PopupWindow には、setOutsideTouchable(true) を指定すると、PopupWindow の外の領域をタップしたときにポップアップを閉じる処理が実装されています。
PopupWindow.java#1578
- 1578 @Override
- 1579 public boolean onTouchEvent(MotionEvent event) {
- 1580 final int x = (int) event.getX();
- 1581 final int y = (int) event.getY();
- 1582
- 1583 if ((event.getAction() == MotionEvent.ACTION_DOWN)
- 1584 && ((x < 0) || (x >= getWidth()) || (y < 0) || (y >= getHeight()))) {
- 1585 dismiss();
- 1586 return true;
- 1587 } else if (event.getAction() == MotionEvent.ACTION_OUTSIDE) {
- 1588 dismiss();
- 1589 return true;
- 1590 } else {
- 1591 return super.onTouchEvent(event);
- 1592 }
- 1593 }
1578 @Override
1579 public boolean onTouchEvent(MotionEvent event) {
1580 final int x = (int) event.getX();
1581 final int y = (int) event.getY();
1582
1583 if ((event.getAction() == MotionEvent.ACTION_DOWN)
1584 && ((x < 0) || (x >= getWidth()) || (y < 0) || (y >= getHeight()))) {
1585 dismiss();
1586 return true;
1587 } else if (event.getAction() == MotionEvent.ACTION_OUTSIDE) {
1588 dismiss();
1589 return true;
1590 } else {
1591 return super.onTouchEvent(event);
1592 }
1593 }
また、setTouchInterceptor() で View.OnTouchListener を指定すると、ポップアップのタッチを取得することができます。
ちなみに、setOutsideTouchable(true) を指定しないと、ポップアップの外側をタッチしたときにリスナーが呼ばれません。
PopupWindow.java#1570
- 1570 @Override
- 1571 public boolean dispatchTouchEvent(MotionEvent ev) {
- 1572 if (mTouchInterceptor != null && mTouchInterceptor.onTouch(this, ev)) {
- 1573 return true;
- 1574 }
- 1575 return super.dispatchTouchEvent(ev);
- 1576 }
1570 @Override
1571 public boolean dispatchTouchEvent(MotionEvent ev) {
1572 if (mTouchInterceptor != null && mTouchInterceptor.onTouch(this, ev)) {
1573 return true;
1574 }
1575 return super.dispatchTouchEvent(ev);
1576 }
問題は、これらポップアップのタッチイベントが PopupWindow の mBackground 変数が null の時は呼ばれない!!!ということなのです。
レイアウトXMLファイルから PopupWindow を生成した場合は
- 177 public PopupWindow(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
- 178 mContext = context;
- 179 mWindowManager = (WindowManager)context.getSystemService(Context.WINDOW_SERVICE);
- 180
- 181 TypedArray a =
- 182 context.obtainStyledAttributes(
- 183 attrs, com.android.internal.R.styleable.PopupWindow, defStyleAttr, defStyleRes);
- 184
- 185 mBackground = a.getDrawable(R.styleable.PopupWindow_popupBackground);
177 public PopupWindow(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
178 mContext = context;
179 mWindowManager = (WindowManager)context.getSystemService(Context.WINDOW_SERVICE);
180
181 TypedArray a =
182 context.obtainStyledAttributes(
183 attrs, com.android.internal.R.styleable.PopupWindow, defStyleAttr, defStyleRes);
184
185 mBackground = a.getDrawable(R.styleable.PopupWindow_popupBackground);
のようにデフォルトの背景が指定されるので問題ないのですが、コードから生成する場合は PopupWindow の setBackgroundDrawable() メソッドを読んで mBackground に Drawable をセットしないと
- 312 public Drawable getBackground() {
- 313 return mBackground;
- 314 }
312 public Drawable getBackground() {
313 return mBackground;
314 }
setOutsideTouchable(true) を指定してもポップアップの周りをタップしても自動で dismiss() してくれないし、さらには setTouchInterceptor() で指定したリスナーの onTouch() も呼ばれなくなります!
実は、これらのタッチイベントの処理 (onTouchEvent や dispatchTouchEvent) は PopupWindow のサブクラスの PopupViewContainer の中に実装されています。この PopupViewContainer が mBackground が null 以外のときだけ使うような実装になっているのです。
PopupWindow.java#944
- 944 private void preparePopup(WindowManager.LayoutParams p) {
- ...
- 950 if (mBackground != null) {
- ...
- 960 PopupViewContainer popupViewContainer = new PopupViewContainer(mContext);
- 961 PopupViewContainer.LayoutParams listParams = new PopupViewContainer.LayoutParams(
- 962 ViewGroup.LayoutParams.MATCH_PARENT, height
- 963 );
- 964 popupViewContainer.setBackgroundDrawable(mBackground);
- 965 popupViewContainer.addView(mContentView, listParams);
- 966
- 967 mPopupView = popupViewContainer;
- 968 } else {
- 969 mPopupView = mContentView;
- 970 }
- ...
- 973 }
944 private void preparePopup(WindowManager.LayoutParams p) {
...
950 if (mBackground != null) {
...
960 PopupViewContainer popupViewContainer = new PopupViewContainer(mContext);
961 PopupViewContainer.LayoutParams listParams = new PopupViewContainer.LayoutParams(
962 ViewGroup.LayoutParams.MATCH_PARENT, height
963 );
964 popupViewContainer.setBackgroundDrawable(mBackground);
965 popupViewContainer.addView(mContentView, listParams);
966
967 mPopupView = popupViewContainer;
968 } else {
969 mPopupView = mContentView;
970 }
...
973 }
別に背景なくても PopupViewContainer 使ってくれればいいのにー。
ということで、とりあえず PopupWindow は背景指定したほうがいいよー。