PopupWindow的一些干货

1,329 阅读8分钟

还有其他关于PopupWindow的文章:
【达内课程】PopupWindow

文章目录

一、 概述

1、PopupWindow与AlertDialog的区别

最关键的区别是AlertDialog不能指定显示位置,只能默认显示在屏幕最中间(当然也可以通过设置WindowManager参数来改变位置)。而PopupWindow是可以指定显示位置的,随便哪个位置都可以,更加灵活

相同点
这两个都是弹窗

不同点

  1. popupwindow在显示之前一定要设置宽高,Dialog不用
  2. popupwindow默认不会响应物理键盘的返回键,只有设置了popup.setfocusable(true)时才会响应,Dialog会响应物理键盘
  3. Popupwindow不会给页面其他的部分添加蒙层,而Dialog会
  4. Popupwindow没有标题,Dialog默认有标题,可以通过dialog.requestWindowFeature(Window.FEATURE_NO_TITLE);取消标题
  5. 二者显示的时候都要设置Gravity。如果不设置,Dialog默认是Gravity.CENTER
  6. 二者都有默认的背景,都可以通过setBackgroundDrawable(new ColorDrawable(android.R.color.transparent));去掉
  7. AlertDialog是非阻塞式对话框:AlertDialog弹出时,后台还可以做事情;而PopupWindow是阻塞式对话框:PopupWindow弹出时,程序会等待,在PopupWindow退出前,程序一直等待,只有当我们调用了dismiss方法的后,PopupWindow退出,程序才会向下执行
    (这两种表现最直接的解释方式:AlertDialog弹出时背景是黑的,当我们点击背景时AlertDialog会消失,这证明了程序不仅响应会AlertDialog,还会响应其他操作,这证明AlertDialog是非阻塞式对话框;popupwindow弹出时背景没什么变化,当我们点击背景时程序没有响应,只允许我们操作popupwindow,其他操作被阻塞)

2、PopupWindow的相关函数

(1)构造函数

//方法一:  
public PopupWindow (Context context);  
//方法二:  
public PopupWindow(View contentView); 
//方法三:  
public PopupWindow(View contentView, int width, int height);
//方法四:  
public PopupWindow(View contentView, int width, int height, boolean focusable);

看这里有四个构造函数,但要生成一个PopupWindow最基本的三个条件是一定要设置的:View contentView,int width, int height

所以,如果使用方法一来构造PopupWindow,那完整的构造代码应该是这样的:

View contentView = LayoutInflater.from(MainActivity.this).inflate(R.layout.popuplayout, null);  
PopupWindwo popWnd = PopupWindow (context);  
popWnd.setContentView(contentView);  
popWnd.setWidth(ViewGroup.LayoutParams.WRAP_CONTENT);  
popWnd.setHeight(ViewGroup.LayoutParams.WRAP_CONTENT);  

(2)显示函数

//相对某个控件的位置(正左下方),无偏移  
showAsDropDown(View anchor):  
//相对某个控件的位置,有偏移;xoff表示x轴的偏移,正值表示向左,负值表示向右;yoff表示相对y轴的偏移,正值是向下,负值是向上;  
showAsDropDown(View anchor, int xoff, int yoff):  
//相对于父控件的位置(例如正中央Gravity.CENTER,下方Gravity.BOTTOM等),可以设置偏移或无偏移  
showAtLocation(View parent, int gravity, int x, int y):  

这里有两种显示方式:
1、显示在某个指定控件的下方
showAsDropDown(View anchor):
showAsDropDown(View anchor, int xoff, int yoff);
2、指定父视图,显示在父控件的某个位置(Gravity.TOP,Gravity.RIGHT等)
showAtLocation(View parent, int gravity, int x, int y);

(3)其它函数

//用于不需要的时候,将窗体隐藏掉
public void dismiss()  
public void setFocusable(boolean focusable)  
public void setTouchable(boolean touchable)  
public void setOutsideTouchable(boolean touchable)  
public void setBackgroundDrawable(Drawable background)  

二、简单示例(showAtLocation显示窗体)

在这里插入图片描述

1、popupwindow布局(popuplayout1.xml)

<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"
    android:background="#e5e5e5"
    android:orientation="vertical">

    <TextView
        android:id="@+id/pop_computer"
        android:layout_width="match_parent"
        android:layout_height="30dp"
        android:text="计算机"
        android:gravity="center"/>

    <View
        android:layout_width="match_parent"
        android:layout_height="1dp"
        android:background="#000"/>

    <TextView
        android:id="@+id/pop_financial"
        android:layout_width="match_parent"
        android:layout_height="30dp"
        android:text="金融"
        android:gravity="center"/>

    <View
        android:layout_width="match_parent"
        android:layout_height="1dp"
        android:background="#000"/>

    <TextView
        android:id="@+id/pop_manage"
        android:layout_width="match_parent"
        android:layout_height="30dp"
        android:text="管理"
        android:gravity="center"/>

    <View
        android:layout_width="match_parent"
        android:layout_height="1dp"/>

</LinearLayout>

2、MainActivity关键代码

@Override
    public void onClick(View view) {
        switch (view.getId()){
            case R.id.btn1:
                showPopupWindow1();
                break;
            case R.id.pop_computer:
            case R.id.pop_financial:
            case R.id.pop_manage:
                Toast.makeText(this,"Hello Popupwindow",Toast.LENGTH_SHORT).show();
                break;
        }
    }

    private void showPopupWindow1() {
        //利用LayoutInflater获取R.layout.popuplayout1对应的View,然后利用我们上面所讲的构造函数三来生成mPopWindow
        View contentView = LayoutInflater.from(MainActivity.this).inflate(R.layout.popuplayout1, null);
        //如果在代码中重新设置了popupWindow的宽和高,那就以代码中所设置为准。
        PopupWindow mPopWindow = new PopupWindow(contentView, LinearLayoutCompat.LayoutParams.MATCH_PARENT,
                LinearLayoutCompat.LayoutParams.WRAP_CONTENT, true);
        mPopWindow.setContentView(contentView);
        //设置各个控件的点击响应
        TextView tv1 = contentView.findViewById(R.id.pop_computer);
        TextView tv2 = contentView.findViewById(R.id.pop_financial);
        TextView tv3 = contentView.findViewById(R.id.pop_manage);
        tv1.setOnClickListener(this);
        tv2.setOnClickListener(this);
        tv3.setOnClickListener(this);
        //我们将R.layout.main做为它的父容器。将其显示在BOTTOM的位置
        View rootview = LayoutInflater.from(MainActivity.this).inflate(R.layout.activity_main,
                null);
        mPopWindow.showAtLocation(rootview, Gravity.BOTTOM, 0, 0);
    }

三、简单示例(showAsDropDown显示窗体)

在这里插入图片描述

1、popupwindow布局(popuplayout2.xml)

<?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="@drawable/pop_bg">

    <LinearLayout
        android:layout_width="150dp"
        android:layout_height="wrap_content"
        android:orientation="vertical">

        <TextView
            android:layout_width="match_parent"
            android:layout_height="30dp"
            android:gravity="center"
            android:text="计算机" />

        <View
            android:layout_width="match_parent"
            android:layout_height="1dp"
            android:background="#ccc" />

        <TextView
            android:layout_width="match_parent"
            android:layout_height="30dp"
            android:gravity="center"
            android:text="金融" />

        <View
            android:layout_width="match_parent"
            android:layout_height="1dp"
            android:background="#ccc" />

        <TextView
            android:layout_width="match_parent"
            android:layout_height="30dp"
            android:gravity="center"
            android:text="管理" />

        <View
            android:layout_width="match_parent"
            android:layout_height="1dp" />

    </LinearLayout>
</LinearLayout>

其中pop_bug是.9图

.9图

2、MainActivity关键代码

这里在右上角添加了菜单按钮,点击按钮显示popupwindow在其下方

public class MainActivity extends AppCompatActivity implements View.OnClickListener {
   ......

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        getMenuInflater().inflate(R.menu.menu_option,menu);
        return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        switch (item.getItemId()){
            case R.id.btn2:
                showPopupWindow2();
                break;
        }
        return super.onOptionsItemSelected(item);
    }
	
    private void showPopupWindow2() {
        //使用的构造方法二来生成的PopupWindow实例
        View contentView = LayoutInflater.from(MainActivity.this).inflate(R.layout.popuplayout2, null);
        PopupWindow mPopWindow = new PopupWindow(contentView);
        mPopWindow.setWidth(ViewGroup.LayoutParams.WRAP_CONTENT);
        mPopWindow.setHeight(ViewGroup.LayoutParams.WRAP_CONTENT);
        mPopWindow.setFocusable(true);
        //使用showAsDropDown()显示PopupWindow
        mPopWindow.showAsDropDown(findViewById(R.id.btn2));
    }
}

四、为菜单添加阴影

Android 6.0显示效果如下:
在这里插入图片描述
1、popupwindow布局(popuplayout3.xml)

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="#66000000">

    <LinearLayout
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:background="@drawable/pop_bg"
        android:orientation="vertical"
        android:layout_alignParentRight="true"
        >

        <TextView
            android:id="@+id/pop_computer"
            style="@style/pop_text_style"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="计算机"/>

        <View
            android:layout_width="match_parent"
            android:layout_height="1dp"
            android:background="#ccc"/>

        <TextView
            android:id="@+id/pop_financial"
            style="@style/pop_text_style"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="金融"/>

        <View
            android:layout_width="match_parent"
            android:layout_height="1dp"
            android:background="#ccc"/>

        <TextView
            android:id="@+id/pop_manage"
            style="@style/pop_text_style"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="管理"/>

        <View
            android:layout_width="match_parent"
            android:layout_height="1dp"/>

    </LinearLayout>
</RelativeLayout>

2、MainActivity代码

private void showPopupWindow3() {
        View contentView = LayoutInflater.from(MainActivity.this).inflate(R.layout.popuplayout3, null);
        View relativelayout = contentView.findViewById(R.id.relativelayout);
        final PopupWindow mPopWindow = new PopupWindow(contentView);
        mPopWindow.setWidth(ViewGroup.LayoutParams.MATCH_PARENT);
        mPopWindow.setHeight(ViewGroup.LayoutParams.MATCH_PARENT);
        mPopWindow.showAsDropDown(findViewById(R.id.btn2));
        relativelayout.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                mPopWindow.dismiss();
            }
        });
    }

五、为PopupWindow添加动画

在这里插入图片描述

1、进入时的动画:(popup_enter.xml)

<?xml version="1.0" encoding="utf-8"?>  
<set xmlns:android="http://schemas.android.com/apk/res/android">
    <scale
        android:duration="240"
        android:fromXScale="0.0"
        android:fromYScale="0.0"
        android:pivotX="100%"
        android:pivotY="0%"
        android:toXScale="1.0"
        android:toYScale="1.0" />
    <alpha
        android:duration="240"
        android:fromAlpha="0.0"
        android:toAlpha="1.0" />
</set>

2、退出时动画(popup_exit.xml)

<?xml version="1.0" encoding="utf-8"?>  
<set xmlns:android="http://schemas.android.com/apk/res/android">
    <scale
        android:duration="300"
        android:fromXScale="1.0"
        android:fromYScale="1.0"
        android:pivotX="100%"
        android:pivotY="0%"
        android:toXScale="0.0"
        android:toYScale="0.0" />
    <alpha
        android:duration="300"
        android:fromAlpha="1.0"
        android:toAlpha="0.0" />
</set>

3、生成对应的style

    <style name="popupwindow_style">
        <item name="android:windowEnterAnimation">@anim/popup_enter</item>
        <item name="android:windowExitAnimation">@anim/popup_exit</item>
    </style>

4、使用AnimationStyle

mPopWindow.setAnimationStyle(R.style.popupwindow_style);  

六、弹出时屏幕背景变暗

在这里插入图片描述
MainActivity关键代码

//弹出时屏幕变暗
    private void showPopupWindow4() {
        View contentView = LayoutInflater.from(MainActivity.this).inflate(R.layout.popuplayout2, null);
        final PopupWindow mPopWindow = new PopupWindow(contentView);
        mPopWindow.setWidth(ViewGroup.LayoutParams.WRAP_CONTENT);
        mPopWindow.setHeight(ViewGroup.LayoutParams.WRAP_CONTENT);
        mPopWindow.setFocusable(true);
        mPopWindow.showAsDropDown(findViewById(R.id.btn2));

        backgroundAlpha(0.75f);
        mPopWindow.setOnDismissListener(new PopupWindow.OnDismissListener() {
            @Override
            public void onDismiss() {
                backgroundAlpha(1f);
            }
        });
    }

    // 设置屏幕透明度
    public void backgroundAlpha(float bgAlpha) {
        WindowManager.LayoutParams lp = getWindow().getAttributes();
        lp.alpha = bgAlpha; // 0.0~1.0
        getWindow().setAttributes(lp);
    }

七、使用总结

一个常规Popupwindow的使用,代码如下:

PopupWindow popupWindow = new PopupWindow();

popupWindow.setWidth(ViewGroup.LayoutParams.WRAP_CONTENT);
popupWindow.setHeight(ViewGroup.LayoutParams.WRAP_CONTENT);
popupWindow.setContentView(View.inflate(this, R.layout.layout_popup, null));

popupWindow.setBackgroundDrawable(new ColorDrawable(0x00000000));
popupWindow.setFocusable(true);
popupWindow.setOutsideTouchable(false);

popupWindow.setAnimationStyle(R.style.anim_menu_bottombar);

popupWindow.showAsDropDown(bt_popup, 0, 0);

首先肯定是创建一个PopupWindow对象了,当然,它肯定还有许多带参数的构造方法
然后就是设置三连,设置宽高,设置布局View。 如果想要显示一个弹窗, 这三句话是必须的
然后popupWindow.setBackgroundDrawable(new ColorDrawable(0x00000000));这个很有意思。在高版本的android中(比如8.0),可以不写。但是低版本就不行了(比如4.1), 低版本不写的话, 会导致点击返回或者是屏幕其它地方无法取消弹窗。所以稳妥起见还是加上,并设置一个透明色
popupWindow.setFocusable(true);是比较重要的,一般都为true,也就是弹窗之后,焦点就到了弹窗,你再点击其它地方,弹窗失去焦点就会消失
popupWindow.setOutsideTouchable(false);这句在之前那句为true的前提下, true 和 false效果几乎一样
再往下是添加一个动画效果,你可以用默认的,或者自定义
最后一句显示弹窗,默认对齐左下,后面两个参数是偏移值

八、常见函数讲解

1、setTouchable(boolean touchable)

设置PopupWindow是否响应touch事件,默认是true,如果设置为false,所有touch事件无响应,包括点击事件

2、setFocusable(boolean focusable)

该函数的意义表示,PopupWindow是否具有获取焦点的能力,默认为False。一般来讲是没有用的,因为普通的控件是不需要获取焦点的,而对于EditText则不同,如果不能获取焦点,那么EditText将是无法编辑的

mPopWindow.setFocusable(true);  

在点击EditText的时候,会弹出编辑框。

mPopWindow.setFocusable(false);  

点击EditText没有出现任何反应

3、setOutsideTouchable(boolean touchable)

这个函数的意义,就是指,PopupWindow以外的区域是否可点击,即如果点击PopupWindow以外的区域,PopupWindow是否会消失。

mPopWindow.setOutsideTouchable(true); 

九、为什么要强制代码设置PopupWindow的Height、Width

1、源码角度

我们明明可以看到 layout_width 我们设置为了"fill_parent",layout_height设置为了“fill_parent”;为什么非要我们在代码中还要再设置一遍。如果我们没有设置 width 和 height,那 mWidth 和 mHeight 将会取默认值0!!!!所以当我们没有设置 width 和 height 时,并不是我们的窗体没有弹出来,而是因为他们的 width 和 height 都是0了

2、布局角度

<RelativeLayout  
        xmlns:android="http://schemas.android.com/apk/res/android"  
        android:layout_width="fill_parent"  
        android:layout_height="fill_parent">  
    <LinearLayout  
            android:layout_width="match_parent"  
            android:layout_height="wrap_content"  
            android:background="@drawable/pop_bg"  
            android:orientation="vertical"  
            android:paddingBottom="2dp"  
            android:layout_alignParentRight="true">  
  
        <TextView  
                android:id="@+id/pop_computer"  
                android:layout_width="wrap_content"  
                android:layout_height="wrap_content"  
                style="@style/pop_text_style"  
                android:text="计算机"/>  
    </LinearLayout>  
</RelativeLayout>  

TextView的显示大小是由谁来决定的?
是由它自己的布局 layout_width=“wrap_content”、layout_height="wrap_content"来决定的吗?
当然不是!它的大小,应该是在它父控件的基础上决定的。即 LinearLayout 的显示大小确定了以后,才能确定TextView的大小
这好比,如果 LinearLayout 的大小是全屏的,那 TextView 的大小就由它自己来决定了,那如果 LinearLayout 的大小只有一像素呢?那 TextView 的所显示的大小无论它自己怎么设置,最大也就显示一像素
所以我们的结论来了:控件的大小,是建立在父控件大小确定的基础上的
那同样:LinearLayout 的大小确定是要靠 RelativeLayout 来决定
那问题来了:RelativeLayout的大小靠谁决定呢?
当然是它的父控件了

我们的contentView是怎么来的呢?

View contentView = LayoutInflater.from(MainActivity.this).inflate(R.layout.popuplayout, null);  

直接inflate出来的,我们对它没有设置根结点
那问题来了?它的大小由谁来解决呢?
好像没有谁能决定了,因为他没有父结点。那它到底是多大呢?未知…
所以只有通过代码让用户去手动设置了!所以这就是为什么非要用户设置 width 和 height 的原因了

十、一些问题

Popupwindow showAsDropDown全面屏显示异常

Popupwindow showAsDropDown全面屏显示异常

blog.csdn.net/harvic88092…
github.com/zaaach/TopR…

blog.csdn.net/harvic88092…
www.imooc.com/article/679…