[关闭]
@946898963 2021-12-30T02:11:09.000000Z 字数 14727 阅读 571

PopupWindow

Android学习笔记


前言

Android的对话框有两种:PopupWindow和AlertDialog。它们的不同点在于:

  • AlertDialog的位置固定,而PopupWindow的位置可以随意
  • AlertDialog是非阻塞线程的,而PopupWindow是阻塞线程的

PopupWindow基本使用

PopupWindow这个类用来实现一个弹出框,可以使用任意布局的View作为其内容,这个弹出框是悬浮在当前activity之上的。

PopupWindow的位置按照有无偏移分,可以分为偏移无偏移两种;按照参照物的不同,可以分为相对于某个控件(Anchor锚)相对于父控件。具体如下

  • showAsDropDown(View anchor):相对某个控件的位置(正左下方),无偏移
  • showAsDropDown(View anchor, int xoff, int yoff):相对某个控件的位置,有偏移
  • showAtLocation(View parent, int gravity, int x, int y):相对于父控件的位置(例如正中央Gravity.CENTER,下方Gravity.BOTTOM等),可以设置偏移或无偏移

弹出框布局

  1. <?xml version="1.0" encoding="utf-8"?>
  2. <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
  3. android:layout_width="match_parent"
  4. android:layout_height="wrap_content"
  5. android:background="#FFBBFFBB"
  6. android:orientation="vertical" >
  7. <TextView
  8. android:layout_width="match_parent"
  9. android:layout_height="wrap_content"
  10. android:padding="10dp"
  11. android:text="Hello My Window"
  12. android:textSize="20sp" />
  13. <Button
  14. android:id="@+id/button1"
  15. android:layout_width="match_parent"
  16. android:layout_height="wrap_content"
  17. android:padding="10dp"
  18. android:text="Button"
  19. android:textSize="20sp" />
  20. </LinearLayout>

 Activity的布局中只有一个按钮,按下后会弹出框,Activity代码如下:

  1. package com.example.hellopopupwindow;
  2. import android.os.Bundle;
  3. import android.app.Activity;
  4. import android.content.Context;
  5. import android.util.Log;
  6. import android.view.LayoutInflater;
  7. import android.view.MotionEvent;
  8. import android.view.View;
  9. import android.view.View.OnClickListener;
  10. import android.view.View.OnTouchListener;
  11. import android.view.ViewGroup.LayoutParams;
  12. import android.widget.Button;
  13. import android.widget.PopupWindow;
  14. import android.widget.Toast;
  15. public class MainActivity extends Activity {
  16. private Context mContext = null;
  17. @Override
  18. protected void onCreate(Bundle savedInstanceState) {
  19. super.onCreate(savedInstanceState);
  20. setContentView(R.layout.activity_main);
  21. mContext = this;
  22. Button button = (Button) findViewById(R.id.button);
  23. button.setOnClickListener(new View.OnClickListener() {
  24. @Override
  25. public void onClick(View view) {
  26. showPopupWindow(view);
  27. }
  28. });
  29. }
  30. private void showPopupWindow(View view) {
  31. // 一个自定义的布局,作为显示的内容
  32. View contentView = LayoutInflater.from(mContext).inflate(
  33. R.layout.pop_window, null);
  34. // 设置按钮的点击事件
  35. Button button = (Button) contentView.findViewById(R.id.button1);
  36. button.setOnClickListener(new OnClickListener() {
  37. @Override
  38. public void onClick(View v) {
  39. Toast.makeText(mContext, "button is pressed",
  40. Toast.LENGTH_SHORT).show();
  41. }
  42. });
  43. final PopupWindow popupWindow = new PopupWindow(contentView,
  44. LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT, true);
  45. popupWindow.setTouchable(true);
  46. popupWindow.setTouchInterceptor(new OnTouchListener() {
  47. @Override
  48. public boolean onTouch(View v, MotionEvent event) {
  49. Log.i("mengdd", "onTouch : ");
  50. return false;
  51. // 这里如果返回true的话,touch事件将被拦截
  52. // 拦截后 PopupWindow的onTouchEvent不被调用,这样点击外部区域无法dismiss
  53. }
  54. });
  55. // 如果不设置PopupWindow的背景,无论是点击外部区域还是Back键都无法dismiss弹框
  56. // 我觉得这里是API的一个bug
  57. popupWindow.setBackgroundDrawable(getResources().getDrawable(
  58. R.drawable.selectmenu_bg_downward));
  59. // 设置好参数之后再show
  60. popupWindow.showAsDropDown(view);
  61. }
  62. }

弹出框的布局中有一个TextView和一个Button,Button点击后显示Toast,如图:
效果图

第一次实现的时候遇到了问题,就是弹出框不会在按下Back键的时候消失,点击弹框外区域也没有正常消失,搜索了一下,都说只要设置背景就好了。然后我就找了个图片,果然弹框能正常dismiss了(见注释)。

在inflate布局文件的时候可能会有问题,解决参考
http://note.youdao.com/share/?id=8464f9ca89ec93efc4dc89b09263906f&type=note

PopupWindow特殊用法

实现从底部弹出或滑出选择菜单或窗口

效果一

本实例弹出窗口主要是继承PopupWindow类来实现的弹出窗体,布局可以根据自己定义设计。弹出效果主要使用了translate和alpha样式实现,具体实习如下:
第一步:设计弹出窗口xml:

  1. <?xml version="1.0" encoding="utf-8"?>
  2. <RelativeLayout
  3. xmlns:android="http://schemas.android.com/apk/res/android"
  4. android:layout_width="fill_parent"
  5. android:layout_height="wrap_content"
  6. android:gravity="center_horizontal"
  7. android:orientation="vertical"
  8. >
  9. <LinearLayout
  10. android:id="@+id/pop_layout"
  11. android:layout_width="fill_parent"
  12. android:layout_height="wrap_content"
  13. android:gravity="center_horizontal"
  14. android:orientation="vertical"
  15. android:layout_alignParentBottom="true"
  16. android:background="@drawable/btn_style_alert_dialog_background"
  17. >
  18. <Button
  19. android:id="@+id/btn_take_photo"
  20. android:layout_marginLeft="20dip"
  21. android:layout_marginRight="20dip"
  22. android:layout_marginTop="20dip"
  23. android:layout_width="fill_parent"
  24. android:layout_height="wrap_content"
  25. android:text="拍照"
  26. android:background="@drawable/btn_style_alert_dialog_button"
  27. android:textStyle="bold"
  28. />
  29. <Button
  30. android:id="@+id/btn_pick_photo"
  31. android:layout_marginLeft="20dip"
  32. android:layout_marginRight="20dip"
  33. android:layout_marginTop="5dip"
  34. android:layout_width="fill_parent"
  35. android:layout_height="wrap_content"
  36. android:text="从相册选择"
  37. android:background="@drawable/btn_style_alert_dialog_button"
  38. android:textStyle="bold"
  39. />
  40. <Button
  41. android:id="@+id/btn_cancel"
  42. android:layout_marginLeft="20dip"
  43. android:layout_marginRight="20dip"
  44. android:layout_marginTop="15dip"
  45. android:layout_marginBottom="15dip"
  46. android:layout_width="fill_parent"
  47. android:layout_height="wrap_content"
  48. android:text="取消"
  49. android:background="@drawable/btn_style_alert_dialog_cancel"
  50. android:textColor="#ffffff"
  51. android:textStyle="bold"
  52. />
  53. </LinearLayout>
  54. </RelativeLayout>

第二步:创建SelectPicPopupWindow类继承PopupWindow:

  1. package com.example.picpopupwindow;
  2. import android.app.Activity;
  3. import android.content.Context;
  4. import android.graphics.drawable.ColorDrawable;
  5. import android.view.LayoutInflater;
  6. import android.view.MotionEvent;
  7. import android.view.View;
  8. import android.view.View.OnClickListener;
  9. import android.view.View.OnTouchListener;
  10. import android.view.ViewGroup.LayoutParams;
  11. import android.widget.Button;
  12. import android.widget.PopupWindow;
  13. public class SelectPicPopupWindow extends PopupWindow {
  14. private Button btn_take_photo, btn_pick_photo, btn_cancel;
  15. private View mMenuView;
  16. public SelectPicPopupWindow(Activity context,OnClickListener itemsOnClick) {
  17. super(context);
  18. LayoutInflater inflater = (LayoutInflater) context
  19. .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
  20. mMenuView = inflater.inflate(R.layout.alert_dialog, null);
  21. btn_take_photo = (Button) mMenuView.findViewById(R.id.btn_take_photo);
  22. btn_pick_photo = (Button) mMenuView.findViewById(R.id.btn_pick_photo);
  23. btn_cancel = (Button) mMenuView.findViewById(R.id.btn_cancel);
  24. //取消按钮
  25. btn_cancel.setOnClickListener(new OnClickListener() {
  26. public void onClick(View v) {
  27. //销毁弹出框
  28. dismiss();
  29. }
  30. });
  31. //设置按钮监听
  32. btn_pick_photo.setOnClickListener(itemsOnClick);
  33. btn_take_photo.setOnClickListener(itemsOnClick);
  34. //设置SelectPicPopupWindow的View
  35. this.setContentView(mMenuView);
  36. //设置SelectPicPopupWindow弹出窗体的宽
  37. this.setWidth(LayoutParams.FILL_PARENT);
  38. //设置SelectPicPopupWindow弹出窗体的高
  39. this.setHeight(LayoutParams.WRAP_CONTENT);
  40. //设置SelectPicPopupWindow弹出窗体可点击
  41. this.setFocusable(true);
  42. //设置SelectPicPopupWindow弹出窗体动画效果
  43. this.setAnimationStyle(R.style.AnimBottom);
  44. //实例化一个ColorDrawable颜色为半透明
  45. ColorDrawable dw = new ColorDrawable(0xb0000000);
  46. //设置SelectPicPopupWindow弹出窗体的背景
  47. this.setBackgroundDrawable(dw);
  48. //mMenuView添加OnTouchListener监听判断获取触屏位置如果在选择框外面则销毁弹出框
  49. mMenuView.setOnTouchListener(new OnTouchListener() {
  50. public boolean onTouch(View v, MotionEvent event) {
  51. int height = mMenuView.findViewById(R.id.pop_layout).getTop();
  52. int y=(int) event.getY();
  53. if(event.getAction()==MotionEvent.ACTION_UP){
  54. if(y<height){
  55. dismiss();
  56. }
  57. }
  58. return true;
  59. }
  60. });
  61. }
  62. }

第三步:编写MainActivity类实现测试:

  1. package com.example.picpopupwindow;
  2. import android.app.Activity;
  3. import android.os.Bundle;
  4. import android.view.Gravity;
  5. import android.view.View;
  6. import android.view.View.OnClickListener;
  7. import android.widget.TextView;
  8. public class MainActivity extends Activity {
  9. //自定义的弹出框类
  10. SelectPicPopupWindow menuWindow;
  11. @Override
  12. public void onCreate(Bundle savedInstanceState) {
  13. super.onCreate(savedInstanceState);
  14. setContentView(R.layout.activity_main);
  15. TextView tv = (TextView) this.findViewById(R.id.text);
  16. //把文字控件添加监听,点击弹出自定义窗口
  17. tv.setOnClickListener(new OnClickListener() {
  18. public void onClick(View v) {
  19. //实例化SelectPicPopupWindow
  20. menuWindow = new SelectPicPopupWindow(MainActivity.this, itemsOnClick);
  21. //显示窗口
  22. menuWindow.showAtLocation(MainActivity.this.findViewById(R.id.main), Gravity.BOTTOM|Gravity.CENTER_HORIZONTAL, 0, 0); //设置layout在PopupWindow中显示的位置
  23. }
  24. });
  25. }
  26. //为弹出窗口实现监听类
  27. private OnClickListener itemsOnClick = new OnClickListener(){
  28. public void onClick(View v) {
  29. menuWindow.dismiss();
  30. switch (v.getId()) {
  31. case R.id.btn_take_photo:
  32. break;
  33. case R.id.btn_pick_photo:
  34. break;
  35. default:
  36. break;
  37. }
  38. }
  39. };
  40. }

第四:运行效果如下:
此处输入图片的描述

源码下载链接 : http://pan.baidu.com/s/1nuejKRB

效果二

自定义一个位于底部、占满屏幕的DiaglogFragment

  1. package rfidlib.com.fangaiwujiwu.view;
  2. import android.app.Dialog;
  3. import android.os.Bundle;
  4. import android.support.v4.app.DialogFragment;
  5. import android.view.Gravity;
  6. import android.view.View;
  7. import android.view.View.OnClickListener;
  8. import android.view.Window;
  9. import android.view.WindowManager;
  10. import android.widget.ImageButton;
  11. import android.widget.ListView;
  12. import java.util.Date;
  13. import rfidlib.com.fangaiwujiwu.R;
  14. public class DatePickerDialog extends DialogFragment implements OnClickListener {
  15. private ImageButton mCancelBtn;
  16. private ImageButton mAcceptBtn;
  17. private ListView listView;
  18. private OnDatePickerClickListener mOnDatePickerClickListener;
  19. public void setOnDatePickerClickListener(OnDatePickerClickListener l) {
  20. mOnDatePickerClickListener = l;
  21. }
  22. public interface OnDatePickerClickListener {
  23. public void onCancelClick();
  24. public void onAcceptClick(Date date);
  25. }
  26. @Override
  27. public Dialog onCreateDialog(Bundle savedInstanceState) {
  28. // 使用不带theme的构造器,获得的dialog边框距离屏幕仍有几毫米的缝隙。
  29. // Dialog dialog = new Dialog(getActivity());
  30. Dialog dialog = new Dialog(getActivity(), R.style.CustomDatePickerDialog);
  31. dialog.requestWindowFeature(Window.FEATURE_NO_TITLE); // must be called before set content 去掉这句代码,在这个例子中也没有影响
  32. dialog.setContentView(R.layout.dialog_datepicker);//设置Dialog的布局
  33. dialog.setCanceledOnTouchOutside(true);//点击屏幕之外的地方Dialog会消失
  34. // 设置靠近屏幕底部。
  35. Window window = dialog.getWindow();
  36. WindowManager.LayoutParams wlp = window.getAttributes();
  37. wlp.gravity = Gravity.BOTTOM;
  38. wlp.width = WindowManager.LayoutParams.MATCH_PARENT;//必须要进行设置,虽然style中已经设置了宽度match_parent,但是如果不加这句代码,距离 //边框还是有距离,什么原因不清楚
  39. window.setAttributes(wlp);
  40. mCancelBtn = (ImageButton) dialog.findViewById(R.id.dialog_dashboard_date_cancel);
  41. mAcceptBtn = (ImageButton) dialog.findViewById(R.id.dialog_dashboard_date_accept);
  42. mCancelBtn.setOnClickListener(this);
  43. mAcceptBtn.setOnClickListener(this);
  44. return dialog;
  45. }
  46. @Override
  47. public void onClick(View v) {
  48. int id = v.getId();
  49. if (mOnDatePickerClickListener != null) {
  50. if (id == R.id.dialog_dashboard_date_cancel) {
  51. mOnDatePickerClickListener.onCancelClick();
  52. } else if (id == R.id.dialog_dashboard_date_accept) {
  53. /* mOnDatePickerClickListener.onAcceptClick(cal.getTime());*/
  54. }
  55. }
  56. }
  57. }

在代码中使用

  1. DatePickerDialog mDatePickerDialog = new DatePickerDialog();
  2. if (mDatePickerDialog.isVisible()) {
  3. mDatePickerDialog.dismiss();
  4. } else {
  5. mDatePickerDialog.show(getSupportFragmentManager(), "date");
  6. }
  1. <style name="CustomDatePickerDialog" parent="@style/AppTheme">
  2. <item name="android:layout_width">match_parent</item>
  3. <item name="android:layout_height">wrap_content</item>
  4. <item name="android:windowIsFloating">true</item><!--设置Dialog悬浮在Activity上,如果设置为false,效果图2见下-->
  5. </style>
  1. <?xml version="1.0" encoding="utf-8"?>
  2. <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
  3. android:layout_width="match_parent"
  4. android:layout_height="wrap_content"
  5. android:orientation="vertical"
  6. >
  7. <FrameLayout
  8. android:layout_width="match_parent"
  9. android:layout_height="wrap_content"
  10. android:padding="8dp" >
  11. <ImageButton
  12. android:id="@+id/dialog_dashboard_date_cancel"
  13. android:layout_width="wrap_content"
  14. android:layout_height="match_parent"
  15. android:layout_gravity="start"
  16. android:contentDescription="@string/app_name"
  17. android:src="@android:drawable/ic_menu_close_clear_cancel"
  18. android:background="@android:color/transparent" />
  19. <ImageButton
  20. android:id="@+id/dialog_dashboard_date_accept"
  21. android:layout_width="wrap_content"
  22. android:layout_height="match_parent"
  23. android:layout_gravity="end"
  24. android:contentDescription="@string/app_name"
  25. android:src="@android:drawable/ic_menu_save"
  26. android:background="@android:color/transparent" />
  27. </FrameLayout>
  28. <View
  29. android:layout_width="match_parent"
  30. android:layout_height="1dp"
  31. android:background="@android:color/background_light" />
  32. <LinearLayout android:id="@+id/ll_dialog"
  33. android:layout_width="match_parent"
  34. android:layout_height="170dp"
  35. android:orientation="vertical">
  36. <ListView android:id="@+id/list_dialog"
  37. android:layout_width="match_parent"
  38. android:layout_height="wrap_content">
  39. </ListView>
  40. <TextView
  41. android:layout_width="match_parent"
  42. android:layout_height="wrap_content"
  43. android:gravity="center"
  44. android:text="已显示全部车位信息"/>
  45. </LinearLayout>
  46. </LinearLayout>

效果图
此处输入图片的描述

源码链接:http://pan.baidu.com/s/1pKssI8r

实现从任意位置弹出选择框,同时其他部分变暗的效果

效果如下所示
此处输入图片的描述

选择框布局

  1. <?xml version="1.0" encoding="utf-8"?>
  2. <RelativeLayout
  3. xmlns:android="http://schemas.android.com/apk/res/android"
  4. android:layout_width="match_parent"
  5. android:layout_height="match_parent"
  6. >
  7. <ScrollView
  8. android:background="@color/subjectBackGroundColoe"
  9. android:layout_alignParentTop="true"
  10. android:layout_width="match_parent"
  11. android:layout_height="200dp">
  12. <LinearLayout
  13. android:layout_width="match_parent"
  14. android:layout_height="match_parent"
  15. android:orientation="vertical">
  16. <TextView
  17. android:layout_width="match_parent"
  18. android:layout_height="50dp"
  19. android:text="第1个"
  20. android:gravity="center"/>
  21. <TextView
  22. android:layout_width="match_parent"
  23. android:layout_height="50dp"
  24. android:text="第2个"
  25. android:gravity="center"/>
  26. <TextView
  27. android:layout_width="match_parent"
  28. android:layout_height="50dp"
  29. android:text="第3个"
  30. android:gravity="center"/>
  31. <TextView
  32. android:layout_width="match_parent"
  33. android:layout_height="50dp"
  34. android:text="第4个"
  35. android:gravity="center"/>
  36. <TextView
  37. android:layout_width="match_parent"
  38. android:layout_height="50dp"
  39. android:text="第5个"
  40. android:gravity="center"/>
  41. <TextView
  42. android:layout_width="match_parent"
  43. android:layout_height="50dp"
  44. android:text="第6个"
  45. android:gravity="center"/>
  46. <TextView
  47. android:layout_width="match_parent"
  48. android:layout_height="50dp"
  49. android:text="第7个"
  50. android:gravity="center"/>
  51. <TextView
  52. android:layout_width="match_parent"
  53. android:layout_height="50dp"
  54. android:text="第8个"
  55. android:gravity="center"/>
  56. <TextView
  57. android:layout_width="match_parent"
  58. android:layout_height="50dp"
  59. android:text="第9个"
  60. android:gravity="center"/>
  61. </LinearLayout>
  62. </ScrollView>
  63. </RelativeLayout>

布局分析,将最外层的布局,设置为充满整个屏幕,即
android:layout_width="match_parent"
android:layout_height="match_parent"
在代码中给最外层的布局,设置一个半透明的背景,已达到弹出选择框的时候,整个屏幕变暗的效果。

将ScrollView设置为
android:background="@color/subjectBackGroundColoe"
android:layout_alignParentTop="true"
android:layout_width="match_parent"
android:layout_height="200dp"

设置ScrollView位于弹出框的最顶部,高度为200dp,宽度为填满整个窗体,同时给其设置非透明的背景,这样就可以达到整个屏幕只有顶部部分,而底部半透明的效果,即底部变暗的效果。

代码

  1. private void showSubjectPopupWindow(View view) {//想要选择框在哪个view下面,就把那个view作为参数传递进来
  2. // 一个自定义的布局,作为显示的内容
  3. View contentView = LayoutInflater.from(ctx).inflate(R.layout.subject_select_layout,null);
  4. final PopupWindow popupWindow = new PopupWindow(contentView,
  5. ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT, true);
  6. //实例化一个ColorDrawable颜色为半透明,已达到变暗的效果
  7. ColorDrawable dw = new ColorDrawable(0xb0000000);
  8. // 如果不设置PopupWindow的背景,无论是点击外部区域还是Back键都无法dismiss弹框
  9. // 我觉得这里是API的一个bug
  10. popupWindow.setBackgroundDrawable(dw);
  11. // 设置好参数之后再show
  12. popupWindow.showAsDropDown(view, 0, 0);
  13. }

在想要显示选择框的地方,调用showSubjectPopupWindow方法即可。

基本用法参考链接 http://www.cnblogs.com/mengdd/p/3569127.html
基本用法参考链接 http://note.youdao.com/share/?id=c658bf947612cedb1039650fb26f528d&type=note

背景变暗的参考链接 http://note.youdao.com/share/?id=e2b11e8a9741bc796b04c82b5267d3f3&type=note

添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注