下面是一个弹出带ListView和TextView的PopupWindow实例:
import android.app.Activity;
import android.content.Context;
import android.util.DisplayMetrics;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.widget.AdapterView;
import android.widget.ListView;
import android.widget.PopupWindow;
import android.widget.TextView;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
/**
* Created by SRain on 2015/10/15.
* <p/>
* 弹出框
*/
public class PopupWindowUtils implements View.OnClickListener {
private PopupWindow popupWindow;
private Context context;
private View view;
private TextView tvClose;
private JSONArray jsonArray;
private BasicPopuListAdapter adapter; //ListView的适配器
public PopuWindowUtils(Context context, View view, JSONArray jsonArray, int index) {
this.context = context;
this.view = view;
this.jsonArray = jsonArray;
initPopuWindow(index);
}
/**
* 弹出popupwindow
*
* @param index 弹出框样式标识
*/
private void initPopuWindow(final int index) {
View contentView = LayoutInflater.from(context).inflate(R.layout.layout_popupwindow, null);
ListView listView = (ListView) contentView.findViewById(R.id.lv_files);
tvClose = (TextView) contentView.findViewById(R.id.tvClose);
tvClose.setVisibility(View.VISIBLE);
tvClose.setOnClickListener(this);
/*
* 创建PopupWindow实例
* getScreenWidth(), (int) (getScreenWidth() * 0.6))分别是宽度和高度
*/
if (popupWindow == null) {
popupWindow = new PopupWindow(contentView, getScreenWidth(), (int) (getScreenWidth() * 0.6));
}
// 自定义view添加触摸事件
contentView.setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
if (popupWindow != null && popupWindow.isShowing()) {
popupWindow.dismiss();
return true;
}
// 这里如果返回true的话,touch事件将被拦截
// 拦截后 PopupWindow的onTouchEvent不被调用,这样点击外部区域无法dismiss
return false;
}
});
switch (index) {
case 1:
adapter = new RoutePupuAdapter(context, jsonArray);
break;
}
listView.setAdapter(adapter);
listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
try {
adapter.setSelectedPosition(position);
// 更新列表框
adapter.notifyDataSetInvalidated();
JSONObject object = jsonArray.getJSONObject(position);
clickFunction(index, object);
} catch (JSONException e) {
Log.e("onItemClick", e.toString());
}
}
});
popupWindow.setAnimationStyle(R.style.PopupAnimation);
popupWindow.setFocusable(true);
/*
* 弹出框位置
*/
popupWindow.showAsDropDown(view, 0, -110);
//popupWindow.showAtLocation(MainActivity.this.findViewById(R.id.main), Gravity.BOTTOM|Gravi //ty.CENTER_HORIZONTAL, 0, 0); //设置layout在PopupWindow中显示的位置,从底部弹出
}
/**
* 根据屏幕宽度设置高度值
*/
private int getScreenWidth() {
//获取屏幕宽度
DisplayMetrics dm = new DisplayMetrics();
double densityDpi = dm.density;
//获取屏幕信息
((Activity) context).getWindowManager().getDefaultDisplay().getMetrics(dm);
int screenWidth = dm.widthPixels;
return screenWidth;
}
public void clickFunction(int index, JSONObject json) {
switch (index) {
case 1:
((LocationMapActivity) context).initRoadLines(json);
break;
}
}
/**
* 销毁PopupWindow
*/
public boolean dismissPopu() {
if (popupWindow != null && popupWindow.isShowing()) {
popupWindow.dismiss();
return true;
}
return false;
}
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.tvClose:
dismissPopu();
break;
}
}
}
//这是BasicPopuListAdapter代码,自己的listview中adapter继承它就可以了:
import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import org.json.JSONArray;
import org.json.JSONObject;
/**
* Created by SRain on 2015/10/20.
* <p/>
* 普通listview适配器
*/
public abstract class BasicPopuListAdapter extends BaseAdapter {
protected Context context;
protected JSONArray array;
protected LayoutInflater mInflater;
private int selectedPosition = -1;
public BasicPopuListAdapter(Context context, JSONArray array) {
this.context = context;
this.array = array;
mInflater = LayoutInflater.from(context);
}
@Override
public int getCount() {
return array.length();
}
@Override
public Object getItem(int position) {
JSONObject object = null;
try {
object = array.getJSONObject(position);
} catch (Exception e) {
}
return object;
}
@Override
public long getItemId(int position) {
return position;
}
@Override
public abstract View getView(int position, View convertView, ViewGroup parent);
public abstract void setSelectedPosition(int position);
public JSONArray getData(){
return this.array;
}
}
这是弹出框的布局文件:
<?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:background="@color/result_view"
android:orientation="vertical">
<ListView
android:id="@+id/lv_files"
android:layout_weight="1"
android:layout_width="match_parent"
android:layout_height="match_parent" />
<TextView
android:id="@+id/tvClose"
android:layout_marginTop="2dp"
android:visibility="gone"
android:layout_width="match_parent"
android:layout_height="45dp"
android:background="@color/green"
android:gravity="center"
android:text="关闭"
android:textColor="@color/white" />
</LinearLayout>
style中添加:
<style name="PopupAnimation" mce_bogus="1" parent="android:Animation">
<item name="android:windowEnterAnimation">@anim/dialog_enter</item>
<item name="android:windowExitAnimation">@anim/dialog_exit</item>
</style>
drawable中添加dialog_enter.xml和dialog_exit.xml:
<!--dialog_enter-->
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
<translate
android:duration="500"
android:fromYDelta="500"
android:toYDelta="0" />
</set>
<!--dialog_exit-->
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
<translate
android:duration="500"
android:fromYDelta="0"
android:toYDelta="500" />
</set>
补充:
在用PopupWindow实现底部弹出菜单的时候要注意几个问题:
1)如果弹出菜单中有EditText这种输入控件,如果不给PopupWindow设置可获取焦点的话,EditText是无法获取输入的。
2)一定要设置 虚拟软键盘随需要更改屏幕显示内容的大小,否则虚拟软键盘会被底部弹出菜单遮挡。
3)如果设置了PopupWindow可获取焦点的话,此时会遇到一个问题就是当PopupWindow中的控件比如EditText获取焦点之后,点击PopupWindow之外的控件是不会有响应的,如果用setBackgroundDrawable(new BitmapDrawable())进行设置的话,则不会出现这种情况。
//设置可以获取焦点,否则弹出菜单中的EditText是无法获取输入的
popWindow.setFocusable(true);
//这句是为了防止弹出菜单获取焦点之后,点击activity的其他组件没有响应
popWindow.setBackgroundDrawable(new BitmapDrawable());
//防止虚拟软键盘被弹出菜单遮住
popWindow.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE);
//在底部显示
popWindow.showAtLocation(this,Gravity.BOTTOM, 0, 0);
4)popupWindow.setBackgroundDrawable(new BitmapDrawable()); // 需要设置一下此参数,点击外边可消失, 如果不设置PopupWindow的背景,无论是点击外部区域还是Back键都无法dismiss弹框
popupWindow.setOutsideTouchable(true); // 设置popwindow如果点击外面区域,便关闭。
popupWindow.setFocusable(true); // 设置此参数获得焦点,否则无法点击
popupWindow.update();
5)PopupWindow和AlertDialog的区别
Android的对话框有两种:PopupWindow和AlertDialog。它们的不同点在于:
- AlertDialog的位置固定,而PopupWindow的位置可以随意
- AlertDialog是非阻塞线程的,而PopupWindow是阻塞线程的
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等),可以设置偏移或无偏移
来源:oschina
链接:https://my.oschina.net/u/2320057/blog/534867