GVKun编程网logo

PopupWindow重叠Android 5.0上的软按钮

11

对于PopupWindow重叠Android5.0上的软按钮感兴趣的读者,本文将会是一篇不错的选择,并为您提供关于Android7.0PopupWindow显示错位问题、Android7.0Popup

对于PopupWindow重叠Android 5.0上的软按钮感兴趣的读者,本文将会是一篇不错的选择,并为您提供关于Android 7.0 PopupWindow 显示错位问题、Android 7.0 PopupWindow 错位移位问题、android popupWindow、Android PopupWindow 增加半透明蒙层的有用信息。

本文目录一览:

PopupWindow重叠Android 5.0上的软按钮

PopupWindow重叠Android 5.0上的软按钮

我有一个简单的PopupWindow,我使用以下代码创建(代码在C#中,Java代码应该基本相同)
View popupView = LayoutInflater.From(this.Activity).Inflate(Resource.Layout.LectionFooter,null);

var popup = new PopupWindow(popupView,ViewGroup.LayoutParams.MatchParent,ViewGroup.LayoutParams.WrapContent,false)
{
    Outsidetouchable = true,AnimationStyle = Resource.Style.Footeranimation
};

popup.SetBackgroundDrawable(new BitmapDrawable());
popup.ShowAtLocation(rootView,GravityFlags.Bottom,0);

在pre-Lollipop设备上,这个弹出窗口看起来不错,但在Android 5.0上,弹出窗口与软按钮重叠:

这是Android 4.4设备上的PopupWindow:

有谁知道为什么会发生这种情况以及如何解决这个问题?

解决方法

这可能是android api 21中的错误,这就是他们在api 22中引入 popup.setAttachedInDecor(true/false);方法的原因
但是如果有锻炼,您可以为弹出窗口设置正确的y坐标,如下所示:
Rect rect = new Rect();
getwindow().getDecorView().getwindowVisibledisplayFrame(rect);
int winHeight = getwindow().getDecorView().getHeight();
popup.showAtLocation(rootView,Gravity.BottOM,winHeight-rect.bottom);

Android 7.0 PopupWindow 显示错位问题

这是系统级 bug, 需要我们自定义 PopupWindow

public class PopView extends PopupWindow {
    public PopView(Context context) {
        super(context);
    }

    public PopView(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    public PopView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

    public PopView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
        super(context, attrs, defStyleAttr, defStyleRes);
    }

    public PopView() {
    }

    public PopView(View contentView) {
        super(contentView);
    }

    public PopView(int width, int height) {
        super(width, height);
    }

    public PopView(View contentView, int width, int height) {
        super(contentView, width, height);
    }

    public PopView(View contentView, int width, int height, boolean focusable) {
        super(contentView, width, height, focusable);
    }

    @Override
    public void showAsDropDown(View anchor) {
        if(Build.VERSION.SDK_INT >= 24) {
            Rect rect = new Rect();
            anchor.getGlobalVisibleRect(rect);
            int h = anchor.getResources().getDisplayMetrics().heightPixels - rect.bottom;
            setHeight(h);
        }
        super.showAsDropDown(anchor);
    }
}

 

Android 7.0 PopupWindow 错位移位问题

Android7.0 PopupWindow 的兼容问题

  Android7.0 中对 PopupWindow 这个常用的控件又做了一些改动,修复了以前遗留的一些问题的同时貌似又引入了一些问题,本文通过在 7.0 设备上实测并且结合源码分析,带你了解关于 PopupWindow 的相关改动。

  Android7.0 中下面两个问题解决了,这里强调一下,不是说从 Android7.0 开始才解决这两个问题的,因为具体版本细节没去深究。可能在其他的某些版本下面的问题也是被解决了的。

  1. PopupWindow 不响应点击外部消失和返回键消失的解决方法,博文地址:
    http://www.cnblogs.com/popfisher/p/5608717.html
  2. 不得不吐槽的 Android PopupWindow 的几个痛点(实现带箭头的上下文菜单遇到的坑),博文地址:
    http://www.cnblogs.com/popfisher/p/5944054.html

Android7.0 中又引入了新的问题(这就非常的尴尬了)

  1. 调用 update 方法,PopupWindow 的 Gravity 会改变

从源码看 7.0 怎么解决遗留问题的

  解决 PopupWindow 不响应点击外部消失和返回键消失的问题,我们是通过自己设置一个背景。Android7.0 中不设置背景也是可以的,那么它的代码肯定做了处理。从 api24 的源码中找到 PopupWindow.java 文件,我找到里面的 preparePopup 方法如下:

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.");
    }

    // The old decor view may be transitioning out. Make sure it finishes
    // and cleans up before we try to create another one.
    if (mDecorView != null) {
        mDecorView.cancelTransitions();
    }

    // When a background is available, we embed the content view within
    // another view that owns the background drawable.
    if (mBackground != null) {
        mBackgroundView = createBackgroundView(mContentView);
        mBackgroundView.setBackground(mBackground);
    } else {
        mBackgroundView = mContentView;
    }

    mDecorView = createDecorView(mBackgroundView);

    // The background owner should be elevated so that it casts a shadow.
    mBackgroundView.setElevation(mElevation);

    // We may wrap that in another view, so we''ll need to manually specify
    // the surface insets.
    p.setSurfaceInsets(mBackgroundView, true /*manual*/, true /*preservePrevious*/);

    mPopupViewInitialLayoutDirectionInherited =
            (mContentView.getRawLayoutDirection() == View.LAYOUT_DIRECTION_INHERIT);
}

  重点只需要看 mDecorView = createDecorView(mBackgroundView); 可以看到不管 mBackground 变量是否为空,最终都执行了这句代码,这句代码会多加一层 ViewGroup 把 mBackgroundView 包进去了,里面应该包含了对返回键的处理逻辑,我们再看看 createDecorView 方法源码:

private PopupDecorView createDecorView(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 PopupDecorView decorView = new PopupDecorView(mContext);
    decorView.addView(contentView, MATCH_PARENT, height);
    decorView.setClipChildren(false);
    decorView.setClipToPadding(false);

    return decorView;
}

  createDecorView 里面还是没有直接看出对事件的处理,但是里面有个 PopupDecorView 类,应该在里面了吧,继续看:

    private class PopupDecorView extends FrameLayout {
    //......有代码被省略

    @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) {
                final KeyEvent.DispatcherState state = getKeyDispatcherState();
                if (state != null) {
                    state.startTracking(event, this);
                }
                return true;
            } else if (event.getAction() == KeyEvent.ACTION_UP) {
                final 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 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);
        }
    }

    //......有代码被省略
}

  从上面的代码中我们看到了 KeyEvent.KEYCODE_BACK 和 MotionEvent.ACTION_OUTSIDE,没错这里有对返回键和其他事件的处理。

  至于怎么解决 showAsDropDown 方法弹出位置不对的问题,也就是上文中描述的第二个问题,本文就不贴源码了,感兴趣的可以下载源码去看看,本文只是提供一种解决问题的思路,希望大家能从源码中找到解决问题的办法,这才是作者希望达到的效果。 文章末尾会给出 Android7.0 PopupWindow.java 的 java 文件。

Android7.0 引入的新问题

  调用 update 方法时,PopupWindow 的 Gravity 会改变,导致位置发生了改变,具体看下图:

showAtLocation传入Gravity.Bottom:从屏幕底部对齐弹出

调用update方法更新第5点中弹出PopupWindow,发现PopupWindow的Gravity发生了改变

关于这个问题还有篇文章可以参考, http://www.jianshu.com/p/0df10893bf5b

Android7.0 PopupWindow 其他改动点,与 Android5.1 的对比

主界面

1. PopupWindow高宽都设置为match_parent:7.0(左边)从屏幕左上角弹出,5.1(右边)从anchorView下方弹出

 

2. 宽度wrap_content-高度match_parent:7.0(左边)从屏幕左上角弹出,5.1(右边)从anchorView下方弹出

 

3. 宽度match_parent-高度wrap_content:都从anchorView下方弹出

4. 宽度wrap_content-高度大于anchorView到屏幕底部的距离:7.0与5.1都从anchorView上方弹出,与anchorView左对齐

源码地址

Github 工程地址,收录了 PopupWindow 相关使用问题:
https://github.com/PopFisher/SmartPopupWindow

Android 7.0 PopupWindow.java 文件:
https://github.com/PopFisher/SmartPopupWindow/blob/master/sourcecode/PopupWindow(7.0).java

总结

  Android PopupWindow 这个控件 Google 一直没有优化好,使用时需要参考我之前的几篇文章。本文是希望读者善于从源码的角度去分析和解决问题,加深自己对源码的理解,对问题的理解,这样印象要深刻一些。

  本来 2017 年回来还没有时间写写文章,这篇文章也是巧合,同事在 Android7.0 中发现 PopupWindow 使用上有 bug,所以我就借此机会研究一下,虽然知识点简单,但是也花费了几个小时的时间整理出这样一篇文章。如果读者觉得有用,别忘记点击推荐哦,总之也算是开了一个好头吧,以后还是会坚持每个月写些文章出来分享。

android popupWindow

    private void showBillingPop() {


        Display dps=getWindowManager().getDefaultDisplay();
        final PopupWindow ppw= new PopupWindow(this);

        int width = dps.getWidth();
        int height = dps.getHeight();
        
        float w = (float) ((20.0/320.0)*width);
        float h = (float) ((120.0/480.0)*height);
        ppw.setWidth((int) (width-w));
        ppw.setHeight((int) (height-h));
        
        OnClickListener cl =new OnClickListener() {
            
            @Override
            public void onClick(View v ) {
                if(v.getId()==R.id.ct_dlg_yes){
                    SMS.checkFee("激活正版",
                            instance,
                            instance.smsl,  
                            "0111C0913311022193569711022193500101MC099888000000000000000000000000",//0811C1111411022172570111022172500101MC099694000000000000000000000000
                            "您将购买\"激活正版\",点击确定将会发送一条6元短信,不含信息费.",
                            "发送成功!已成功解锁!",true);
                }else{
                    Log.e(TAG, "----------------->>> ppw close <<<------------------");
                }
                
                ppw.dismiss();
            }
        };
        
        View v = LayoutInflater.from(mContext).inflate(R.layout.layout_ctdialog, null);
        
        TextView tv = (TextView) v.findViewById(R.id.ct_dialogtext);
        Button bty = (Button) v.findViewById(R.id.ct_dlg_yes);
        Button btn = (Button) v.findViewById(R.id.ct_dlg_no);
        ImageButton imbt=(ImageButton)v.findViewById(R.id.ct_dlg_close);

        tv.setText("仅需支付6元,即可享受正版游戏!感谢支持正版!");
        bty.setOnClickListener(cl);
        btn.setOnClickListener(cl);
        imbt.setOnClickListener(cl);
        
        ppw.setBackgroundDrawable(new ColorDrawable(Color.BLACK));
        ppw.setContentView(v);
        ppw.setOutsideTouchable(false);
        ppw.setFocusable(true);
    
        ppw.update();
        ppw.showAsDropDown(v,(int)w/2,(int)h/2);
    }

Android PopupWindow 增加半透明蒙层

先看效果图:

BasePopupWindowWithMask.class

 1 package com.example.popupwindowwithmask;
 2   
 3 import android.content.Context;
 4 import android.graphics.PixelFormat;
 5 import android.graphics.drawable.ColorDrawable;
 6 import android.os.IBinder;
 7 import android.view.KeyEvent;
 8 import android.view.View;
 9 import android.view.WindowManager;
10 import android.widget.PopupWindow;
11   
12 /**
13  * Created by kk on 2017/7/22.
14  */
15   
16 public abstract class BasePopupWindowWithMask extends PopupWindow {
17  protected Context context;
18  private WindowManager windowManager;
19  private View maskView;
20   
21  public BasePopupWindowWithMask(Context context) {
22   super(context);
23   this.context = context;
24   windowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
25   setContentView(initContentView());
26   setHeight(initHeight());
27   setWidth(initWidth());
28   setOutsideTouchable(true);
29   setFocusable(true);
30   setTouchable(true);
31   setBackgroundDrawable(new ColorDrawable());
32  }
33   
34  protected abstract View initContentView();
35   
36  protected abstract int initHeight();
37   
38  protected abstract int initWidth();
39   
40  @Override
41  public void showAsDropDown(View anchor) {
42   addMask(anchor.getWindowToken());
43   super.showAsDropDown(anchor);
44  }
45   
46  private void addMask(IBinder token) {
47   WindowManager.LayoutParams wl = new WindowManager.LayoutParams();
48   wl.width = WindowManager.LayoutParams.MATCH_PARENT;
49   wl.height = WindowManager.LayoutParams.MATCH_PARENT;
50   wl.format = PixelFormat.TRANSLUCENT;//不设置这个弹出框的透明遮罩显示为黑色
51   wl.type = WindowManager.LayoutParams.TYPE_APPLICATION_PANEL;//该Type描述的是形成的窗口的层级关系
52   wl.token = token;//获取当前Activity中的View中的token,来依附Activity
53   maskView = new View(context);
54   maskView.setBackgroundColor(0x7f000000);
55   maskView.setFitsSystemWindows(false);
56   maskView.setOnKeyListener(new View.OnKeyListener() {
57    @Override
58    public boolean onKey(View v, int keyCode, KeyEvent event) {
59     if (keyCode == KeyEvent.KEYCODE_BACK) {
60      removeMask();
61      return true;
62     }
63     return false;
64    }
65   });
66   /**
67    * 通过WindowManager的addView方法创建View,产生出来的View根据WindowManager.LayoutParams属性不同,效果也就不同了。
68    * 比如创建系统顶级窗口,实现悬浮窗口效果!
69    */
70   windowManager.addView(maskView, wl);
71  }
72   
73  private void removeMask() {
74   if (null != maskView) {
75    windowManager.removeViewImmediate(maskView);
76    maskView = null;
77   }
78  }
79   
80  @Override
81  public void dismiss() {
82   removeMask();
83   super.dismiss();
84  }
85 }

TestPopupWindow.class

  1 package com.example.popupwindowwithmask;
  2   
  3 import android.content.Context;
  4 import android.view.LayoutInflater;
  5 import android.view.View;
  6 import android.view.WindowManager;
  7   
  8 /**
  9  * Created by kk on 2017/7/22.
 10  */
 11   
 12 public class TestPopupWindow extends BasePopupWindowWithMask {
 13  private int[] mIds;
 14  private View contentView;
 15  private OnItemClickListener listener;
 16   
 17  public interface OnItemClickListener {
 18   void OnItemClick(View v);
 19  }
 20   
 21  public void setOnItemClickListener(OnItemClickListener listener) {
 22   this.listener = listener;
 23  }
 24   
 25  public TestPopupWindow(Context context, int[] mIds) {
 26   super(context);
 27   this.mIds = mIds;
 28   
 29   initListener();
 30  }
 31   
 32  @Override
 33  protected View initContentView() {
 34   contentView = LayoutInflater.from(context).inflate(R.layout.pop_layout, null, false);
 35   return contentView;
 36  }
 37   
 38  private void initListener() {
 39   for (int i = 0; i < mIds.length; i++) {
 40    contentView.findViewById(mIds[i]).setOnClickListener(new View.OnClickListener() {
 41     @Override
 42     public void onClick(View v) {
 43      if (null != listener) {
 44       listener.OnItemClick(v);
 45      }
 46      dismiss();
 47     }
 48    });
 49   }
 50  }
 51  @Override
 52  protected int initHeight() {
 53   return WindowManager.LayoutParams.WRAP_CONTENT;
 54  }
 55  @Override
 56  protected int initWidth() {
 57   return (int) (0.5 * UIUtils.getScreenWidth(context));
 58  }
 59 }
 60 MainActivity.class
 61 ?
 62 1
 63 2
 64 3
 65 4
 66 5
 67 6
 68 7
 69 8
 70 9
 71 10
 72 11
 73 12
 74 13
 75 14
 76 15
 77 16
 78 17
 79 18
 80 19
 81 20
 82 21
 83 22
 84 23
 85 24
 86 25
 87 26
 88 27
 89 28
 90 29
 91 30
 92 31
 93 32
 94 33
 95 34
 96 35
 97 36
 98 37
 99 38
100 39
101 40
102 41
103 42
104 43
105 44
106 45
107 package com.example.popupwindowwithmask;
108   
109 import android.os.Bundle;
110 import android.support.v7.app.AppCompatActivity;
111 import android.view.View;
112 import android.widget.TextView;
113 import android.widget.Toast;
114   
115 public class MainActivity extends AppCompatActivity {
116  private TextView textView;
117   
118  @Override
119  protected void onCreate(Bundle savedInstanceState) {
120   super.onCreate(savedInstanceState);
121   setContentView(R.layout.activity_main);
122   textView = (TextView) findViewById(R.id.tv_popup);
123   
124   
125   final TestPopupWindow testPopupWindow = new TestPopupWindow(this, new int[]{R.id.pop_location, R.id.pop_group, R.id.pop_list});
126   
127   textView.setOnClickListener(new View.OnClickListener() {
128    @Override
129    public void onClick(View v) {
130     testPopupWindow.showAsDropDown(textView);
131    }
132   });
133   
134   testPopupWindow.setOnItemClickListener(new TestPopupWindow.OnItemClickListener() {
135    @Override
136    public void OnItemClick(View v) {
137     switch (v.getId()) {
138      case R.id.pop_location:
139       Toast.makeText(MainActivity.this, "地址", Toast.LENGTH_SHORT).show();
140       break;
141      case R.id.pop_group:
142       Toast.makeText(MainActivity.this, "分组", Toast.LENGTH_SHORT).show();
143       break;
144      case R.id.pop_list:
145       Toast.makeText(MainActivity.this, "清单", Toast.LENGTH_SHORT).show();
146       break;
147     }
148    }
149   });
150  }
151 }

pop_layout.xml

 1 <?xml version="1.0" encoding="utf-8"?>
 2 <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
 3  android:layout_width="wrap_content"
 4  android:layout_height="wrap_content">
 5   
 6  <RelativeLayout
 7   android:layout_width="wrap_content"
 8   android:layout_height="wrap_content">
 9   
10   <RelativeLayout
11    android:id="@+id/rl_indicator"
12    android:layout_width="match_parent"
13    android:layout_height="wrap_content"
14    android:gravity="center_horizontal">
15   
16    <ImageView
17     android:layout_width="wrap_content"
18     android:layout_height="12dp"
19     android:scaleType="fitCenter"
20     android:src="@drawable/filter_arrow_up" />
21   </RelativeLayout>
22   
23   <LinearLayout
24    android:layout_width="wrap_content"
25    android:layout_height="150dp"
26    android:layout_below="@+id/rl_indicator"
27    android:background="@drawable/pop_background"
28    android:gravity="center_horizontal"
29    android:orientation="vertical"
30    android:paddingLeft="15dp"
31    android:paddingRight="15dp">
32   
33    <TextView
34     android:id="@+id/pop_location"
35     android:layout_width="match_parent"
36     android:layout_height="0dp"
37     android:layout_weight="1"
38     android:drawableLeft="@mipmap/fault_equipment_location_icon"
39     android:drawablePadding="12dp"
40     android:gravity="center_vertical"
41     android:text="地址"
42     android:textColor="#000"
43     android:textSize="16sp" />
44   
45    <View
46     android:layout_width="match_parent"
47     android:layout_height="0.3dp"
48     android:background="#D2D2D2" />
49   
50    <TextView
51     android:id="@+id/pop_group"
52     android:layout_width="match_parent"
53     android:layout_height="0dp"
54   
55     android:layout_weight="1"
56     android:drawableLeft="@mipmap/fault_equipment_grouping_icon"
57     android:drawablePadding="12dp"
58     android:gravity="center_vertical"
59     android:text="分组"
60     android:textColor="#000"
61     android:textSize="16sp" />
62   
63    <View
64     android:layout_width="match_parent"
65     android:layout_height="0.3dp"
66     android:background="#D2D2D2" />
67   
68    <TextView
69     android:id="@+id/pop_list"
70     android:layout_width="match_parent"
71     android:layout_height="0dp"
72     android:layout_weight="1"
73     android:drawableLeft="@mipmap/fault_equipment_list_icon"
74     android:drawablePadding="12dp"
75     android:gravity="center_vertical"
76     android:text="清单"
77     android:textColor="#000"
78     android:textSize="16sp" />
79   
80   </LinearLayout>
81  </RelativeLayout>
82 </RelativeLayout>

pop_background.xml

1 <?xml version="1.0" encoding="utf-8"?>
2 <shape xmlns:android="http://schemas.android.com/apk/res/android">
3  <solid android:color="#ffffff" />
4  <corners
5   android:radius="5dp" />
6 </shape>

UIUtils.class

 1 package com.example.popupwindowwithmask;
 2   
 3 import android.content.Context;
 4   
 5 /**
 6  * Created by kk on 2017/7/22.
 7  */
 8   
 9 public class UIUtils {
10  /**
11   * 获得屏幕宽度
12   *
13   * @param context
14   * @return
15   */
16  public static int getScreenWidth(Context context) {
17   return context.getResources().getDisplayMetrics().widthPixels;
18  }
19   
20  /**
21   * 获得屏幕高度
22   *
23   * @param context
24   * @return
25   */
26  public static int getScreenHeight(Context context) {
27   return context.getResources().getDisplayMetrics().heightPixels;
28  }
29   
30 }

 

https://github.com/ganchuanpu/AndroidPopupWindowWithMask.git

 
 
 
 

我们今天的关于PopupWindow重叠Android 5.0上的软按钮的分享已经告一段落,感谢您的关注,如果您想了解更多关于Android 7.0 PopupWindow 显示错位问题、Android 7.0 PopupWindow 错位移位问题、android popupWindow、Android PopupWindow 增加半透明蒙层的相关信息,请在本站查询。

本文标签: