程序员

android的PopupWindow简解

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

前言

注:为书写方便,本文的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显示出来,但是怎么消失,点击子项怎么产生效果我们还有指明,这个慢慢来。

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

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



    

.
.

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的源码

    /**
     * 

Create a new empty, non focusable popup window of dimension (0,0).

* *

The popup does not provide any background. This should be handled * by the content view.

*/ public PopupWindow() { this(null, 0, 0); } /** *

Create a new non focusable popup window which can display the * contentView. The dimension of the window are (0,0).

* *

The popup does not provide any background. This should be handled * by the content view.

* * @param contentView the popup's content */ public PopupWindow(View contentView) { this(contentView, 0, 0); } /** *

Create a new empty, non focusable popup window. The dimension of the * window must be passed to this constructor.

* *

The popup does not provide any background. This should be handled * by the content view.

* * @param width the popup's width * @param height the popup's height */ public PopupWindow(int width, int height) { this(null, width, height); } /** *

Create a new non focusable popup window which can display the * contentView. The dimension of the window must be passed to * this constructor.

* *

The popup does not provide any background. This should be handled * by the content view.

* * @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); } /** *

Create a new popup window which can display the contentView. * The dimension of the window must be passed to this constructor.

* *

The popup does not provide any background. This should be handled * by the content view.

* * @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代码:

    
       

    

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

        
    
        
        

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

    
    
        
        

设置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
本篇至此完。