还有其他关于PopupWindow的文章:
【达内课程】PopupWindow
文章目录
- 一、 概述
- 二、简单示例(showAtLocation显示窗体)
- 三、简单示例(showAsDropDown显示窗体)
- 四、为菜单添加阴影
- 五、为PopupWindow添加动画
- 六、弹出时屏幕背景变暗
- 七、使用总结
- 八、常见函数讲解
- 九、为什么要强制代码设置PopupWindow的Height、Width
- 十、一些问题
一、 概述
1、PopupWindow与AlertDialog的区别
最关键的区别是AlertDialog不能指定显示位置,只能默认显示在屏幕最中间(当然也可以通过设置WindowManager参数来改变位置)。而PopupWindow是可以指定显示位置的,随便哪个位置都可以,更加灵活
相同点
这两个都是弹窗
不同点
- popupwindow在显示之前一定要设置宽高,Dialog不用
- popupwindow默认不会响应物理键盘的返回键,只有设置了popup.setfocusable(true)时才会响应,Dialog会响应物理键盘
- Popupwindow不会给页面其他的部分添加蒙层,而Dialog会
- Popupwindow没有标题,Dialog默认有标题,可以通过dialog.requestWindowFeature(Window.FEATURE_NO_TITLE);取消标题
- 二者显示的时候都要设置Gravity。如果不设置,Dialog默认是Gravity.CENTER
- 二者都有默认的背景,都可以通过setBackgroundDrawable(new ColorDrawable(android.R.color.transparent));去掉
- 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图
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全面屏显示异常