一、开发背景
大家好,在 Android 开发中,我们经常遇到项目有弹窗的需求,而且往往弹窗呈现的布局内容相当复杂。针对这种情况,如果我们用 对话框 AlertDialog 来完成需求,那么就有点不合适了。不过,针对这种情况,我们可以使用高级 UI 组件 PopupWindow 来完成复杂的弹窗功能需求。
二、PopupWindow的构造函数
在正式介绍弹窗 PopupWindow 之前,首先来让我们领略 PopupWindow 的风采。以下就是使用 PopupWindow 来完成的显示效果,如图所示:
点击 Button 之后,在屏幕上弹出 PopupWindow。这里可以看到,PopupWindow 内部填充的布局可以由开发人员高度定制。好了,接下来向大家介绍 PopupWindow 的构造函数。PopupWindow 有四个常用的构造函数,它们的使用场景各有不同。
方法一:
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 常用构造函数有四个,但是必须设置其 contentView、width 、height 参数。所以如果要用方法一的构造函数,那么构造的代码应该如下所示:
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);
必须为 PopupWindow 设置 contentView 的原因是 PopupWindow 没有默认布局,不能像对话框 AlertDialog 那样,只用简单地设置一个 Title 就可以弹出一个方框。因此 PopupWindow 的布局必须由开发者亲自设置。
方法四中的 focusable 虽然不是必须设置的,但是它可以控制点击 PopupWindow 以外区域是否让 PopupWindow 在屏幕上消失。
三、PopupWindow 的显示函数
如同 Toast 一样,我们创建好 PopupWindow 的实例之后,还需要调用其专门的显示函数,方可在屏幕上弹出 PopupWindow。我接下来向大家介绍 PopupWindow 常用的三个显示函数。
方法一:
showAsDropDown(View anchor)
在锚点控件 anchor 的正下方,无偏移地展示。
方法二:
showAsDropDown(View anchor, int xoff, int yoff)
相对于锚点控件 anchor 有偏移地显示。xoff 表示在 X 轴上的偏移量,正值代表向左,负值代表向右; yoff 表示在 Y 轴上的偏移量,正值代表向上,负值代表向下。
方法三:
showAtLocation(View parent, int gravity, int xoff, int yoff)
指定控件所在的父布局,在父布局内的相对位置进行展示,可以设置偏移或无偏移。例如:例如正中央Gravity.CENTER、下方Gravity.BOTTOM等。
可以将上述常用的显示函数分成两类:
一类是指定锚点控件的函数:
showAsDropDown(View anchor)
showAsDropDown(View anchor, int xoff, int yoff)
二类是指定 PopupWindow 所在父布局的显示函数:
showAtLocation(View parent, int gravity, int xoff, int yoff)
四、PopupWindow 常用的其它函数
弹窗 PopupWindow 还有许多常用的其它函数,在这里因为篇幅的限制,所以向大家介绍几个常用的函数。如果要实现更加精细的功能需求,那么请查阅 Android 的官方 API。在那上面,对其常用函数会有更为细致的描述。
方法一:
public void dismiss()
顾名思义,有隐藏弹窗的作用。
方法二:
public void setFocusable(boolean focusable)设置弹窗是否获取焦点,false代表弹窗内部得控件(eg:EditText)无法获取焦点,那么弹窗内部的 EditText 就不能显示光标支持用户输入。
方法三:
public void setTouchable(boolean touchable)方法四:
public void setOutsideTouchable(boolean touchable)方法五:
public void setBackgroundDrawable(Drawable background)
五、PopupWindow 的样例 Demo
1、功能需求
点击 Button ,在屏幕上弹出 PopupWindow 的弹窗。
2、MainActivity 的 XML 布局文件
MainActivity 的布局代码如下所示:
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#66CCCCCC"
tools:context=".MainActivity3">
<Button
android:id="@+id/btn"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="100dp"
android:background="@android:color/transparent"
android:text="show PopupWindow"
android:textAllCaps="false"
android:textColor="#E5D400"
android:textSize="20sp"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
可以看到,MainActivity.xml 只有一个按钮 Button,待会儿用来唤醒弹窗 PopupWindow。
3、弹窗 PopupWindow 的布局
在使用 PopupWindow 的时候,我们需要定制其布局,并且将布局与 PopupWindow 进行绑定。本案例中 PopupWindow 的布局 popup_layout 的代码如下所示:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".MainActivity3">
<TextView
android:layout_width="match_parent"
android:layout_height="30dp"
android:gravity="center"
android:text="Match" />
<TextView
android:layout_width="match_parent"
android:layout_height="30dp"
android:gravity="center"
android:text="Chinese" />
<TextView
android:layout_width="match_parent"
android:layout_height="30dp"
android:gravity="center"
android:text="English" />
<TextView
android:layout_width="match_parent"
android:layout_height="30dp"
android:gravity="center"
android:text="Computer" />
</LinearLayout>
上述代码在Android Studio中的预览图,如下图所示:
4、MainActivity 的逻辑代码
MainActivity 的逻辑代码如下所示:
package cn.com.helloworld;
import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import android.text.Layout;
import android.util.Log;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.PopupWindow;
import java.util.ArrayList;
import java.util.List;
public class MainActivity3 extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main3);
Button btn = findViewById(R.id.btn);
btn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
showPopupWindow();
}
});
}
public void showPopupWindow() {
View contentView = LayoutInflater.from(MainActivity3.this)
.inflate(R.layout.pop_layout, null);
PopupWindow popupWindow = new PopupWindow(this);
popupWindow.setWidth(ViewGroup.LayoutParams.MATCH_PARENT);
popupWindow.setHeight(ViewGroup.LayoutParams.WRAP_CONTENT);
popupWindow.setFocusable(true);
popupWindow.setContentView(contentView);
View rootView = LayoutInflater.from(MainActivity3.this).inflate(R.layout.activity_main3, null);
popupWindow.showAtLocation(rootView, Gravity.BOTTOM, 0, 0);
}
}
代码分析:
在 onCreate 函数中,绑定了 Button 控件,并且为其设置点击事件监听。当用户点击 Button 的时候,就会执行 onClick 里面的代码。即执行 showPopupWindow 弹出 PopupWindow。
在函数 showPopupWindow 的内部,contentView 是弹窗的布局。rootView 是弹窗所属的父布局。显示函数 showAtLocation 将弹窗展示在屏幕上。