孔明讲完Dialog时,天又亮了。这已经是刘关张在孔明家的第三天了,这三天他们蹭吃蹭喝,如强盗过境般把孔明家能下口的东西吃了个精光。张飞数次想闯进孔明的茅房一探究竟,但都被刘备拦住,他的意思是硬闯不礼貌,还是等孔明睡着了的时候偷偷进去比较有礼节。
这天早上刘关张三人正琢磨孔明睡没睡着的时候,茅房门竟忽然开了。只见一个白衣书生,约莫十七八岁的样子,穿着人字拖跑了出来,冲三人喊道:“擦……擦……”
刘备摇了摇头说:“诸葛先生也太不讲究了,一见面不问声好也就算了,还满口脏话,这让我等好生失望,我们把你屋子搞成这样是我们不对,但你也没必要骂人呀。”
孔明急道:“擦……擦屁股纸给我一张先!”
关羽赶忙递上一卷,孔明接了之后又匆匆返回茅房去了。几分钟之后,茅房门又再度打开,只见孔明右手持一羽扇,左手拎着个笔记本,信步走出,“刚才的出场实在是有些对不起观众,大家就当没看见。”
刘关张三人强忍住笑,说道:“是的,我们什么都没看见!”
孔明扇了扇羽扇,说:“三位三顾茅房,看来对学习Android真的很有诚意。我夜观星象,知道近日必有三人求助于我,看来你们就是那三人。”
刘备问道:“原来我们的到来星星都知道了,太感动了,其实我们是真的很想学习移动开发的相关知识,诸葛先生如肯教我们,那是再好不过了,我们立即拜你为师!”
孔明说道:“拜师就不用啦,我就给你们当个技术顾问吧,我会给你强大的技术支持,不过可先说好,做起项目来代码我可是不写的,我只会给你们指明方向,具体实践你们自己完成,到时候赚了钱可得大家一起分。”
刘备说:“这没问题,二弟、三弟,快来拜见军师!”
关羽、张飞齐道:“军师!”
孔明道:“闲话不多说,我看你们Android基础太差,我这里有本小册子,你们拿去恶补一下!”
三人接过册子,只见上面写了一个蛮文单词:PopupWindow。
1、介绍
在Android开发中,时不时地可能要弹出个窗口或对话框,常用的Android对话框有两种: Dialog和PopupWindow。Dialog是一种非阻塞的对话框,它一般显示图标、标题、文本和按钮;PopupWindow是阻塞的对话框,只有由外部线程或PopupWindow本身执行退出操作。
孔明:什么是阻塞型对话框和非阻塞型对话框呢?简单来说就是说当对话框出现的时候,当前线程是否还在进行操作,不是则为前者,是则为后者。
|
PopupWindow有点类似于Dialog,相同点在于都是弹出窗口,并且都可以对其进行自定义,同时可以监听窗口里的组件。但它与Dialog又有很大的区别,PopupWindow只是弹出窗口,不会使宿主Activity组件失去焦点,也就是说PopupWindow弹出后,我们仍可以与宿主Activity进行交互,Dialog却不能做到这一点。PopupWindow弹出的位置可以有很多:按照有无偏移,可以分为无偏移和偏移两种;按照参照类型不同又可以分为相对某个控件的位置和父容器内部的相对位置两种。
2、PopupWindow的使用方法
本节将要介绍PopupWindow弹窗方面的相关知识。其实AndroidPopupWindow弹窗,也和Dialog对话框类似,PopupWindow能实现非模态的对话框效果,也可以用来装载View,它可以悬浮在当前活动的窗口上,并且不干扰用户对背后窗口的操作。
2.1、popupwindow的创建方式
PopupWindow常用的构造函数为:
PopupWindow(ViewcontentView, int width, int height, boolean focusable)
l contentView:用于显示PopupWindow的内容。
l width、height:分别用于设置PopupWindow的宽度和高度。
l focusable:设置PopupWindow的焦点。当设置为false时,是不接受返回键等按钮的事件;当设置为true时,PopupWindow可以接收返回键等按钮的事件。但是,设置为true之后,PopupWindow响应返回键按钮时,并不关闭。
除了在创建PopupWindow时设置以上的参数之外,也可以在构造完PopupWindow之后进行设置。例如,setContentView(ViewcontentView)设置布局,setWidth(int width)设置宽度,setHeight(int height)设置高度和setFocusable(booleanfocusable)设置能否聚焦。
孔明:注意了!如果PopupWindow已经显示,当我们使用这些set方法后,只有在PopupWindow下次显示时才能生效。
|
2.2、popupwindow的显示方式
PopupWindow常用的显示方式有两种:
showAsDropDown(View anchor, int xoff, int yoff):该方法以anchor为参考坐标系,规定PopupWindow出现的位置。参数anchor用于设置弹出的PopupWindow紧挨着的View组件;Xoff用于表示相对于View的左下角x轴上的偏移;yoff用于表示相对于View的左下角y轴上的偏移。
l showAtLocation(View parent, int gravity, int x, int y):该方法以parent为父布局,规定PopupWindow出现的位置。参数parent用于设置弹出的PopupWindow的父布局;参数gravity用于设置PopupWindow对应父布局的显示位置,例如当设置为“Gravity.LEFT | Gravity.TOP”时,表示PopupWindow显示在左上角;参数x,y和xoff,yoff类似,分别设置了x轴和y轴的偏移。
这两种显示方式看似相同,但是对特殊情况的处理方式却是不同的。当使用showAsDropDown()方法显示PopupWindow时,如果待显示的PopupWindow占用屏幕大于最多能给与的空间,showAsDropDown会试图尝试寻找ScrollView进行调整,即使找不到ScrollView,也会显示出来。而showAtLocation在这种情况下会自动剪短要显示的PopupWindow。
2.3、popupwindow的常用方法
除了上述所说的方法外,PopupWindow还有如表8-1所示的常用方法:
表8-1 PopupWindow的常用方法
常用方法 | 简要介绍 |
dismiss() | 关闭一个PopupWindow窗口,刚方法只有在使用showAsDropDown(View anchor, int xoff, int yoff)创建PopupWindow后才能被调用。 |
isShowing() | 查看PopupWindow是否已经在屏幕上显示。 |
setAnimationStyle(int animationStyle) | 设置自定义在PopupWindow上的动画,注意:这个方法只有在重启PopupWindow后才能生效,也可以在使用这个方法的后面加一个update()方法使之生效。 |
setBackgroundDrawable(Drawable background) | 设置PopupWindow的背景,只有在update PopupWindow后才能生效。 |
setOutsideTouchable(boolean touchable) | 设置PopupWindow是否响应点击在PopupWindow屏幕以外的事件,只有在update()后才能生效。 |
setTouchable(boolean touchable) | 设置PopupWindow是否响应点击在PopupWindow上的事件,否则,这个事件将被传给PopupWindow背后的窗口进行响应。 |
3、popupwindow的使用
3.1、简单的对话框
新建一个Android项目,在layout文件夹中编写布局文件main.xml,代码如下所示:
main.xml代码清单8-3-1:
<!--小飞飞:大哥家祖祖辈辈都有钱,他的曾祖,曾经是前清宫的二品带剪子护卫。-->
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:id="@+id/mainlayout">
<TextView
android:layout_width="fill_parent"
android:id="@+id/textView1"
android:layout_height="wrap_content"
android:text="欢迎信息" />
<Button
android:text="点击弹出对话框"
android:id="@+id/button1"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
</Button>
</LinearLayout>
该文件定义了一个文本框显示“欢迎信息”,并且定义了一个按钮用来弹出popupWindow。接着,再新建一个PopupWindow的布局文件popup.xml,代码如下所示:
popup.xml代码清单8-3-1:
<!--小备备:我这辈子最痛恨三种人:码农和不识数的人。-->
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<EditText android:text="Hello!PopupWindow弹出框!"
android:id="@+id/editText1"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
</EditText>
<LinearLayout android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/linearLayout1">
<Button android:text="确定"
android:id="@+id/button1_sure"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
</Button>
<Button android:text="取消"
android:id="@+id/button2_cancel"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
</Button>
</LinearLayout>
</LinearLayout>
MainActivity.java代码清单8-3-1:
/**
* @author孔明:自己选择45°仰视别人,就休怪他人135°俯视着看你。
*/
public class MainActivity extends Activity {
TextView mTextView;
Button mButton;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
mButton = (Button)findViewById(R.id.button1);
mTextView = (TextView)findViewById(R.id.textView1);
mButton.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
//调用自定义初始化PopupWindow方法
initPopWindow();
}
});
}
//在本方法中初始化PopupWindow
private void initPopWindow() {
// 加载popupWindow的布局文件
View contentView = LayoutInflater.from(getApplicationContext())
.inflate(R.layout.popup,null);
// 设置popupWindow的背景颜色
contentView.setBackgroundColor(Color.BLUE);
// 声明一个弹出框并为弹出框设定自定义的布局
final PopupWindow popupWindow = new PopupWindow(findViewById(R.id.mainlayout),
200, 300);
//设置PopupWindow布局
popupWindow.setContentView(contentView);
final EditText editText = (EditText)contentView.findViewById(R.id.editText1);
editText.setInputType(InputType.TYPE_CLASS_NUMBER);
// 该方法可以设定popupWindow获取焦点的能力。
popupWindow.setFocusable(true);
// 弹出PopupWindow
popupWindow.showAsDropDown(mButton);
Button button_sure = (Button)contentView.findViewById(R.id.button1_sure);
button_sure.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
//点击确定按钮后关闭PopupWIndow,并将输入值显示在TextView中
popupWindow.dismiss();
mTextView.setText("展示信息:" + editText.getText());
}
});
Button button_cancel = (Button)contentView.findViewById(R.id.button2_cancel);
button_cancel.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
// 点击取消关闭PopupWindow
popupWindow.dismiss();
}
});
}
}
上述代码中的popupWindow.setFocusable(true)方法用于设置焦点,该方法一定要在弹出popupWindow之前进行调用。当设置为true时,系统会捕获到焦点并将事件传送给popupWindow上的组件,当设置为false时,popupWindow将不能接受任何输入,默认为false。
运行程序,结果如图8-1所示:
3.2、简单菜单
接下来新建一个Android项目,使用PopupWindow实现菜单的功能。在layout文件夹中编写布局文件menu.xml,代码如下所示:
menu.xml代码清单8-3-2:<!--小羽羽:写代码需要坚持,因为你永远都不会知道,Bug和死机,哪个会先来!-->
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="horizontal">
<ImageView
android:id="@+id/icon_1"
android:background="@drawable/icon_1"
android:layout_width="40dp"
android:layout_height="40dp"/>
<ImageView
android:id="@+id/icon_2"
android:background="@drawable/icon_2"
android:layout_width="40dp"
android:layout_height="40dp"/>
<ImageView
android:id="@+id/icon_3"
android:background="@drawable/icon_3"
android:layout_width="40dp"
android:layout_height="40dp"/>
<ImageView
android:id="@+id/icon_4"
android:background="@drawable/icon_4"
android:layout_width="40dp"
android:layout_height="40dp"/>
</LinearLayout>
该布局文件定义了四个ImageView用来显示按钮的图标,并设置了它们的长宽和高度。接着,新建一个Activity,命名为MainActivity.java,具体代码如下所示:
MainActivity.java代码清单8-3-2:/**
* @author孔明:别和我谈事业,戒了!
*/
public class MainActivity extends Activity {
private PopupWindow mMenu;
private LayoutInflater mInflater;
private View mPopupLayout;
private View mMainLayout;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
// 实例化PopupWindow创建菜单
initMenu();
}
// 判断按键菜单的显示与隐藏
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
// 判断键盘输入为menu时
if (!mMenu.isShowing() && keyCode == KeyEvent.KEYCODE_MENU) {
show();
} else {
mMenu.dismiss();
}
if (keyCode == KeyEvent.KEYCODE_BACK&& mMenu.isShowing()) {
mMenu.dismiss();
}
return true;
}
// 实例化PopupWindow创建菜单
private void initMenu() {
// 获取LayoutInflater实例,用来设置PopupWindow布局
mInflater = (LayoutInflater)this.getSystemService(LAYOUT_INFLATER_SERVICE);
// 获取布局
mPopupLayout = mInflater.inflate(R.layout.menu, null);
mMainLayout = mInflater.inflate(R.layout.main, null);
// 设置popupWindow的布局
mMenu = new PopupWindow(mPopupLayout, WindowManager.LayoutParams.WRAP_CONTENT,
WindowManager.LayoutParams.WRAP_CONTENT);
}
// 显示菜单
private void show() {
// 设置在屏幕中的显示位置
mMenu.showAtLocation(mMainLayout, Gravity.BOTTOM | Gravity.CENTER_HORIZONTAL,
0, 0);
mMenu.update();
}
}
代码if (!mMenu.isShowing() && keyCode == KeyEvent.KEYCODE_MENU)用来判断是否按下了Menu键,当该事件被触发时,将会调用show()方法弹出popupwindow;当判断键盘的操作非Menu时调用mMenu.dismiss()消除弹出的popupwindow。
运行程序,结果如图8-2所示:图 利用PopupWindow生成简单的菜单
3.3、自定义动画
除了上述所说的功能,还可以利用PopupWindow作为一个图层,实现在Activity上的动画。首先,在layout布局文件夹中创建布局文件main.xml,代码如下所示:
main.xml代码清单8-3-3:
<!--小飞飞:不吃饱哪有力气减肥啊?-->
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<Button
android:id="@+id/btn"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="显示PopupWindow动画"/>
</LinearLayout>
该布局文件定义了一个按钮Button,用来启动PopupWindow的动画。接着,创建布局文件popupwindow.xml并在该文件中利用ImageView和TextView实现了三个按钮的效果,具体代码如下所示:
popupWindow.xml代码清单8-3-3:
<!--小备备:是呀,吃饱才有力气减肥啊!-->
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:id="@+id/layout_main">
<LinearLayout android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:gravity="center_horizontal"
android:clickable="true"
android:background="@drawable/state_btn_pressed"
android:layout_weight="1"
android:id="@+id/btn_0">
<ImageView android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:scaleType="fitCenter"
android:src="@drawable/ic_call">
</ImageView>
<TextView android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="#000000"
android:textSize="18px"
android:text="电话">
</TextView>
</LinearLayout>
<LinearLayout android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:gravity="center_horizontal"
android:clickable="true"
android:background="@drawable/state_btn_pressed"
android:layout_weight="1"
android:id="@+id/btn_1">
<ImageView android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:scaleType="fitCenter"
android:src="@drawable/ic_home">
</ImageView>
<TextView android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="#000"
android:textSize="18px"
android:text="空间">
</TextView>
</LinearLayout>
<LinearLayout android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:gravity="center_horizontal"
android:clickable="true"
android:background="@drawable/state_btn_pressed"
android:layout_weight="1"
android:id="@+id/btn_2">
<ImageView android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:scaleType="fitCenter"
android:src="@drawable/ic_sms">
</ImageView>
<TextView android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="#000"
android:textSize="18px"
android:text="短信">
</TextView>
</LinearLayout>
</LinearLayout>
该文件定义了三个按钮,分别为“电话”,“空间”和“菜单”。接着,在anim动画文件夹下建立两个动画的布局文件,分别为ani_in.xml和ani_out.xml,用来指定PopupWindow的弹入和弹出动画,具体代码如下所示:
ani_in.xml代码清单8-3-3:
<!--小羽羽:小飞飞减肥成功了啊!这个月只胖了二十斤!-->
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:oneshot="true">
<!--动画的具体设置请参看16回哼,我四岁就会看动画!-->
<translate android:interpolator="@android:anim/accelerate_decelerate_interpolator"
android:fromYDelta="0"
android:toYDelta="100"
android:duration="1000"
android:fillEnabled="true"
android:fillAfter="true"/>
<alpha
android:fromAlpha="0.0"
android:toAlpha="1.0"
android:duration="1000"/>
</set>
ani_out.xml文件代码清单8-3-3:
<!--小明明:小飞飞,你要减肥?学会放弃,才能拥有!-->
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:oneshot="true">
<translate android:interpolator="@android:anim/accelerate_decelerate_interpolator"
android:fromYDelta="0"
android:toYDelta="-100"
android:duration="1000"
android:fillEnabled="true"
android:fillAfter="true"/>
<alpha android:interpolator="@android:anim/decelerate_interpolator"
android:fromAlpha="1.0"
android:toAlpha="0.0"
android:duration="1000"/>
</set>
然后,在values文件夹下的styles.xml文件中引用刚才定义的动画效果,并分别设置为弹入和弹出动画,弹入的标签为“android:windowEnterAnimation”,弹出的标签为“android:windowExitAnimation”,具体代码如下所示:
styles.xml文件代码清单8-3-3:
<!--小飞飞:谁说我要减肥?我的身材,要胸有胸,要屁股有屁股!-->
<?xml version="1.0" encoding="utf-8"?>
<resources>
<style name="mytheme" parent="@android:style/Theme.Light">
<item name="android:windowNoTitle"> true </item>
</style>
<style name="PopupAnimation" parent="android:Animation">
<item name="android:windowEnterAnimation">@anim/ani_in</item>
<item name="android:windowExitAnimation">@anim/ani_out</item>
</style>
</resources>
最后,新建一个Activity,命名为AnimActivity.java,在该Activity里引用刚才定义的动画效果,具体代码如下所示:
AnimActivity.java文件代码清单8-3-3:
/**
* @author小飞飞:啊,最近身材保持不错,一直维持在150公斤!
孔明:那个称最大150公斤!
*/
public class AnimActivity extends Activity {
private View view;
private Button btn;
private PopupWindow mPopupWindow;
private View[] btns;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
btn = (Button)this.findViewById(R.id.btn);
// 设置按钮的事件
btn.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
// TODO Auto-generated method stub
// 弹出PopupWindow
showPopupWindow(btn);
}
});
// 设置PopupWindow
initPopupWindow(R.layout.popupwindow);
}
private void initPopupWindow(int resId) {
LayoutInflater mLayoutInflater = (LayoutInflater)getSystemService(LAYOUT_INFLATER_SERVICE);
view = mLayoutInflater.inflate(resId, null);
// 定义一个PopupWindow,显示位置为View
mPopupWindow = new PopupWindow(view, 400, LayoutParams.WRAP_CONTENT);
// 设置PopupWindow的背景
mPopupWindow.setBackgroundDrawable(getResources()
.getDrawable(R.drawable.bg_frame));
// 当点击PopupWindow以外的屏幕时,退出PopupWindow
mPopupWindow.setOutsideTouchable(true);
// 自定义动画
mPopupWindow.setAnimationStyle(R.style.PopupAnimation);
mPopupWindow.update();
mPopupWindow.setTouchable(true);
mPopupWindow.setFocusable(true);
btns = new View[3];
btns[0] = view.findViewById(R.id.btn_0);
btns[1] = view.findViewById(R.id.btn_1);
btns[2] = view.findViewById(R.id.btn_2);
btns[0].setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
//可以在这里设置点击事件
}
});
btns[1].setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
//可以在这里设置点击事件
}
});
btns[2].setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
//可以在这里设置点击事件
}
});
}
private void showPopupWindow(View view) {
if (!mPopupWindow.isShowing()) {
// 显示PopupWindow
mPopupWindow.showAtLocation(view, Gravity.CENTER, 0, 0);
}
}
}
上述代码的mPopupWindow.setAnimationStyle(R.style.PopupAnimation)方法引入了前面定义的动画(在style.xml文件里)。mPopupWindow.setOutsideTouchable(true)方法用于表示当点击到PopupWindow以外的屏幕时,系统会自动调用mPopupWindow.dismiss()方法消除PopupWindow。最后的运行结果如图8-3所示,弹窗实现了从屏幕中央向下移动并淡入淡出的效果。
图 利用PopupWindow自定义动画
4、玄德有话说
刘备:军师,我有一个问题!为何我的PopupWindow中的控件无法响应,点按钮没反应?
孔明:若需要监听PopupWindow里控件的事件,如PopupWindow里面一个按钮的事件,那么就需要调用方法setFocusable(true)获得焦点。并且在调用该方法后,可以通过点击Back(返回)按钮使PopupWindow消失。另外再告诉你一个秘密,调用方法setOutsideTouchable(true)后,点击PopupWindow外面的控件也可以使得PopupWindow消失噢。
刘备:不对啊,为何我设置了还是没反应呢?
孔明:你确定你按钮监听器都设置好并且写好了?做做断点调试看看走到了哪一步?
刘备:嗯……军师神算啊!我果然没设置按钮监听器!
孔明:这你都能忘,罚你做三百个蹲起!