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显示出来,但是怎么消失,点击子项怎么产生效果我们还有指明,这个慢慢来。

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

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

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

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,126评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,254评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,445评论 0 341
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,185评论 1 278
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,178评论 5 371
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,970评论 1 284
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,276评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,927评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,400评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,883评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,997评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,646评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,213评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,204评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,423评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,423评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,722评论 2 345

推荐阅读更多精彩内容