android中的dialog,以及activiy形式的dialog均是模态对话框,对话框不消失时,不能对其他页面进行操作,也就是其他页面不能获得焦点。而PopupWindow是非模态对话框,对话框显示的时候,其他界面仍然可以获得焦点,仍然可以进行点击等操作,同时也可以对对话框进行点击等操作。 

很好的例子就是输入法,通过查看源码就可以看到,其界面是几个popupwindow组成的。

三个关键设置
// 如果不设置PopupWindow的背景,有些版本就会出现一个问题:无论是点击外部区域还是Back键都无法dismiss弹框
popupWindow.setBackgroundDrawable(new ColorDrawable());
// setOutsideTouchable设置生效的前提是setTouchable(true)和setFocusable(false)
popupWindow.setOutsideTouchable(true);
// 设置为true之后,PopupWindow内容区域 才可以响应点击事件
popupWindow.setTouchable(true);

Activity

public class MainActivity extends ListActivity {
    private PopupWindow pop;
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        String[] array = { "在指定view的正左下方,无偏移", //
                "相对指定view正左下方,有偏移",//
                "Gravity.NO_GRAVITY:相对屏幕左上角,注意包含状态栏区域",//
                "Gravity.TOP:相对屏幕正上方居中",//
                "pop不会偏移出屏幕,偏移值过大时取临界值",//
                "Gravity.BOTTOM:相对屏幕正下方居中,注意包含虚拟按键区域",//
                "Gravity.BOTTOM:此时yoff为正时表示向上偏移(而非向下偏移)",//
                "出现在View的任意位置", };
        setListAdapter(new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1, new ArrayList<String>(Arrays.asList(array))));
        pop = new PopupWindow(this);
        pop.setFocusable(true);//必备设置1
        pop.setBackgroundDrawable(new ColorDrawable(0x00ff0000));//必备设置2
        pop.setOutsideTouchable(true);//setFocusable(true)后这个设置就没用了
        pop.setAnimationStyle(R.style.popAniStyle);//设置动画所对应的style 
    }
    private boolean b;
    @Override
    protected void onListItemClick(ListView l, View v, int position, long id) {
        b = !b;
        if (b) pop.setContentView(LayoutInflater.from(this).inflate(R.layout.view1, null));
        else pop.setContentView(LayoutInflater.from(this).inflate(R.layout.view2, null));
        switch (position) {
        case 0://在指定view的正左下方,无偏移
            pop.setWidth(LinearLayout.LayoutParams.WRAP_CONTENT);//必须设置宽和高
            pop.setHeight(LinearLayout.LayoutParams.WRAP_CONTENT);
            pop.showAsDropDown(v);
            break;
        case 1://相对指定view正左下方的位置,有偏移
            pop.setWidth(DensityUtils.getScreenWidth(this) - 20);
            pop.setHeight(LinearLayout.LayoutParams.WRAP_CONTENT);
            pop.showAsDropDown(v, 20, 10);
            break;
        case 2://相对于父控件的某个相对位置的偏移,默认的Gravity.NO_GRAVITY的参照物为【屏幕左上角】,注意包含状态栏区域
        case 3://若偏移值大于pop偏出屏幕的临界值,将取临界值。也即pop不可能会偏移出屏幕
        case 4://不管view设置的是哪一个,参照物都是整个屏幕的根布局
            pop.setWidth(DensityUtils.getScreenWidth(this) - 20);
            pop.setHeight(20);
            if (position == 2) pop.showAtLocation(v, Gravity.NO_GRAVITY, 20, DensityUtils.getStatusBarHeight(this) + 10);//Gravity.NO_GRAVITY:相对屏幕左上角
            else if (position == 3) pop.showAtLocation(v, Gravity.TOP, 10, DensityUtils.getStatusBarHeight(this) + 10);//Gravity.TOP:相对屏幕正上方居中
            else pop.showAtLocation(v, Gravity.TOP, 100, DensityUtils.getStatusBarHeight(this));//临界值为 20/2=10,偏移值大于10时仅偏移10
            break;
        case 5://Gravity.BOTTOM:相对屏幕正下方居中,注意包含虚拟按键区域,并且此时yoff为正时表示向上偏移(而非向下偏移)
        case 6://
            pop.setWidth(DensityUtils.getScreenWidth(this) - 20);
            pop.setHeight(LinearLayout.LayoutParams.WRAP_CONTENT);
            if (position == 5) pop.showAtLocation(v, Gravity.BOTTOM, 0, DensityUtils.getBottomBarHeight(this));//Gravity.BOTTOM:相对屏幕正下方居中
            else if (position == 6) pop.showAtLocation(v, Gravity.BOTTOM, -10, DensityUtils.getBottomBarHeight(this) + 10);//此时yoff为正时表示向上偏移
            break;
        case 7://
            startActivity(new Intent(this, SecondActivity.class));
            break;
        }
    }

}

Activity2

public class SecondActivity extends Activity {
    private int num = 0;
    private PopupWindow pop;
    private int[] location = new int[2];;
    private View contentView;
    @SuppressLint("InflateParams")
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.layout);
        contentView = LayoutInflater.from(this).inflate(R.layout.view2, null);
        pop = new PopupWindow(contentView, LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.WRAP_CONTENT);
        pop.setFocusable(true);//必备设置1
        pop.setBackgroundDrawable(new ColorDrawable(0x00ff0000));//必备设置2
        pop.setOutsideTouchable(true);//setFocusable(true)后这个设置就没用了
        pop.setAnimationStyle(R.style.popAniStyle);//设置动画所对应的style
    }
    public void onClick(View v) {
        v.getLocationOnScreen(location);// 获取锚点View在屏幕上的左上角坐标位置
        switch (num % 4) {
        case 0://右下
            pop.showAtLocation(v, Gravity.NO_GRAVITY, location[0] + v.getWidth(), location[1] + v.getHeight());
            break;
        case 1://右上
            pop.showAtLocation(v, Gravity.NO_GRAVITY, location[0] + v.getWidth(), location[1] - contentView.getHeight());
            break;
        case 2://左上
            pop.showAtLocation(v, Gravity.NO_GRAVITY, location[0] - contentView.getWidth(), location[1] - contentView.getHeight());
            break;
        default://左下
            pop.showAtLocation(v, Gravity.NO_GRAVITY, location[0] - contentView.getWidth(), location[1] + v.getHeight());
            break;
        }
        Toast.makeText(this, num % 4 + "", Toast.LENGTH_SHORT).show();
        num++;
    }

}

工具类

public class DensityUtils {
    //******************************************************************************************
    //                                                                                    单位转换
    //******************************************************************************************
    /**像素密度*/
    public static float getDisplayMetrics(Context context) {
        return context.getResources().getDisplayMetrics().density;
    }
    /**  dp 转成为 px     */
    public static int dp2px(Context context, float dpValue) {
        return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dpValue, context.getResources().getDisplayMetrics());
    }
    /**  px 转成为 dp     */
    public static int px2dp(Context context, float pxValue) {
        return (int) (pxValue / getDisplayMetrics(context) + 0.5f);
    }
    /** sp转px */
    public static int sp2px(Context context, float spVal) {
        return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, spVal, context.getResources().getDisplayMetrics());
    }
    /** px转sp */
    public static float px2sp(Context context, float pxVal) {
        return (pxVal / context.getResources().getDisplayMetrics().scaledDensity);
    }
    //******************************************************************************************
    //                                                                                屏幕宽高
    //******************************************************************************************
    /**  获取屏幕宽  */
    public static int getScreenWidth(Context context) {
        DisplayMetrics metric = new DisplayMetrics();
        ((WindowManager) context.getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay().getMetrics(metric);
        return metric.widthPixels;
    }
    /** 获取屏幕高,包含状态栏,但不包含虚拟按键,如1920屏幕只有1794  */
    public static int getScreenHeight(Context context) {
        DisplayMetrics metric = new DisplayMetrics();
        ((WindowManager) context.getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay().getMetrics(metric);
        return metric.heightPixels;
    }
    /**  获取屏幕宽  */
    public static int getScreenWidth2(Context context) {
        Point point = new Point();
        ((WindowManager) context.getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay().getSize(point);
        return point.x;
    }
    /**  获取屏幕高,包含状态栏,但不包含某些手机最下面的【HOME键那一栏】,如1920屏幕只有1794  */
    public static int getScreenHeight2(Context context) {
        Point point = new Point();
        ((WindowManager) context.getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay().getSize(point);
        return point.y;
    }
    /**  获取屏幕原始尺寸高度,包括状态栏以及虚拟功能键高度  */
    public static int getAllScreenHeight(Context context) {
        Display display = ((WindowManager) context.getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay();
        try {
            DisplayMetrics displayMetrics = new DisplayMetrics();
            Method method = Class.forName("android.view.Display").getMethod("getRealMetrics", DisplayMetrics.class);
            method.invoke(display, displayMetrics);
            return displayMetrics.heightPixels;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return 0;
    }
    //******************************************************************************************
    //                                                                        状态栏、标题栏、虚拟按键
    //******************************************************************************************
    /** 状态栏高度,单位px,一般为25dp  */
    public static int getStatusBarHeight(Context context) {
        int height = 0;
        int resourceId = context.getResources().getIdentifier("status_bar_height", "dimen", "android");
        if (resourceId > 0) {
            height = context.getResources().getDimensionPixelSize(resourceId);
        }
        return height;
    }
    /** 状态栏高度,单位px,【注意】要在onWindowFocusChanged中获取才可以 */
    public static int getStatusBarHeight2(Activity activity) {
        Rect rect = new Rect();
        //DecorView是Window中的最顶层view,可以从DecorView获取到程序显示的区域,包括标题栏,但不包括状态栏。所以状态栏的高度即为显示区域的top坐标值
        activity.getWindow().getDecorView().getWindowVisibleDisplayFrame(rect);
        return rect.top;
    }
    /**标题栏的高度,【注意】要在onWindowFocusChanged中获取才可以*/
    public static int getTitleBarHeight(Activity activity) {
        int contentTop = activity.getWindow().findViewById(Window.ID_ANDROID_CONTENT).getTop();
        return contentTop - getStatusBarHeight(activity);
    }
    /**获取 虚拟按键的高度     */
    public static int getBottomBarHeight(Context context) {
        return getAllScreenHeight(context) - getScreenHeight(context);
    }

}

常用API

构造方法

  • public PopupWindow(View contentView)
  • public PopupWindow(Context context)
  • public PopupWindow(View contentView, int width, int height)
  • public PopupWindow(View contentView, int width, int height, boolean focusable)
参数说明
  • contentView为要显示的view;PopupWindow没有默认布局,它不会像AlertDialog那样只setTitle就能弹出来一个框,所以contentView必须设置。
  • width和height为要显示的view的宽和高,值为像素值或MATCHT_PARENT、WRAP_CONTENT;如果View是从xml得到的,那么xml的第一层view的大小属性将被忽略;必须设置宽和高,否则显示不出来。
  • focusable为是否能获得焦点
举例
View contentView = LayoutInflater.from(this).inflate(R.layout.popuplayout, null);  
方法1:PopupWindow popupWindow = new PopupWindow(contentview,LayoutParams.WRAP_CONTENT,LayoutParams.WRAP_CONTENT);   
方法2:PopupWindow popupWindow = new PopupWindow(contentview);
方法3:PopupWindwo popWnd = PopupWindow (context); 
            popWnd.setContentView(contentView);
            popupWindow.setWidth(LayoutParams.MATCHT_PARENT);           
            popupWindow.setHeight(LayoutParams.WRAP_CONTENT);

show方法

  • showAsDropDown(View anchor)  相对某个控件的位置(正左下方),无偏移。最终调用的是下面的方法
  • showAsDropDown(View anchor, int xoff, int yoff)  相对某个控件的位置,有偏移,xoff表示x轴的偏移,正值表示向左,负值表示向右;yoff表示相对y轴的偏移,正值是向下,负值是向上;最终调用的是下面的方法
  • showAsDropDown(View anchor, int xoff, int yoff, int gravity)  【注:这个方法貌似不起作用】相对某个控件的某个位置,有偏移。gravity的默认值为DEFAULT_ANCHORED_GRAVITY= Gravity.TOP | Gravity.START
  • showAtLocation(View parent, int gravity, int x, int y)  相对于父控件(不管view设置的是哪一个,参照物都是整个屏幕的根布局)的某个位置(例如正中央Gravity.CENTER,下方Gravity.BOTTOM等),可以设置偏移或无偏移。
  1. /**
  2. * Displays the content view in a popup window anchored to the corner of
  3. * another view. The window is positioned according to the specified
  4. * gravity and offset by the specified x and y coordinates.
  5. * <p>
  6. * If there is not enough room on screen to show the popup in its entirety,
  7. * this method tries to find a parent scroll view to scroll. If no parent
  8. * view can be scrolled, the specified vertical gravity will be ignored and
  9. * the popup will anchor itself such that it is visible.
  10. * <p>
  11. * If the view later scrolls to move <code>anchor</code> to a different
  12. * location, the popup will be moved correspondingly.
  13. *
  14. * @param anchor the view on which to pin the popup window
  15. * @param xoff A horizontal offset from the anchor in pixels
  16. * @param yoff A vertical offset from the anchor in pixels
  17. * @param gravity Alignment of the popup relative to the anchor
  18. *
  19. * @see #dismiss()
  20. */
  21. public void showAsDropDown(View anchor, int xoff, int yoff, int gravity)
  22. /**
  23. * <p>
  24. * Display the content view in a popup window at the specified location. If the popup window
  25. * cannot fit on screen, it will be clipped. See {@link android.view.WindowManager.LayoutParams}
  26. * for more information on how gravity and the x and y parameters are related. Specifying
  27. * a gravity of {@link android.view.Gravity#NO_GRAVITY} is similar to specifying
  28. * <code>Gravity.LEFT | Gravity.TOP</code>.
  29. * </p>
  30. *
  31. * @param parent a parent view to get the {@link android.view.View#getWindowToken()} token from
  32. * @param gravity the gravity which controls the placement of the popup window
  33. * @param x the popup's x location offset
  34. * @param y the popup's y location offset
  35. */
  36. public void showAtLocation(View parent, int gravity, int x, int y)
showAtLocation的parent参数可以很随意,只要是activity中的view都可以。

setFocusable

setFocusable(boolean focusable) 设置PopupWindow是否获取焦点
  1. /**
  2. * <p>Changes the focusability of the popup window. When focusable, the
  3. * window will grab the focus from the current focused widget if the popup
  4. * contains a focusable {@link android.view.View}. By default a popup
  5. * window is not focusable.</p>
  6. *
  7. * <p>If the popup is showing, calling this method will take effect only
  8. * the next time the popup is shown or through a manual call to one of
  9. * the {@link #update()} methods.</p>
  10. *
  11. * @param focusable true if the popup should grab focus, false otherwise.
  12. *
  13. * @see #isFocusable()
  14. * @see #isShowing()
  15. * @see #update()
  16. */
  17. public void setFocusable(boolean focusable) {
  18. mFocusable = focusable;
  19. }

setOutsideTouchable

  1. /**
  2. * <p>Controls whether the pop-up will be informed of touch events outside
  3. * of its window. This only makes sense for pop-ups that are touchable
  4. * but not focusable, which means touches outside of the window will
  5. * be delivered to the window behind. The default is false.</p>
  6. *
  7. * <p>If the popup is showing, calling this method will take effect only
  8. * the next time the popup is shown or through a manual call to one of
  9. * the {@link #update()} methods.</p>
  10. *
  11. * @param touchable true if the popup should receive outside
  12. * touch events, false otherwise
  13. *
  14. * @see #isOutsideTouchable()
  15. * @see #isShowing()
  16. * @see #update()
  17. */
  18. public void setOutsideTouchable(boolean touchable) {
  19. mOutsideTouchable = touchable;
  20. }
2017-2-20

附件列表

popupWindow 用法总结 控制位置的更多相关文章

  1. Android popupwindow 弹出的位置问题

    在Android开发中,需要用到PopupWindow这个类.在初始化完成,显示之前,都需要获得这个对象的width,height去计算popupWindow弹出的位置. 这个时候会发现取得的widt ...

  2. Android中PopupWindow用法

    参考资料链接:http://developer.android.com/reference/android/widget/PopupWindow.html 在Android中有很多级别的Window, ...

  3. Android PopupWindow怎么合理控制弹出位置(showAtLocation)

    说到PopupWindow,应该都会有种熟悉的感觉,使用起来也很简单 // 一个自定义的布局,作为显示的内容 Context context = null; // 真实环境中要赋值 int layou ...

  4. 屏幕旋转时调用PopupWindow update方法更新位置失效的问题及解决方案

       接到一个博友的反馈,在屏幕旋转时调用PopupWindow的update方法失效.使用场景如下:在一个Activity中监听屏幕旋转事件,在Activity主布局文件中有个按钮点击弹出一个Pop ...

  5. PopupWindow计算弹出位置

    1.首先自定义PopupWindow    popWindowView= LinearLayout.inflate(context, R.layout.popupWindow,null);    po ...

  6. opencv播放视屏并控制位置

    原文地址:http://blog.csdn.net/augusdi/article/details/9000592 cvGetCaptureProperty是我们需要使用到的获取视频属性的函数. do ...

  7. 一些特殊ACII码的用法 在控制台中覆盖显示、刷新显示和删除字符

    很好奇怎么实现在控制台中不换行直接显示新的信息把旧的替换掉,于是找到了两个ACII码字符,他们可以帮助实现. 一个是‘\b’字符,这个字符是backspace,即删除上一个字符,于是可以清除以显示的旧 ...

  8. Unity 3D 中实现对物体 位置(position) 旋转(rotation) 大小(scale) 的全面控制

    今天分享一些基础控制的脚本 1.位置(Position): 控制位置很简单,首先要知道要在xyz哪几个轴上移动,确定好后定义代表着那些轴的移动变量,速度(m_speed在函数外定义为全局变量)然后通过 ...

  9. android PopupWindow显示位置

    PopupWindow的显示及位置设置 window.showAtLocation(parent, Gravity.RIGHT | Gravity.BOTTOM, 10,10); 第一个参数指定Pop ...

随机推荐

  1. Oracle表空间不足ORA-01654

    v在往数据表里插入数据时,出现了ORA-01654: 索引 SSERVICE.IX_MSI_WDR_INPUT_1 无法通过 1024 (在表空间 USERD 中) 扩展的错误信息,原来是数据量太大, ...

  2. 面试的65个回答技巧-适用于BAT公司

    互联网职业群分享的资料,里面大多是BAT公司的人,很多是猎头.这些技巧对于职场人来说,是非常宝贵的. 1.请你自我介绍一下你自己? 回答提示:一般人回答这个问题过于平常,只说姓名.年龄.爱好.工作经验 ...

  3. MySQL常用知识

    1.MySQL常用引擎有哪些? A:MySQL常用的引擎有InnoDB.MyISAM.Memory,默认时InnoDB InnoDB:磁盘表,支持事务,支持行级锁,B+Tree索引 优点:具有良好的A ...

  4. Android之 解析XML文件(1)—— Pull解析

    (以下文章基本照抄<第一行代码>) 解析XML文件有很多方法,这里主要讲Pull解析和SAX解析.这篇文章主要是讲Pull解析. 一.Pull解析参考代码 先上代码: private vo ...

  5. Android之 内容提供器(2)——创建自己的内容提供器将数据共享出去

    创建自己的内容提供器非常简单,只需要新建一个类继承ContentProvider类,通过实现ContentProvider的增删改查的方法向内容提供器中增删数据. 1 ContentProvider简 ...

  6. Array.reduce()方法的使用

    起因是学习异步函数的串行与并行写法时,发现reduce方法可以简化写法,然后看到一篇博客里面这样一段代码: var array = [1, [2, [3, 4], 5], 6]; function f ...

  7. java学习助手

    感谢大家使用Java学习助手! 打一开始,这应用就是全开源,希望大家自觉遵循开源协议,还Android开发一片净土(国内的情况你懂的) 接下来都不会更频繁更新文章,大家更应该把注意力放在基础的模块那里 ...

  8. 关于django 京东淘宝 混合搜索实现原理

    混合搜索在各大网站如京东.淘宝都有应用,他们的原理都是什么呢?本博文将为你介绍它们的实现过程. 混合搜索的原理,用一句话来说就是:关键字id进行拼接. 混合搜索示例: 数据库设计: 视频方向: 1 2 ...

  9. 深入理解javascript作用域系列第五篇

    前面的话 对于执行环境(execution context)和作用域(scope)并不容易区分,甚至很多人认为它们就是一回事,只是高程和犀牛书关于作用域的两种不同翻译而已.但实际上,它们并不相同,却相 ...

  10. FastReport.Net使用:[11]公共对象属性介绍

    公共对象属性介绍 1.Left(左),Top(上),Height(高度),Width(宽度) Left和Top,用来控制对象的位置:Height和Width用来控制对象的大小. 2.Anchor(基准 ...