本文出自 “阿敏其人” 简书博客,转载或引用请注明出处。
前言
注:为书写方便,本文的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显示出来,但是怎么消失,点击子项怎么产生效果我们还有指明,这个慢慢来。
先按照上面的三步曲附上简单示例代码:
三步曲第一:
写一个布局文件
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="#ff0000"
android:orientation="vertical">
android:id="@+id/btn_xixi"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="5dp"
android:text="第一项"
android:textSize="18sp" />
android:id="@+id/btn_hehe"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="5dp"
android:text="第二项"
android:textSize="18sp" />
.
.
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中模式。
4、关于popup的动画
利用下面这个方法就可以指定
public void setAnimationStyle(int animationStyle)
可以选用系统提供的
popWindow.setAnimationStyle(android.R.style.Animation_InputMethod);
可以使用自定义的进出动画:
android:windowEnterAnimation表示进入窗口动画
android:windowExitAnimation表示窗口退出动画
在res/values/style.xml代码:
@anim/popup_enter
@anim/popup_exit
.
.
在res/anim/popup_enter.xml声明所需进入动画
android:fromYScale="0.6"android:toYScale="1.0"android:pivotX="50%"
android:pivotY="50%"android:duration="1000"/>
android:fromAlpha="0.0"android:toAlpha="1.0"android:duration="1000"/>
在res/anim/popup_exit.xml声明所需退出动画
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"/>
android:interpolator="@android:anim/accelerate_interpolator"
android:fromAlpha="1.0"
android:toAlpha="0.0"
android:duration="500"/>
设置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