android的PopupWindow简解

作者: 阿敏其人 | 来源:发表于2016-01-31 12:01 被阅读1572次

    本文出自 “阿敏其人” 简书博客,转载或引用请注明出处。

    前言

    注:为书写方便,本文的popup代表PopupWindow。

    概述
    PopupWindow在android.widget包下,弹出窗口的形式展示。官方文档对该控件的描述
    “一个弹出窗口控件,可以用来显示任意视图(View),而且会浮动在当前 活动(activity)的顶部”。

    PopupWindow可以让我们实现多种自定义控件,例如:menu、alertdialog等弹窗似的View。

    与Dialog的对比
    AlertDialog 和PopupWindow的不同点:

    AlertDialog 的位置固定,PopupWindow 的位置是自定义的
    AlertDialog 是非阻塞线程的,而PopupWindow 是阻塞线程的。

    Popup的样子

    Paste_Image.png

    再来一个例子

    Paste_Image.png

    二、简单使用——展示

    我们先把一个popup,给展示出来。

    二、1 展示popup的三步曲

    简单来说,我们的popup长什么样子,我们就需要使用一个写一个xml布局文件指明清楚。
    最简单的三步曲

    1、写一个item布局文件
    2、利用popup的构造函数把item作为参数传进去。
    3、调用popup的showAsDropDown或者showAtLocation方法将popup显示出来。

    上面这三部是最基本最简单,但是这样是不够的,通过上面这和三步我们只是让popup显示出来,但是怎么消失,点击子项怎么产生效果我们还有指明,这个慢慢来。

    先按照上面的三步曲附上简单示例代码:

    三步曲第一:
    写一个布局文件

    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:background="#ff0000"
        android:orientation="vertical">
        <Button
            android:id="@+id/btn_xixi"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:padding="5dp"
            android:text="第一项"
            android:textSize="18sp" />
        <Button
            android:id="@+id/btn_hehe"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:padding="5dp"
            android:text="第二项"
            android:textSize="18sp" />
    </LinearLayout>
    

    .
    .

    Paste_Image.png

    .
    .

    三步曲 第二:
    利用构造函数关联

    popupWindow = new PopupWindow(popupView, ViewGroup.LayoutParams.WRAP_CONTENT,ViewGroup.LayoutParams.WRAP_CONTENT,true);
    

    .
    .
    三步曲 第三:

    调用方法展示popup

     popupWindow.showAsDropDown(view);
    
    

    .
    .

    public class MainActivity extends Activity {
        private TextView mTv;
        private Context mContext;
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            mContext= this;
            mTv = (TextView) findViewById(R.id.mTv);
            mTv.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    initPopupWindow(v);
                }
            });
        }
        PopupWindow popupWindow;
        private void initPopupWindow(View view) {
            if(popupWindow == null){
                View popupView = LayoutInflater.from(MainActivity.this).inflate(R.layout.item_popip,null);
                // 三部曲第二  构造函数关联
                popupWindow = new PopupWindow(popupView, ViewGroup.LayoutParams.WRAP_CONTENT,ViewGroup.LayoutParams.WRAP_CONTENT,true);
            }
            
            // 三部曲第三   展示popup
            popupWindow.showAsDropDown(view);
        }
    }
    

    .
    .

    通过上面的三部曲,我们就可以通过点击对应的View展示popup了

    Paste_Image.png

    .
    .

    三步曲走完了,先不着急往下看,我们先看看三步曲中涉及到
    1、构造函数
    2、展示popup的方法

    开说。
    .
    .

    二、2 PopupWindow的构造函数

    PopupWindow的构造函数,5个。

    Paste_Image.png

    参数最长的 public PopupWindow(View contentView, int width, int height, boolean focusable) 才是日常使用的重点,其他的不怎么重要。

    第一种,一个空的,没有焦点的Popup

    第二种,没有焦点不能指定位置的popup

    第三种,空的popup

    第四种,没有焦点的popup

    其实前4种都不常见,常见的是最后一种,第五种
    第五种:可以指定父亲,位置,是否具备焦点。

    public PopupWindow(View contentView, int width, int height, boolean focusable)
    这个构造方法才重点!!

    附上google的源码

        /**
         * <p>Create a new empty, non focusable popup window of dimension (0,0).</p>
         *
         * <p>The popup does not provide any background. This should be handled
         * by the content view.</p>
         */
        public PopupWindow() {
            this(null, 0, 0);
        }
        /**
         * <p>Create a new non focusable popup window which can display the
         * <tt>contentView</tt>. The dimension of the window are (0,0).</p>
         *
         * <p>The popup does not provide any background. This should be handled
         * by the content view.</p>
         *
         * @param contentView the popup's content
         */
        public PopupWindow(View contentView) {
            this(contentView, 0, 0);
        }
        /**
         * <p>Create a new empty, non focusable popup window. The dimension of the
         * window must be passed to this constructor.</p>
         *
         * <p>The popup does not provide any background. This should be handled
         * by the content view.</p>
         *
         * @param width the popup's width
         * @param height the popup's height
         */
        public PopupWindow(int width, int height) {
            this(null, width, height);
        }
        /**
         * <p>Create a new non focusable popup window which can display the
         * <tt>contentView</tt>. The dimension of the window must be passed to
         * this constructor.</p>
         *
         * <p>The popup does not provide any background. This should be handled
         * by the content view.</p>
         *
         * @param contentView the popup's content
         * @param width the popup's width
         * @param height the popup's height
         */
        public PopupWindow(View contentView, int width, int height) {
            this(contentView, width, height, false);
        }
        /**
         * <p>Create a new popup window which can display the <tt>contentView</tt>.
         * The dimension of the window must be passed to this constructor.</p>
         *
         * <p>The popup does not provide any background. This should be handled
         * by the content view.</p>
         *
         * @param contentView the popup's content
         * @param width the popup's width
         * @param height the popup's height
         * @param focusable true if the popup can be focused, false otherwise
         */
        public PopupWindow(View contentView, int width, int height, boolean focusable) {
            if (contentView != null) {
                mContext = contentView.getContext();
                mWindowManager = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
            }
            setContentView(contentView);
            setWidth(width);
            setHeight(height);
            setFocusable(focusable);
        }
    

    .
    .

    二、2 展示方法:showAsDropDown和showAtLocation

    Paste_Image.png

    anchored是锚的意思

    方法一:
    showAsDropDown(View anchor)
    出现的指定的锚View的左下角位置,也就是以锚View的左下角作为popup关联的布局文件自身的左上角的起始点

    * Display the content view in a popup window anchored to the bottom-left
    * corner of the anchor view. If there is not enough room on screen to show
    * the popup in its entirety, this method tries to find a parent scroll
    * view to scroll. If no parent scroll view can be scrolled, the
    * bottom-left corner of the popup is pinned at the top left corner of the
    * anchor view.
    
    

    还是看图吧,直接。

    Paste_Image.png

    方法二:

    public void showAsDropDown(View anchor, int xoff, int yoff)

    出现在锚View的左下角,根据指定的x和y产生偏移

    一切照旧,只改一行代码

    popupWindow.showAsDropDown(view,30,150);
    查看效果

    Paste_Image.png

    方法三:
    public void showAsDropDown(View anchor, int xoff, int yoff, int gravity) (基本忽略)

    指定锚View,可以设定x和y的偏移量,而且,可以指定相对父控件的位置,(例如正中央Gravity.CENTER,下方Gravity.BOTTOM等),可以设置偏移或无偏移

    注意:这个方法是API 19以后才提供的,谨慎采用,如果我们想指定偏移量和相对父控件的位置,那么可以用另外一个方法。也就是接下来要介绍的这个方法。

    方法四:

    重点方法
    public void showAtLocation(View parent, int gravity, int x, int y)

    指定锚View,可以设定x和y的偏移量,而且,可以指定相对父控件的位置,(例如正中央Gravity.CENTER,下方Gravity.BOTTOM等),可以设置偏移或无偏移。这段描述和前面的方法三一样,他们可以做的事情一样,但是showAtLocation是API 1就已经提供的了。

    一定注意gravity是相对锚View父控件。

    popupWindow.showAtLocation(view, Gravity.CENTER,0, 0);

    Paste_Image.png

    再来一个,利用|指定popup出现在锚View的父控件的右下角

    popupWindow.showAtLocation(view, Gravity.BOTTOM|Gravity.RIGHT,0, 0);

    Paste_Image.png

    .
    .

    三、关于Popup的各种设置

    1、关于点击popup之外的地方让popup消失

    需要两个方法一起使用才能产生效果

        // =======  两者结合才能让popup点击外部消失
        popupWindow.setOutsideTouchable(true);
        popupWindow.setBackgroundDrawable(new BitmapDrawable());
        // =======  两者结合才能让popup点击外部消失
    

    2、关于焦点问题 .setFocusable(true);

    我们看到,不管是微信右上角的点击弹出popup还是其他的什么软件,当我们的popup已经弹出来了,这是我们按下back(返回键),会发现是popup消失,而是不是退出当前的activity,这说明,当前程序(比如微信)在popup出现的时候,PopupWindow弹出后,所有的触屏和物理按键都有PopupWindows处理。所以第一次back键是先退出popup,第二次按下back键才是退出当前activity。

    但是popup这个具有处理触屏和物理按键的能力不是与生俱来的,需要设置 popupWindow.setFocusable(true); ,这里不单单应该理解为具备焦点,而且让popup具备优先的交互响应等级。

    如果 .setFocusable(false); 那么如果当前popup已经展示着了,这是按下back键,结果是 popup和activity一起退出。

    3、关于popup的和软键盘的关系

    利用 setSoftInputMode 这个方法可以指定,一共有9中模式。

    详情可以看 下面的链接了解
    Android中的windowSoftInputMode属性详解 9种popup和软件盘的关系
    http://blog.csdn.net/twoicewoo/article/details/7384398

    4、关于popup的动画

    利用下面这个方法就可以指定
    public void setAnimationStyle(int animationStyle)

    可以选用系统提供的
    popWindow.setAnimationStyle(android.R.style.Animation_InputMethod);
    可以使用自定义的进出动画:

    这里提供一份自定义参考代码
    http://blog.csdn.net/heng615975867/article/details/8893655

    android:windowEnterAnimation表示进入窗口动画
    android:windowExitAnimation表示窗口退出动画

    在res/values/style.xml代码:

    <?xml version="1.0"encoding="utf-8"?>    
    <resources>       
     
    <style name="PopupAnimation"parent="android:Animation"mce_bogus="1">         
            <item name="android:windowEnterAnimation">@anim/popup_enter</item>    
            <item name="android:windowExitAnimation">@anim/popup_exit</item>    
        </style>    
    </resources> 
    

    .
    .
    在res/anim/popup_enter.xml声明所需进入动画

    <?xml version="1.0"encoding="utf-8"?>        
    <set xmlns:android="http://schemas.android.com/apk/res/android">    
        <scale android:fromXScale="0.6"android:toXScale="1.0" 
            android:fromYScale="0.6"android:toYScale="1.0"android:pivotX="50%" 
            android:pivotY="50%"android:duration="1000"/>    
        <alpha android:interpolator="@android:anim/decelerate_interpolator" 
            android:fromAlpha="0.0"android:toAlpha="1.0"android:duration="1000"/>    
    </set> 
    

    在res/anim/popup_exit.xml声明所需退出动画

    <?xml version="1.0"encoding="utf-8"?>    
    <set xmlns:android="http://schemas.android.com/apk/res/android">    
        <scale    
            android:fromXScale="1.0" 
            android:toXScale="0.5" 
            android:fromYScale="1.0" 
            android:toYScale="0.5" 
            android:pivotX="50%" 
            android:pivotY="50%" 
            android:duration="500"/>    
        <alpha    
            android:interpolator="@android:anim/accelerate_interpolator" 
            android:fromAlpha="1.0" 
            android:toAlpha="0.0" 
            android:duration="500"/>    
    </set> 
    

    设置popwindow的位置及动画
    popupWindow.setAnimationStyle(R.style.PopupAnimation);

    6. popup的消失

    popupWindow.dismiss();
    这个没什么好讲的

    7、popup子项的点击相应,这个么没什么好说的

    .
    .
    基本上popup开发中所常用的涉及到的上看都提及了,现在附上完整demo代码

    public class MainActivity extends Activity {
        private TextView mTv;
        private Context mContext;
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            mContext= this;
            mTv = (TextView) findViewById(R.id.mTv);
            mTv.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    initPopupWindow(v);
                }
            });
        }
        PopupWindow popupWindow;
        private void initPopupWindow(View view) {
            if(popupWindow == null){
                View popupView = LayoutInflater.from(MainActivity.this).inflate(R.layout.item_popip,null);
                // 三部曲第二  构造函数关联
                popupWindow = new PopupWindow(popupView, ViewGroup.LayoutParams.WRAP_CONTENT,ViewGroup.LayoutParams.WRAP_CONTENT,true);
                initClick(popupView);
            }
            // =======  两者结合才能让popup点击外部消失
            popupWindow.setOutsideTouchable(true);
            popupWindow.setBackgroundDrawable(new BitmapDrawable());
            // =======  两者结合才能让popup点击外部消失
            // 让popup占有优先于activity的交互响应能力,不单单是焦点问题。
            popupWindow.setFocusable(true);
            // 设置动画  这里选用系统提供的
            popupWindow.setAnimationStyle(android.R.style.Animation_InputMethod);
            // popup和软键盘的关系
            // 三部曲第三   展示popup
            popupWindow.showAsDropDown(view);
        }
        private void initClick(View popupView) {
            Button btn1 = (Button) popupView.findViewById(R.id.btn1);
            Button btn2 = (Button) popupView.findViewById(R.id.btn2);
            btn1.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    Toast.makeText(MainActivity.this,"点击第一项",Toast.LENGTH_SHORT).show();
                }
            });
            btn2.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    Toast.makeText(MainActivity.this,"点击第二项",Toast.LENGTH_SHORT).show();
                }
            });
        }
    }
    
    GIF.png

    .
    .
    simple_demo_dowmload
    本篇至此完。

    相关文章

      网友评论

      • imdy777:感谢作者,写得非常详细!感谢
      • 6ee72519b51d:挺好,对于长期不用popupwindow的我来说帮助很大
      • jsonxcz:我也照这样写了,为什么popupWindow没有动画展示出来

      本文标题:android的PopupWindow简解

      本文链接:https://www.haomeiwen.com/subject/edcskttx.html